Minor fixes

Cleaned up TODOs
Cleaned up code
This commit is contained in:
Nite 2021-02-14 15:55:16 +01:00
parent d70d2cc2fb
commit 86bfcefe93
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
50 changed files with 1226 additions and 1304 deletions

View File

@ -19,8 +19,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import timber.log.Timber;
public class AboutFragment extends Fragment { public class AboutFragment extends Fragment {
private WebView webView; private WebView webView;

View File

@ -27,7 +27,7 @@ import org.moire.ultrasonic.subsonic.VideoPlayer;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.Pair; import org.moire.ultrasonic.util.Pair;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.EntryAdapter; import org.moire.ultrasonic.view.EntryAdapter;
@ -35,7 +35,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import kotlin.Lazy; import kotlin.Lazy;
import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
@ -73,6 +72,7 @@ public class BookmarksFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
cancellationToken = new CancellationToken(); cancellationToken = new CancellationToken();
albumButtons = view.findViewById(R.id.menu_album); albumButtons = view.findViewById(R.id.menu_album);
super.onViewCreated(view, savedInstanceState);
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh); refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
albumListView = view.findViewById(R.id.select_album_entries_list); albumListView = view.findViewById(R.id.select_album_entries_list);
@ -186,7 +186,6 @@ public class BookmarksFragment extends Fragment {
FragmentTitle.Companion.setTitle(this, R.string.button_bar_bookmarks); FragmentTitle.Companion.setTitle(this, R.string.button_bar_bookmarks);
enableButtons(); enableButtons();
getBookmarks(); getBookmarks();
} }
@ -203,7 +202,7 @@ public class BookmarksFragment extends Fragment {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return Util.getSongsFromBookmarks(service.getBookmarks(getContext(), this)); return Util.getSongsFromBookmarks(service.getBookmarks(getContext()));
} }
}.execute(); }.execute();
} }
@ -239,27 +238,8 @@ public class BookmarksFragment extends Fragment {
private void refresh() private void refresh()
{ {
// TODO: create better restart enableButtons();
getView().post(new Runnable() { getBookmarks();
public void run() {
Timber.d("Refresh called...");
if (getArguments() == null) {
Bundle bundle = new Bundle();
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
setArguments(bundle);
} else {
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
}
onViewCreated(getView(), null);
}
});
/* finish();
Intent intent = getIntent();
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
startActivityForResultWithoutTransition(this, intent);
*/
} }
private void selectAllOrNone() private void selectAllOrNone()
@ -392,7 +372,7 @@ public class BookmarksFragment extends Fragment {
mediaPlayerController.getValue().unpin(songs); mediaPlayerController.getValue().unpin(songs);
} }
private abstract class LoadTask extends TabActivityBackgroundTask<Pair<MusicDirectory, Boolean>> private abstract class LoadTask extends FragmentBackgroundTask<Pair<MusicDirectory, Boolean>>
{ {
public LoadTask() public LoadTask()
{ {
@ -406,7 +386,7 @@ public class BookmarksFragment extends Fragment {
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
MusicDirectory dir = load(musicService); MusicDirectory dir = load(musicService);
boolean valid = musicService.isLicenseValid(getContext(), this); boolean valid = musicService.isLicenseValid(getContext());
return new Pair<>(dir, valid); return new Pair<>(dir, valid);
} }

View File

@ -21,6 +21,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
@ -29,7 +30,7 @@ import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.BackgroundTask; import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.ChatAdapter; import org.moire.ultrasonic.view.ChatAdapter;
@ -52,6 +53,7 @@ public class ChatFragment extends Fragment {
private volatile static Long lastChatMessageTime = (long) 0; private volatile static Long lastChatMessageTime = (long) 0;
private static final ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>(); private static final ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
private CancellationToken cancellationToken; private CancellationToken cancellationToken;
private SwipeRefreshLayout swipeRefresh;
private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class); private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
@ -70,6 +72,9 @@ public class ChatFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
swipeRefresh = view.findViewById(R.id.chat_refresh);
swipeRefresh.setEnabled(false);
cancellationToken = new CancellationToken(); cancellationToken = new CancellationToken();
messageEditText = view.findViewById(R.id.chat_edittext); messageEditText = view.findViewById(R.id.chat_edittext);
sendButton = view.findViewById(R.id.chat_send); sendButton = view.findViewById(R.id.chat_send);
@ -238,13 +243,13 @@ public class ChatFragment extends Fragment {
{ {
messageEditText.setText(""); messageEditText.setText("");
BackgroundTask<Void> task = new TabActivityBackgroundTask<Void>(getActivity(), false, null, cancellationToken) BackgroundTask<Void> task = new FragmentBackgroundTask<Void>(getActivity(), false, swipeRefresh, cancellationToken)
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.addChatMessage(message, getContext(), this); musicService.addChatMessage(message, getContext());
return null; return null;
} }
@ -262,14 +267,13 @@ public class ChatFragment extends Fragment {
private synchronized void load() private synchronized void load()
{ {
// TODO: Do we need a SwipeToRefresh progress indicator? BackgroundTask<List<ChatMessage>> task = new FragmentBackgroundTask<List<ChatMessage>>(getActivity(), false, swipeRefresh, cancellationToken)
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(getActivity(), false, null, cancellationToken)
{ {
@Override @Override
protected List<ChatMessage> doInBackground() throws Throwable protected List<ChatMessage> doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
return musicService.getChatMessages(lastChatMessageTime, getContext(), this); return musicService.getChatMessages(lastChatMessageTime, getContext());
} }
@Override @Override

View File

@ -18,7 +18,7 @@ import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.BackgroundTask; import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import timber.log.Timber; import timber.log.Timber;
@ -66,15 +66,17 @@ public class LyricsFragment extends Fragment {
private void load() private void load()
{ {
BackgroundTask<Lyrics> task = new TabActivityBackgroundTask<Lyrics>(getActivity(), true, swipe, cancellationToken) BackgroundTask<Lyrics> task = new FragmentBackgroundTask<Lyrics>(getActivity(), true, swipe, cancellationToken)
{ {
@Override @Override
protected Lyrics doInBackground() throws Throwable protected Lyrics doInBackground() throws Throwable
{ {
String artist = getArguments().getString(Constants.INTENT_EXTRA_NAME_ARTIST); Bundle arguments = getArguments();
String title = getArguments().getString(Constants.INTENT_EXTRA_NAME_TITLE); if (arguments == null) return null;
String artist = arguments.getString(Constants.INTENT_EXTRA_NAME_ARTIST);
String title = arguments.getString(Constants.INTENT_EXTRA_NAME_TITLE);
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
return musicService.getLyrics(artist, title, getContext(), this); return musicService.getLyrics(artist, title, getContext());
} }
@Override @Override

View File

@ -1,7 +1,5 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;

View File

@ -134,7 +134,7 @@ public class NowPlayingFragment extends Fragment {
} }
}); });
// TODO: Check if this empty onClickListener is necessary // This empty onClickListener is necessary for the onTouchListener to work
getView().setOnClickListener(new View.OnClickListener() { getView().setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {

View File

@ -115,7 +115,6 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
private SilentBackgroundTask<Void> onProgressChangedTask; private SilentBackgroundTask<Void> onProgressChangedTask;
LinearLayout visualizerViewLayout; LinearLayout visualizerViewLayout;
private MenuItem starMenuItem; private MenuItem starMenuItem;
private LinearLayout ratingLinearLayout;
private ImageView fiveStar1ImageView; private ImageView fiveStar1ImageView;
private ImageView fiveStar2ImageView; private ImageView fiveStar2ImageView;
private ImageView fiveStar3ImageView; private ImageView fiveStar3ImageView;
@ -188,7 +187,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
visualizerViewLayout = view.findViewById(R.id.download_visualizer_view_layout); visualizerViewLayout = view.findViewById(R.id.download_visualizer_view_layout);
ratingLinearLayout = view.findViewById(R.id.song_rating); LinearLayout ratingLinearLayout = view.findViewById(R.id.song_rating);
fiveStar1ImageView = view.findViewById(R.id.song_five_star_1); fiveStar1ImageView = view.findViewById(R.id.song_five_star_1);
fiveStar2ImageView = view.findViewById(R.id.song_five_star_2); fiveStar2ImageView = view.findViewById(R.id.song_five_star_2);
fiveStar3ImageView = view.findViewById(R.id.song_five_star_3); fiveStar3ImageView = view.findViewById(R.id.song_five_star_3);
@ -197,7 +196,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
if (!useFiveStarRating) ratingLinearLayout.setVisibility(View.GONE); if (!useFiveStarRating) ratingLinearLayout.setVisibility(View.GONE);
hollowStar = Util.getDrawableFromAttribute(getContext(), R.attr.star_hollow); hollowStar = Util.getDrawableFromAttribute(view.getContext(), R.attr.star_hollow);
fullStar = Util.getDrawableFromAttribute(getContext(), R.attr.star_full); fullStar = Util.getDrawableFromAttribute(getContext(), R.attr.star_full);
fiveStar1ImageView.setOnClickListener(new View.OnClickListener() fiveStar1ImageView.setOnClickListener(new View.OnClickListener()
@ -893,250 +892,207 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
entry = song.getSong(); entry = song.getSong();
} }
switch (menuItemId) if (menuItemId == R.id.menu_show_artist) {
{ if (entry == null) {
case R.id.menu_show_artist:
if (entry == null)
{
return false;
}
if (Util.getShouldUseId3Tags(getContext()))
{
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getArtistId());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle);
}
return true;
case R.id.menu_show_album:
if (entry == null)
{
return false;
}
String albumId = Util.getShouldUseId3Tags(getContext()) ? entry.getAlbumId() : entry.getParent();
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId);
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true);
Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle);
return true;
case R.id.menu_lyrics:
if (entry == null)
{
return false;
}
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.getArtist());
bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.getTitle());
Navigation.findNavController(getView()).navigate(R.id.playerToLyrics, bundle);
return true;
case R.id.menu_remove:
mediaPlayerControllerLazy.getValue().remove(song);
onDownloadListChanged();
return true;
case R.id.menu_item_screen_on_off:
if (mediaPlayerControllerLazy.getValue().getKeepScreenOn())
{
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mediaPlayerControllerLazy.getValue().setKeepScreenOn(false);
}
else
{
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mediaPlayerControllerLazy.getValue().setKeepScreenOn(true);
}
return true;
case R.id.menu_shuffle:
mediaPlayerControllerLazy.getValue().shuffle();
Util.toast(getContext(), R.string.download_menu_shuffle_notification);
return true;
case R.id.menu_item_equalizer:
Navigation.findNavController(getView()).navigate(R.id.playerToEqualizer);
return true;
case R.id.menu_item_visualizer:
final boolean active = !visualizerView.isActive();
visualizerView.setActive(active);
if (!visualizerView.isActive())
{
visualizerViewLayout.setVisibility(View.GONE);
}
else
{
visualizerViewLayout.setVisibility(View.VISIBLE);
}
mediaPlayerControllerLazy.getValue().setShowVisualization(visualizerView.isActive());
Util.toast(getContext(), active ? R.string.download_visualizer_on : R.string.download_visualizer_off);
return true;
case R.id.menu_item_jukebox:
final boolean jukeboxEnabled = !mediaPlayerControllerLazy.getValue().isJukeboxEnabled();
mediaPlayerControllerLazy.getValue().setJukeboxEnabled(jukeboxEnabled);
Util.toast(getContext(), jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false);
return true;
case R.id.menu_item_toggle_list:
toggleFullScreenAlbumArt();
return true;
case R.id.menu_item_clear_playlist:
mediaPlayerControllerLazy.getValue().setShufflePlayEnabled(false);
mediaPlayerControllerLazy.getValue().clear();
onDownloadListChanged();
return true;
case R.id.menu_item_save_playlist:
if (mediaPlayerControllerLazy.getValue().getPlaylistSize() > 0)
{
showSavePlaylistDialog();
}
return true;
case R.id.menu_item_star:
if (currentSong == null)
{
return true;
}
final boolean isStarred = currentSong.getStarred();
final String id = currentSong.getId();
if (isStarred)
{
starMenuItem.setIcon(hollowStar);
currentSong.setStarred(false);
}
else
{
starMenuItem.setIcon(fullStar);
currentSong.setStarred(true);
}
new Thread(new Runnable()
{
@Override
public void run()
{
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try
{
if (isStarred)
{
musicService.unstar(id, null, null, getContext(), null);
}
else
{
musicService.star(id, null, null, getContext(), null);
}
}
catch (Exception e)
{
Timber.e(e);
}
}
}).start();
return true;
case R.id.menu_item_bookmark_set:
if (currentSong == null)
{
return true;
}
final String songId = currentSong.getId();
final int playerPosition = mediaPlayerControllerLazy.getValue().getPlayerPosition();
currentSong.setBookmarkPosition(playerPosition);
String bookmarkTime = Util.formatTotalDuration(playerPosition, true);
new Thread(new Runnable()
{
@Override
public void run()
{
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try
{
musicService.createBookmark(songId, playerPosition, getContext(), null);
}
catch (Exception e)
{
Timber.e(e);
}
}
}).start();
String msg = getResources().getString(R.string.download_bookmark_set_at_position, bookmarkTime);
Util.toast(getContext(), msg);
return true;
case R.id.menu_item_bookmark_delete:
if (currentSong == null)
{
return true;
}
final String bookmarkSongId = currentSong.getId();
currentSong.setBookmarkPosition(0);
new Thread(new Runnable()
{
@Override
public void run()
{
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try
{
musicService.deleteBookmark(bookmarkSongId, getContext(), null);
}
catch (Exception e)
{
Timber.e(e);
}
}
}).start();
Util.toast(getContext(), R.string.download_bookmark_removed);
return true;
case R.id.menu_item_share:
MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue();
List<MusicDirectory.Entry> entries = new ArrayList<>();
if (mediaPlayerController != null)
{
List<DownloadFile> downloadServiceSongs = mediaPlayerController.getPlayList();
if (downloadServiceSongs != null)
{
for (DownloadFile downloadFile : downloadServiceSongs)
{
if (downloadFile != null)
{
MusicDirectory.Entry playlistEntry = downloadFile.getSong();
if (playlistEntry != null)
{
entries.add(playlistEntry);
}
}
}
}
}
shareHandler.getValue().createShare(this, entries, null, cancellationToken);
return true;
default:
return false; return false;
}
if (Util.getShouldUseId3Tags(getContext())) {
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getArtistId());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle);
}
return true;
} else if (menuItemId == R.id.menu_show_album) {
if (entry == null) {
return false;
}
String albumId = Util.getShouldUseId3Tags(getContext()) ? entry.getAlbumId() : entry.getParent();
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId);
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true);
Navigation.findNavController(getView()).navigate(R.id.playerToSelectAlbum, bundle);
return true;
} else if (menuItemId == R.id.menu_lyrics) {
if (entry == null) {
return false;
}
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.getArtist());
bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.getTitle());
Navigation.findNavController(getView()).navigate(R.id.playerToLyrics, bundle);
return true;
} else if (menuItemId == R.id.menu_remove) {
mediaPlayerControllerLazy.getValue().remove(song);
onDownloadListChanged();
return true;
} else if (menuItemId == R.id.menu_item_screen_on_off) {
if (mediaPlayerControllerLazy.getValue().getKeepScreenOn()) {
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mediaPlayerControllerLazy.getValue().setKeepScreenOn(false);
} else {
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mediaPlayerControllerLazy.getValue().setKeepScreenOn(true);
}
return true;
} else if (menuItemId == R.id.menu_shuffle) {
mediaPlayerControllerLazy.getValue().shuffle();
Util.toast(getContext(), R.string.download_menu_shuffle_notification);
return true;
} else if (menuItemId == R.id.menu_item_equalizer) {
Navigation.findNavController(getView()).navigate(R.id.playerToEqualizer);
return true;
} else if (menuItemId == R.id.menu_item_visualizer) {
final boolean active = !visualizerView.isActive();
visualizerView.setActive(active);
if (!visualizerView.isActive()) {
visualizerViewLayout.setVisibility(View.GONE);
} else {
visualizerViewLayout.setVisibility(View.VISIBLE);
}
mediaPlayerControllerLazy.getValue().setShowVisualization(visualizerView.isActive());
Util.toast(getContext(), active ? R.string.download_visualizer_on : R.string.download_visualizer_off);
return true;
} else if (menuItemId == R.id.menu_item_jukebox) {
final boolean jukeboxEnabled = !mediaPlayerControllerLazy.getValue().isJukeboxEnabled();
mediaPlayerControllerLazy.getValue().setJukeboxEnabled(jukeboxEnabled);
Util.toast(getContext(), jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false);
return true;
} else if (menuItemId == R.id.menu_item_toggle_list) {
toggleFullScreenAlbumArt();
return true;
} else if (menuItemId == R.id.menu_item_clear_playlist) {
mediaPlayerControllerLazy.getValue().setShufflePlayEnabled(false);
mediaPlayerControllerLazy.getValue().clear();
onDownloadListChanged();
return true;
} else if (menuItemId == R.id.menu_item_save_playlist) {
if (mediaPlayerControllerLazy.getValue().getPlaylistSize() > 0) {
showSavePlaylistDialog();
}
return true;
} else if (menuItemId == R.id.menu_item_star) {
if (currentSong == null) {
return true;
}
final boolean isStarred = currentSong.getStarred();
final String id = currentSong.getId();
if (isStarred) {
starMenuItem.setIcon(hollowStar);
currentSong.setStarred(false);
} else {
starMenuItem.setIcon(fullStar);
currentSong.setStarred(true);
}
new Thread(new Runnable() {
@Override
public void run() {
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try {
if (isStarred) {
musicService.unstar(id, null, null, getContext());
} else {
musicService.star(id, null, null, getContext());
}
} catch (Exception e) {
Timber.e(e);
}
}
}).start();
return true;
} else if (menuItemId == R.id.menu_item_bookmark_set) {
if (currentSong == null) {
return true;
}
final String songId = currentSong.getId();
final int playerPosition = mediaPlayerControllerLazy.getValue().getPlayerPosition();
currentSong.setBookmarkPosition(playerPosition);
String bookmarkTime = Util.formatTotalDuration(playerPosition, true);
new Thread(new Runnable() {
@Override
public void run() {
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try {
musicService.createBookmark(songId, playerPosition, getContext());
} catch (Exception e) {
Timber.e(e);
}
}
}).start();
String msg = getResources().getString(R.string.download_bookmark_set_at_position, bookmarkTime);
Util.toast(getContext(), msg);
return true;
} else if (menuItemId == R.id.menu_item_bookmark_delete) {
if (currentSong == null) {
return true;
}
final String bookmarkSongId = currentSong.getId();
currentSong.setBookmarkPosition(0);
new Thread(new Runnable() {
@Override
public void run() {
final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
try {
musicService.deleteBookmark(bookmarkSongId, getContext());
} catch (Exception e) {
Timber.e(e);
}
}
}).start();
Util.toast(getContext(), R.string.download_bookmark_removed);
return true;
} else if (menuItemId == R.id.menu_item_share) {
MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue();
List<MusicDirectory.Entry> entries = new ArrayList<>();
if (mediaPlayerController != null) {
List<DownloadFile> downloadServiceSongs = mediaPlayerController.getPlayList();
if (downloadServiceSongs != null) {
for (DownloadFile downloadFile : downloadServiceSongs) {
if (downloadFile != null) {
MusicDirectory.Entry playlistEntry = downloadFile.getSong();
if (playlistEntry != null) {
entries.add(playlistEntry);
}
}
}
}
}
shareHandler.getValue().createShare(this, entries, null, cancellationToken);
return true;
} }
return false;
} }
private void update(CancellationToken cancel) private void update(CancellationToken cancel)
@ -1178,7 +1134,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
entries.add(downloadFile.getSong()); entries.add(downloadFile.getSong());
} }
final MusicService musicService = MusicServiceFactory.getMusicService(getContext()); final MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.createPlaylist(null, playlistName, entries, getContext(), null); musicService.createPlaylist(null, playlistName, entries, getContext());
return null; return null;
} }

View File

@ -41,14 +41,13 @@ import org.moire.ultrasonic.util.CacheCleaner;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.LoadingTask; import org.moire.ultrasonic.util.LoadingTask;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.PlaylistAdapter; import org.moire.ultrasonic.view.PlaylistAdapter;
import java.util.List; import java.util.List;
import kotlin.Lazy; import kotlin.Lazy;
import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
@ -109,10 +108,9 @@ public class PlaylistsFragment extends Fragment {
} }
}); });
registerForContextMenu(playlistsListView); registerForContextMenu(playlistsListView);
FragmentTitle.Companion.setTitle(this, R.string.playlist_label); FragmentTitle.Companion.setTitle(this, R.string.playlist_label);
load(); load(false);
} }
@Override @Override
@ -123,38 +121,18 @@ public class PlaylistsFragment extends Fragment {
private void refresh() private void refresh()
{ {
// TODO: create better restart load(true);
getView().post(new Runnable() {
public void run() {
Timber.d("Refresh called...");
if (getArguments() == null) {
Bundle bundle = new Bundle();
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
setArguments(bundle);
} else {
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
}
onViewCreated(getView(), null);
}
});
/* finish();
Intent intent = new Intent(this, SelectPlaylistActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
startActivityForResultWithoutTransition(this, intent);
*/
} }
private void load() private void load(final boolean refresh)
{ {
BackgroundTask<List<Playlist>> task = new TabActivityBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken) BackgroundTask<List<Playlist>> task = new FragmentBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken)
{ {
@Override @Override
protected List<Playlist> doInBackground() throws Throwable protected List<Playlist> doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); List<Playlist> playlists = musicService.getPlaylists(refresh, getContext());
List<Playlist> playlists = musicService.getPlaylists(refresh, getContext(), this);
if (!ActiveServerProvider.Companion.isOffline(getContext())) if (!ActiveServerProvider.Companion.isOffline(getContext()))
new CacheCleaner(getContext()).cleanPlaylists(playlists); new CacheCleaner(getContext()).cleanPlaylists(playlists);
@ -258,7 +236,7 @@ public class PlaylistsFragment extends Fragment {
protected Void doInBackground() throws Throwable protected Void doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.deletePlaylist(playlist.getId(), getContext(), null); musicService.deletePlaylist(playlist.getId(), getContext());
return null; return null;
} }
@ -348,7 +326,7 @@ public class PlaylistsFragment extends Fragment {
String comment = commentBoxText != null ? commentBoxText.toString() : null; String comment = commentBoxText != null ? commentBoxText.toString() : null;
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.updatePlaylist(playlist.getId(), name, comment, publicBox.isChecked(), getContext(), null); musicService.updatePlaylist(playlist.getId(), name, comment, publicBox.isChecked(), getContext());
return null; return null;
} }

View File

@ -1,5 +1,6 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -11,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.PodcastsChannel; import org.moire.ultrasonic.domain.PodcastsChannel;
@ -19,7 +21,7 @@ import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.BackgroundTask; import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.PodcastsChannelsAdapter; import org.moire.ultrasonic.view.PodcastsChannelsAdapter;
@ -30,6 +32,7 @@ public class PodcastFragment extends Fragment {
private View emptyTextView; private View emptyTextView;
ListView channelItemsListView = null; ListView channelItemsListView = null;
private CancellationToken cancellationToken; private CancellationToken cancellationToken;
private SwipeRefreshLayout swipeRefresh;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@ -47,6 +50,9 @@ public class PodcastFragment extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
cancellationToken = new CancellationToken(); cancellationToken = new CancellationToken();
swipeRefresh = view.findViewById(R.id.podcasts_refresh);
swipeRefresh.setEnabled(false);
FragmentTitle.Companion.setTitle(this, R.string.podcasts_label); FragmentTitle.Companion.setTitle(this, R.string.podcasts_label);
emptyTextView = view.findViewById(R.id.select_podcasts_empty); emptyTextView = view.findViewById(R.id.select_podcasts_empty);
@ -61,12 +67,11 @@ public class PodcastFragment extends Fragment {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId()); bundle.putString(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId());
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); Navigation.findNavController(view).navigate(R.id.selectAlbumFragment, bundle);
} }
}); });
// TODO: Probably a swipeRefresh should be added here in the long run load(view.getContext());
load();
} }
@Override @Override
@ -75,26 +80,21 @@ public class PodcastFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
} }
private void load() private void load(final Context context)
{ {
BackgroundTask<List<PodcastsChannel>> task = new TabActivityBackgroundTask<List<PodcastsChannel>>(getActivity(), true, null, cancellationToken) BackgroundTask<List<PodcastsChannel>> task = new FragmentBackgroundTask<List<PodcastsChannel>>(getActivity(), true, swipeRefresh, cancellationToken)
{ {
@Override @Override
protected List<PodcastsChannel> doInBackground() throws Throwable protected List<PodcastsChannel> doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(context);
return musicService.getPodcastsChannels(false, getContext(), this); return musicService.getPodcastsChannels(false, context);
/* TODO Why is here a cache cleaning? (original TODO text: c'est quoi ce nettoyage de cache ?)
if (!Util.isOffline(PodcastsActivity.this))
new CacheCleaner(PodcastsActivity.this, getDownloadService()).cleanPlaylists(playlists);
*/
} }
@Override @Override
protected void done(List<PodcastsChannel> result) protected void done(List<PodcastsChannel> result)
{ {
channelItemsListView.setAdapter(new PodcastsChannelsAdapter(getContext(), result)); channelItemsListView.setAdapter(new PodcastsChannelsAdapter(context, result));
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE); emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
} }
}; };

View File

@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jetbrains.annotations.NotNull;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.Artist; import org.moire.ultrasonic.domain.Artist;
@ -42,7 +43,7 @@ import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.MergeAdapter; import org.moire.ultrasonic.util.MergeAdapter;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.ArtistAdapter; import org.moire.ultrasonic.view.ArtistAdapter;
import org.moire.ultrasonic.view.EntryAdapter; import org.moire.ultrasonic.view.EntryAdapter;
@ -128,7 +129,7 @@ public class SearchFragment extends Fragment {
list = view.findViewById(R.id.search_list); list = view.findViewById(R.id.search_list);
searchRefresh = view.findViewById(R.id.search_entries_refresh); searchRefresh = view.findViewById(R.id.search_entries_refresh);
searchRefresh.setEnabled(false); // TODO: Should this be enabled? searchRefresh.setEnabled(false); // TODO: It should be enabled if it is a good feature to refresh search results
list.setOnItemClickListener(new AdapterView.OnItemClickListener() list.setOnItemClickListener(new AdapterView.OnItemClickListener()
{ {
@ -230,6 +231,7 @@ public class SearchFragment extends Fragment {
Timber.d("onQueryTextSubmit: %s", query); Timber.d("onQueryTextSubmit: %s", query);
mergeAdapter = new MergeAdapter(); mergeAdapter = new MergeAdapter();
list.setAdapter(mergeAdapter); list.setAdapter(mergeAdapter);
searchView.clearFocus();
search(query, autoPlay); search(query, autoPlay);
return true; return true;
} }
@ -243,9 +245,11 @@ public class SearchFragment extends Fragment {
} }
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
{ {
super.onCreateContextMenu(menu, view, menuInfo); super.onCreateContextMenu(menu, view, menuInfo);
if (getActivity() == null) return;
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
Object selectedItem = list.getItemAtPosition(info.position); Object selectedItem = list.getItemAtPosition(info.position);
@ -311,84 +315,65 @@ public class SearchFragment extends Fragment {
List<MusicDirectory.Entry> songs = new ArrayList<>(1); List<MusicDirectory.Entry> songs = new ArrayList<>(1);
switch (menuItem.getItemId()) int itemId = menuItem.getItemId();
{ if (itemId == R.id.album_menu_play_now) {
case R.id.album_menu_play_now: downloadHandler.getValue().downloadRecursively(this, id, false, false, true, false, false, false, false, false);
downloadHandler.getValue().downloadRecursively(this, id, false, false, true, false, false, false, false, false); } else if (itemId == R.id.album_menu_play_next) {
break; downloadHandler.getValue().downloadRecursively(this, id, false, true, false, true, false, true, false, false);
case R.id.album_menu_play_next: } else if (itemId == R.id.album_menu_play_last) {
downloadHandler.getValue().downloadRecursively(this, id, false, true, false, true, false, true, false, false); downloadHandler.getValue().downloadRecursively(this, id, false, true, false, false, false, false, false, false);
break; } else if (itemId == R.id.album_menu_pin) {
case R.id.album_menu_play_last: downloadHandler.getValue().downloadRecursively(this, id, true, true, false, false, false, false, false, false);
downloadHandler.getValue().downloadRecursively(this, id, false, true, false, false, false, false, false, false); } else if (itemId == R.id.album_menu_unpin) {
break; downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, false, false, true, false);
case R.id.album_menu_pin: } else if (itemId == R.id.album_menu_download) {
downloadHandler.getValue().downloadRecursively(this, id, true, true, false, false, false, false, false, false); downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, true, false, false, false);
break; } else if (itemId == R.id.song_menu_play_now) {
case R.id.album_menu_unpin: if (entry != null) {
downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, false, false, true, false); songs = new ArrayList<>(1);
break; songs.add(entry);
case R.id.album_menu_download: downloadHandler.getValue().download(this, false, false, true, false, false, songs);
downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, true, false, false, false); }
break; } else if (itemId == R.id.song_menu_play_next) {
case R.id.song_menu_play_now: if (entry != null) {
if (entry != null) songs = new ArrayList<>(1);
{ songs.add(entry);
songs = new ArrayList<>(1); downloadHandler.getValue().download(this, true, false, false, true, false, songs);
songs.add(entry); }
downloadHandler.getValue().download(this, false, false, true, false, false, songs); } else if (itemId == R.id.song_menu_play_last) {
} if (entry != null) {
break; songs = new ArrayList<>(1);
case R.id.song_menu_play_next: songs.add(entry);
if (entry != null) downloadHandler.getValue().download(this, true, false, false, false, false, songs);
{ }
songs = new ArrayList<>(1); } else if (itemId == R.id.song_menu_pin) {
songs.add(entry); if (entry != null) {
downloadHandler.getValue().download(this, true, false, false, true, false, songs); songs.add(entry);
} Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
break; downloadBackground(true, songs);
case R.id.song_menu_play_last: }
if (entry != null) } else if (itemId == R.id.song_menu_download) {
{ if (entry != null) {
songs = new ArrayList<>(1); songs.add(entry);
songs.add(entry); Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
downloadHandler.getValue().download(this, true, false, false, false, false, songs); downloadBackground(false, songs);
} }
break; } else if (itemId == R.id.song_menu_unpin) {
case R.id.song_menu_pin: if (entry != null) {
if (entry != null) songs.add(entry);
{ Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
songs.add(entry); mediaPlayerControllerLazy.getValue().unpin(songs);
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); }
downloadBackground(true, songs); } else if (itemId == R.id.menu_item_share) {
} if (entry != null) {
break; songs = new ArrayList<>(1);
case R.id.song_menu_download: songs.add(entry);
if (entry != null) shareHandler.getValue().createShare(this, songs, searchRefresh, cancellationToken);
{ }
songs.add(entry);
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size())); return super.onContextItemSelected(menuItem);
downloadBackground(false, songs); } else {
} return super.onContextItemSelected(menuItem);
break;
case R.id.song_menu_unpin:
if (entry != null)
{
songs.add(entry);
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
mediaPlayerControllerLazy.getValue().unpin(songs);
}
break;
case R.id.menu_item_share:
if (entry != null)
{
songs = new ArrayList<>(1);
songs.add(entry);
// TODO: Add SwipeRefresh spinner
shareHandler.getValue().createShare(this, songs, null, cancellationToken);
}
default:
return super.onContextItemSelected(menuItem);
} }
return true; return true;
@ -421,14 +406,14 @@ public class SearchFragment extends Fragment {
final int maxAlbums = Util.getMaxAlbums(getContext()); final int maxAlbums = Util.getMaxAlbums(getContext());
final int maxSongs = Util.getMaxSongs(getContext()); final int maxSongs = Util.getMaxSongs(getContext());
BackgroundTask<SearchResult> task = new TabActivityBackgroundTask<SearchResult>(getActivity(), true, searchRefresh, cancellationToken) BackgroundTask<SearchResult> task = new FragmentBackgroundTask<SearchResult>(getActivity(), true, searchRefresh, cancellationToken)
{ {
@Override @Override
protected SearchResult doInBackground() throws Throwable protected SearchResult doInBackground() throws Throwable
{ {
SearchCriteria criteria = new SearchCriteria(query, maxArtists, maxAlbums, maxSongs); SearchCriteria criteria = new SearchCriteria(query, maxArtists, maxAlbums, maxSongs);
MusicService service = MusicServiceFactory.getMusicService(getContext()); MusicService service = MusicServiceFactory.getMusicService(getContext());
return service.search(criteria, getContext(), this); return service.search(criteria, getContext());
} }
@Override @Override
@ -452,16 +437,13 @@ public class SearchFragment extends Fragment {
{ {
mergeAdapter = new MergeAdapter(); mergeAdapter = new MergeAdapter();
// TODO: Remove this if the search widget can do the same
//mergeAdapter.addView(searchButton, true);
if (searchResult != null) if (searchResult != null)
{ {
List<Artist> artists = searchResult.getArtists(); List<Artist> artists = searchResult.getArtists();
if (!artists.isEmpty()) if (!artists.isEmpty())
{ {
mergeAdapter.addView(artistsHeading); mergeAdapter.addView(artistsHeading);
List<Artist> displayedArtists = new ArrayList<Artist>(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size()))); List<Artist> displayedArtists = new ArrayList<>(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size())));
artistAdapter = new ArtistAdapter(getContext(), displayedArtists); artistAdapter = new ArtistAdapter(getContext(), displayedArtists);
mergeAdapter.addAdapter(artistAdapter); mergeAdapter.addAdapter(artistAdapter);
if (artists.size() > DEFAULT_ARTISTS) if (artists.size() > DEFAULT_ARTISTS)
@ -474,7 +456,7 @@ public class SearchFragment extends Fragment {
if (!albums.isEmpty()) if (!albums.isEmpty())
{ {
mergeAdapter.addView(albumsHeading); mergeAdapter.addView(albumsHeading);
List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size()))); List<MusicDirectory.Entry> displayedAlbums = new ArrayList<>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size())));
albumAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedAlbums, false); albumAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedAlbums, false);
mergeAdapter.addAdapter(albumAdapter); mergeAdapter.addAdapter(albumAdapter);
if (albums.size() > DEFAULT_ALBUMS) if (albums.size() > DEFAULT_ALBUMS)
@ -487,7 +469,7 @@ public class SearchFragment extends Fragment {
if (!songs.isEmpty()) if (!songs.isEmpty())
{ {
mergeAdapter.addView(songsHeading); mergeAdapter.addView(songsHeading);
List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size()))); List<MusicDirectory.Entry> displayedSongs = new ArrayList<>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size())));
songAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedSongs, false); songAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedSongs, false);
mergeAdapter.addAdapter(songAdapter); mergeAdapter.addAdapter(songAdapter);
if (songs.size() > DEFAULT_SONGS) if (songs.size() > DEFAULT_SONGS)

View File

@ -39,7 +39,7 @@ import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator; import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator;
import org.moire.ultrasonic.util.Pair; import org.moire.ultrasonic.util.Pair;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.AlbumView; import org.moire.ultrasonic.view.AlbumView;
import org.moire.ultrasonic.view.EntryAdapter; import org.moire.ultrasonic.view.EntryAdapter;
@ -152,7 +152,7 @@ public class SelectAlbumFragment extends Fragment {
}); });
// TODO: Long click on an item will first try to maximize / collapse the item, even when it fits inside the TextView. // TODO: Long click on an item will first try to maximize / collapse the item, even when it fits inside the TextView.
// The context menu is only displayed on the second long click... // The context menu is only displayed on the second long click... This may be improved somehow, e.g. checking first if the texts fit
albumListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener(){ albumListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener(){
@Override @Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
@ -258,7 +258,11 @@ public class SelectAlbumFragment extends Fragment {
registerForContextMenu(albumListView); registerForContextMenu(albumListView);
setHasOptionsMenu(true); setHasOptionsMenu(true);
enableButtons(); enableButtons();
updateDisplay(false);
}
private void updateDisplay(boolean refresh)
{
String id = getArguments().getString(Constants.INTENT_EXTRA_NAME_ID); String id = getArguments().getString(Constants.INTENT_EXTRA_NAME_ID);
boolean isAlbum = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false); boolean isAlbum = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false);
String name = getArguments().getString(Constants.INTENT_EXTRA_NAME_NAME); String name = getArguments().getString(Constants.INTENT_EXTRA_NAME_NAME);
@ -302,7 +306,7 @@ public class SelectAlbumFragment extends Fragment {
} }
else if (getVideos != 0) else if (getVideos != 0)
{ {
getVideos(); getVideos(refresh);
} }
else if (getRandomTracks != 0) else if (getRandomTracks != 0)
{ {
@ -314,16 +318,16 @@ public class SelectAlbumFragment extends Fragment {
{ {
if (isAlbum) if (isAlbum)
{ {
getAlbum(id, name, parentId); getAlbum(refresh, id, name, parentId);
} }
else else
{ {
getArtist(id, name); getArtist(refresh, id, name);
} }
} }
else else
{ {
getMusicDirectory(id, name, parentId); getMusicDirectory(refresh, id, name, parentId);
} }
} }
} }
@ -377,36 +381,28 @@ public class SelectAlbumFragment extends Fragment {
String entryId = entry.getId(); String entryId = entry.getId();
switch (menuItem.getItemId()) int itemId = menuItem.getItemId();
{ if (itemId == R.id.album_menu_play_now) {
case R.id.album_menu_play_now: downloadHandler.getValue().downloadRecursively(this, entryId, false, false, true, false, false, false, false, false);
downloadHandler.getValue().downloadRecursively(this, entryId, false, false, true, false, false, false, false, false); } else if (itemId == R.id.album_menu_play_next) {
break; downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, true, false, false);
case R.id.album_menu_play_next: } else if (itemId == R.id.album_menu_play_last) {
downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, true, false, false); downloadHandler.getValue().downloadRecursively(this, entryId, false, true, false, false, false, false, false, false);
break; } else if (itemId == R.id.album_menu_pin) {
case R.id.album_menu_play_last: downloadHandler.getValue().downloadRecursively(this, entryId, true, true, false, false, false, false, false, false);
downloadHandler.getValue().downloadRecursively(this, entryId, false, true, false, false, false, false, false, false); } else if (itemId == R.id.album_menu_unpin) {
break; downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, false, true, false);
case R.id.album_menu_pin: } else if (itemId == R.id.album_menu_download) {
downloadHandler.getValue().downloadRecursively(this, entryId, true, true, false, false, false, false, false, false); downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, true, false, false, false);
break; } else if (itemId == R.id.select_album_play_all) {
case R.id.album_menu_unpin: playAll();
downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, false, true, false); } else if (itemId == R.id.menu_item_share) {
break; List<MusicDirectory.Entry> entries = new ArrayList<MusicDirectory.Entry>(1);
case R.id.album_menu_download: entries.add(entry);
downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, true, false, false, false); shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken);
break; return true;
case R.id.select_album_play_all: } else {
playAll(); return super.onContextItemSelected(menuItem);
break;
case R.id.menu_item_share:
List<MusicDirectory.Entry> entries = new ArrayList<MusicDirectory.Entry>(1);
entries.add(entry);
shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken);
return true;
default:
return super.onContextItemSelected(menuItem);
} }
return true; return true;
} }
@ -439,14 +435,13 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) public boolean onOptionsItemSelected(MenuItem item)
{ {
switch (item.getItemId()) int itemId = item.getItemId();
{ if (itemId == R.id.select_album_play_all) {
case R.id.select_album_play_all: playAll();
playAll(); return true;
return true; } else if (itemId == R.id.menu_item_share) {
case R.id.menu_item_share: shareHandler.getValue().createShare(this, getSelectedSongs(albumListView), refreshAlbumListView, cancellationToken);
shareHandler.getValue().createShare(this, getSelectedSongs(albumListView), refreshAlbumListView, cancellationToken); return true;
return true;
} }
return false; return false;
@ -528,25 +523,16 @@ public class SelectAlbumFragment extends Fragment {
private void refresh() private void refresh()
{ {
// TODO: create better restart
getView().post(new Runnable() { getView().post(new Runnable() {
public void run() { public void run() {
Timber.d("Refresh called..."); updateDisplay(true);
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
onViewCreated(getView(), null);
} }
}); });
/*finish();
Intent intent = getArguments();
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
startActivityForResultWithoutTransition(this, intent);*/
} }
private void getMusicDirectory(final String id, final String name, final String parentId) private void getMusicDirectory(final boolean refresh, final String id, final String name, final String parentId)
{ {
FragmentTitle.Companion.setTitle(this, name); FragmentTitle.Companion.setTitle(this, name);
//setActionBarSubtitle(name);
new LoadTask() new LoadTask()
{ {
@ -557,8 +543,7 @@ public class SelectAlbumFragment extends Fragment {
if (allSongsId.equals(id)) if (allSongsId.equals(id))
{ {
boolean refresh = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); MusicDirectory musicDirectory = service.getMusicDirectory(parentId, name, refresh, getContext());
MusicDirectory musicDirectory = service.getMusicDirectory(parentId, name, refresh, getContext(), this);
List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>(); List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
getSongsRecursively(musicDirectory, songs); getSongsRecursively(musicDirectory, songs);
@ -573,8 +558,7 @@ public class SelectAlbumFragment extends Fragment {
} }
else else
{ {
boolean refresh = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); MusicDirectory musicDirectory = service.getMusicDirectory(id, name, refresh, getContext());
MusicDirectory musicDirectory = service.getMusicDirectory(id, name, refresh, getContext(), this);
if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size()) if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size())
{ {
@ -622,7 +606,7 @@ public class SelectAlbumFragment extends Fragment {
if (!allSongsId.equals(dir.getId())) if (!allSongsId.equals(dir.getId()))
{ {
root = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, getContext(), this); root = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, getContext());
getSongsRecursively(root, songs); getSongsRecursively(root, songs);
} }
@ -631,10 +615,9 @@ public class SelectAlbumFragment extends Fragment {
}.execute(); }.execute();
} }
private void getArtist(final String id, final String name) private void getArtist(final boolean refresh, final String id, final String name)
{ {
FragmentTitle.Companion.setTitle(this, name); FragmentTitle.Companion.setTitle(this, name);
//setActionBarSubtitle(name);
new LoadTask() new LoadTask()
{ {
@ -643,8 +626,7 @@ public class SelectAlbumFragment extends Fragment {
{ {
MusicDirectory root = new MusicDirectory(); MusicDirectory root = new MusicDirectory();
boolean refresh = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); MusicDirectory musicDirectory = service.getArtist(id, name, refresh, getContext());
MusicDirectory musicDirectory = service.getArtist(id, name, refresh, getContext(), this);
if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size()) if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size())
{ {
@ -675,10 +657,9 @@ public class SelectAlbumFragment extends Fragment {
}.execute(); }.execute();
} }
private void getAlbum(final String id, final String name, final String parentId) private void getAlbum(final boolean refresh, final String id, final String name, final String parentId)
{ {
FragmentTitle.Companion.setTitle(this, name); FragmentTitle.Companion.setTitle(this, name);
//setActionBarSubtitle(name);
new LoadTask() new LoadTask()
{ {
@ -687,8 +668,6 @@ public class SelectAlbumFragment extends Fragment {
{ {
MusicDirectory musicDirectory; MusicDirectory musicDirectory;
boolean refresh = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false);
if (allSongsId.equals(id)) if (allSongsId.equals(id))
{ {
MusicDirectory root = new MusicDirectory(); MusicDirectory root = new MusicDirectory();
@ -708,7 +687,7 @@ public class SelectAlbumFragment extends Fragment {
} }
else else
{ {
musicDirectory = service.getAlbum(id, name, refresh, getContext(), this); musicDirectory = service.getAlbum(id, name, refresh, getContext());
} }
return musicDirectory; return musicDirectory;
@ -717,13 +696,13 @@ public class SelectAlbumFragment extends Fragment {
private void getSongsForArtist(String id, Collection<MusicDirectory.Entry> songs) throws Exception private void getSongsForArtist(String id, Collection<MusicDirectory.Entry> songs) throws Exception
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
MusicDirectory artist = musicService.getArtist(id, "", false, getContext(), this); MusicDirectory artist = musicService.getArtist(id, "", false, getContext());
for (MusicDirectory.Entry album : artist.getChildren()) for (MusicDirectory.Entry album : artist.getChildren())
{ {
if (!allSongsId.equals(album.getId())) if (!allSongsId.equals(album.getId()))
{ {
MusicDirectory albumDirectory = musicService.getAlbum(album.getId(), "", false, getContext(), this); MusicDirectory albumDirectory = musicService.getAlbum(album.getId(), "", false, getContext());
for (MusicDirectory.Entry song : albumDirectory.getChildren()) for (MusicDirectory.Entry song : albumDirectory.getChildren())
{ {
@ -741,14 +720,13 @@ public class SelectAlbumFragment extends Fragment {
private void getSongsForGenre(final String genre, final int count, final int offset) private void getSongsForGenre(final String genre, final int count, final int offset)
{ {
FragmentTitle.Companion.setTitle(this, genre); FragmentTitle.Companion.setTitle(this, genre);
//setActionBarSubtitle(genre);
new LoadTask() new LoadTask()
{ {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return service.getSongsByGenre(genre, count, offset, getContext(), this); return service.getSongsByGenre(genre, count, offset, getContext());
} }
@Override @Override
@ -789,32 +767,29 @@ public class SelectAlbumFragment extends Fragment {
private void getStarred() private void getStarred()
{ {
FragmentTitle.Companion.setTitle(this, R.string.main_songs_starred); FragmentTitle.Companion.setTitle(this, R.string.main_songs_starred);
//setActionBarSubtitle(R.string.main_songs_starred);
new LoadTask() new LoadTask()
{ {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return Util.getShouldUseId3Tags(getContext()) ? Util.getSongsFromSearchResult(service.getStarred2(getContext(), this)) : Util.getSongsFromSearchResult(service.getStarred(getContext(), this)); return Util.getShouldUseId3Tags(getContext()) ? Util.getSongsFromSearchResult(service.getStarred2(getContext())) : Util.getSongsFromSearchResult(service.getStarred(getContext()));
} }
}.execute(); }.execute();
} }
private void getVideos() private void getVideos(final boolean refresh)
{ {
showHeader = false; showHeader = false;
FragmentTitle.Companion.setTitle(this, R.string.main_videos); FragmentTitle.Companion.setTitle(this, R.string.main_videos);
//setActionBarSubtitle(R.string.main_videos);
new LoadTask() new LoadTask()
{ {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
boolean refresh = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); return service.getVideos(refresh, getContext());
return service.getVideos(refresh, getContext(), this);
} }
}.execute(); }.execute();
} }
@ -822,7 +797,6 @@ public class SelectAlbumFragment extends Fragment {
private void getRandom(final int size) private void getRandom(final int size)
{ {
FragmentTitle.Companion.setTitle(this, R.string.main_songs_random); FragmentTitle.Companion.setTitle(this, R.string.main_songs_random);
//setActionBarSubtitle(R.string.main_songs_random);
new LoadTask() new LoadTask()
{ {
@ -834,7 +808,7 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return service.getRandomSongs(size, getContext(), this); return service.getRandomSongs(size, getContext());
} }
}.execute(); }.execute();
} }
@ -842,29 +816,27 @@ public class SelectAlbumFragment extends Fragment {
private void getPlaylist(final String playlistId, final String playlistName) private void getPlaylist(final String playlistId, final String playlistName)
{ {
FragmentTitle.Companion.setTitle(this, playlistName); FragmentTitle.Companion.setTitle(this, playlistName);
//setActionBarSubtitle(playlistName);
new LoadTask() new LoadTask()
{ {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return service.getPlaylist(playlistId, playlistName, getContext(), this); return service.getPlaylist(playlistId, playlistName, getContext());
} }
}.execute(); }.execute();
} }
private void getPodcastEpisodes(final String podcastChannelId) private void getPodcastEpisodes(final String podcastChannelId)
{ {
// TODO: Not sure what the title should be for a podcast episode. Maybe a constant string should be used. FragmentTitle.Companion.setTitle(this, R.string.podcasts_label);
//setActionBarSubtitle(playlistName);
new LoadTask() new LoadTask()
{ {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return service.getPodcastEpisodes(podcastChannelId, getContext(), this); return service.getPodcastEpisodes(podcastChannelId, getContext());
} }
}.execute(); }.execute();
} }
@ -879,7 +851,7 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
List<Share> shares = service.getShares(true, getContext(), this); List<Share> shares = service.getShares(true, getContext());
MusicDirectory md = new MusicDirectory(); MusicDirectory md = new MusicDirectory();
@ -920,7 +892,7 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
protected MusicDirectory load(MusicService service) throws Exception protected MusicDirectory load(MusicService service) throws Exception
{ {
return Util.getShouldUseId3Tags(getContext()) ? service.getAlbumList2(albumListType, size, offset, getContext(), this) : service.getAlbumList(albumListType, size, offset, getContext(), this); return Util.getShouldUseId3Tags(getContext()) ? service.getAlbumList2(albumListType, size, offset, getContext()) : service.getAlbumList(albumListType, size, offset, getContext());
} }
@Override @Override
@ -1103,7 +1075,7 @@ public class SelectAlbumFragment extends Fragment {
mediaPlayerControllerLazy.getValue().unpin(songs); mediaPlayerControllerLazy.getValue().unpin(songs);
} }
private abstract class LoadTask extends TabActivityBackgroundTask<Pair<MusicDirectory, Boolean>> private abstract class LoadTask extends FragmentBackgroundTask<Pair<MusicDirectory, Boolean>>
{ {
public LoadTask() public LoadTask()
@ -1122,7 +1094,7 @@ public class SelectAlbumFragment extends Fragment {
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
MusicDirectory dir = load(musicService); MusicDirectory dir = load(musicService);
boolean valid = musicService.isLicenseValid(getContext(), this); boolean valid = musicService.isLicenseValid(getContext());
return new Pair<MusicDirectory, Boolean>(dir, valid); return new Pair<MusicDirectory, Boolean>(dir, valid);
} }

View File

@ -21,7 +21,7 @@ import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.BackgroundTask; import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.view.GenreAdapter; import org.moire.ultrasonic.view.GenreAdapter;
@ -75,7 +75,7 @@ public class SelectGenreFragment extends Fragment {
bundle.putString(Constants.INTENT_EXTRA_NAME_GENRE_NAME, genre.getName()); bundle.putString(Constants.INTENT_EXTRA_NAME_GENRE_NAME, genre.getName());
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(getContext())); bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(getContext()));
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); Navigation.findNavController(view).navigate(R.id.selectAlbumFragment, bundle);
} }
} }
}); });
@ -84,8 +84,7 @@ public class SelectGenreFragment extends Fragment {
registerForContextMenu(genreListView); registerForContextMenu(genreListView);
FragmentTitle.Companion.setTitle(this, R.string.main_genres_title); FragmentTitle.Companion.setTitle(this, R.string.main_genres_title);
load(false);
load();
} }
@Override @Override
@ -96,43 +95,23 @@ public class SelectGenreFragment extends Fragment {
private void refresh() private void refresh()
{ {
// TODO: create better restart load(true);
getView().post(new Runnable() {
public void run() {
Timber.d("Refresh called...");
if (getArguments() == null) {
Bundle bundle = new Bundle();
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
setArguments(bundle);
} else {
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
}
onViewCreated(getView(), null);
}
});
/* finish();
Intent intent = getIntent();
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
startActivityForResultWithoutTransition(this, intent);
*/
} }
private void load() private void load(final boolean refresh)
{ {
BackgroundTask<List<Genre>> task = new TabActivityBackgroundTask<List<Genre>>(getActivity(), true, refreshGenreListView, cancellationToken) BackgroundTask<List<Genre>> task = new FragmentBackgroundTask<List<Genre>>(getActivity(), true, refreshGenreListView, cancellationToken)
{ {
@Override @Override
protected List<Genre> doInBackground() throws Throwable protected List<Genre> doInBackground() throws Throwable
{ {
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false);
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
List<Genre> genres = new ArrayList<Genre>(); List<Genre> genres = new ArrayList<Genre>();
try try
{ {
genres = musicService.getGenres(refresh, getContext(), this); genres = musicService.getGenres(refresh, getContext());
} }
catch (Exception x) catch (Exception x)
{ {

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.fragment;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
@ -40,7 +39,7 @@ import org.moire.ultrasonic.util.BackgroundTask;
import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.CancellationToken;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.LoadingTask; import org.moire.ultrasonic.util.LoadingTask;
import org.moire.ultrasonic.util.TabActivityBackgroundTask; import org.moire.ultrasonic.util.FragmentBackgroundTask;
import org.moire.ultrasonic.util.TimeSpan; import org.moire.ultrasonic.util.TimeSpan;
import org.moire.ultrasonic.util.TimeSpanPicker; import org.moire.ultrasonic.util.TimeSpanPicker;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
@ -49,7 +48,6 @@ import org.moire.ultrasonic.view.ShareAdapter;
import java.util.List; import java.util.List;
import kotlin.Lazy; import kotlin.Lazy;
import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
@ -105,14 +103,13 @@ public class SharesFragment extends Fragment {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_ID, share.getId()); bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_ID, share.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_NAME, share.getName()); bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_NAME, share.getName());
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); Navigation.findNavController(view).navigate(R.id.selectAlbumFragment, bundle);
} }
}); });
registerForContextMenu(sharesListView); registerForContextMenu(sharesListView);
FragmentTitle.Companion.setTitle(this, R.string.button_bar_shares); FragmentTitle.Companion.setTitle(this, R.string.button_bar_shares);
load(); load(false);
} }
@Override @Override
@ -123,39 +120,18 @@ public class SharesFragment extends Fragment {
private void refresh() private void refresh()
{ {
// TODO: create better restart load(true);
getView().post(new Runnable() {
public void run() {
Timber.d("Refresh called...");
if (getArguments() == null) {
Bundle bundle = new Bundle();
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
setArguments(bundle);
} else {
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
}
onViewCreated(getView(), null);
}
});
/* finish();
Intent intent = new Intent(this, ShareActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
startActivityForResultWithoutTransition(this, intent);
*/
} }
private void load() private void load(final boolean refresh)
{ {
BackgroundTask<List<Share>> task = new TabActivityBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken) BackgroundTask<List<Share>> task = new FragmentBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken)
{ {
@Override @Override
protected List<Share> doInBackground() throws Throwable protected List<Share> doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false); return musicService.getShares(refresh, getContext());
return musicService.getShares(refresh, getContext(), this);
} }
@Override @Override
@ -181,45 +157,30 @@ public class SharesFragment extends Fragment {
public boolean onContextItemSelected(MenuItem menuItem) public boolean onContextItemSelected(MenuItem menuItem)
{ {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
if (info == null) if (info == null) return false;
{
return false;
}
Share share = (Share) sharesListView.getItemAtPosition(info.position); Share share = (Share) sharesListView.getItemAtPosition(info.position);
if (share == null) if (share == null || share.getId() == null) return false;
{
return false;
}
switch (menuItem.getItemId()) int itemId = menuItem.getItemId();
{ if (itemId == R.id.share_menu_pin) {
case R.id.share_menu_pin: downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), true, true, false, false, true, false, false);
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), true, true, false, false, true, false, false); } else if (itemId == R.id.share_menu_unpin) {
break; downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, true);
case R.id.share_menu_unpin: } else if (itemId == R.id.share_menu_download) {
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, true); downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, false);
break; } else if (itemId == R.id.share_menu_play_now) {
case R.id.share_menu_download: downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, false, false, false, false);
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, false); } else if (itemId == R.id.share_menu_play_shuffled) {
break; downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, true, false, false, false);
case R.id.share_menu_play_now: } else if (itemId == R.id.share_menu_delete) {
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, false, false, false, false); deleteShare(share);
break; } else if (itemId == R.id.share_info) {
case R.id.share_menu_play_shuffled: displayShareInfo(share);
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, true, false, false, false); } else if (itemId == R.id.share_update_info) {
break; updateShareInfo(share);
case R.id.share_menu_delete: } else {
deleteShare(share); return super.onContextItemSelected(menuItem);
break;
case R.id.share_info:
displayShareInfo(share);
break;
case R.id.share_update_info:
updateShareInfo(share);
break;
default:
return super.onContextItemSelected(menuItem);
} }
return true; return true;
} }
@ -237,7 +198,7 @@ public class SharesFragment extends Fragment {
protected Void doInBackground() throws Throwable protected Void doInBackground() throws Throwable
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.deleteShare(share.getId(), getContext(), null); musicService.deleteShare(share.getId(), getContext());
return null; return null;
} }
@ -343,7 +304,7 @@ public class SharesFragment extends Fragment {
String description = shareDescriptionText != null ? shareDescriptionText.toString() : null; String description = shareDescriptionText != null ? shareDescriptionText.toString() : null;
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
musicService.updateShare(share.getId(), description, millis, getContext(), null); musicService.updateShare(share.getId(), description, millis, getContext());
return null; return null;
} }

View File

@ -39,7 +39,6 @@ import org.moire.ultrasonic.domain.UserInfo;
import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.CancellableTask;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.LRUCache; import org.moire.ultrasonic.util.LRUCache;
import org.moire.ultrasonic.util.ProgressListener;
import org.moire.ultrasonic.util.TimeLimitedCache; import org.moire.ultrasonic.util.TimeLimitedCache;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
@ -59,7 +58,7 @@ import static org.koin.java.KoinJavaComponent.inject;
*/ */
public class CachedMusicService implements MusicService public class CachedMusicService implements MusicService
{ {
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class); private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
private static final int MUSIC_DIR_CACHE_SIZE = 100; private static final int MUSIC_DIR_CACHE_SIZE = 100;
@ -81,36 +80,36 @@ public class CachedMusicService implements MusicService
public CachedMusicService(MusicService musicService) public CachedMusicService(MusicService musicService)
{ {
this.musicService = musicService; this.musicService = musicService;
cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE); cachedMusicDirectories = new LRUCache<>(MUSIC_DIR_CACHE_SIZE);
cachedArtist = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE); cachedArtist = new LRUCache<>(MUSIC_DIR_CACHE_SIZE);
cachedAlbum = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE); cachedAlbum = new LRUCache<>(MUSIC_DIR_CACHE_SIZE);
cachedUserInfo = new LRUCache<String, TimeLimitedCache<UserInfo>>(MUSIC_DIR_CACHE_SIZE); cachedUserInfo = new LRUCache<>(MUSIC_DIR_CACHE_SIZE);
} }
@Override @Override
public void ping(Context context, ProgressListener progressListener) throws Exception public void ping(Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
musicService.ping(context, progressListener); musicService.ping(context);
} }
@Override @Override
public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception public boolean isLicenseValid(Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
Boolean result = cachedLicenseValid.get(); Boolean result = cachedLicenseValid.get();
if (result == null) if (result == null)
{ {
result = musicService.isLicenseValid(context, progressListener); result = musicService.isLicenseValid(context);
cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS); cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS);
} }
return result; return result;
} }
@Override @Override
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<MusicFolder> getMusicFolders(boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
if (refresh) if (refresh)
{ {
cachedMusicFolders.clear(); cachedMusicFolders.clear();
@ -118,16 +117,16 @@ public class CachedMusicService implements MusicService
List<MusicFolder> result = cachedMusicFolders.get(); List<MusicFolder> result = cachedMusicFolders.get();
if (result == null) if (result == null)
{ {
result = musicService.getMusicFolders(refresh, context, progressListener); result = musicService.getMusicFolders(refresh, context);
cachedMusicFolders.set(result); cachedMusicFolders.set(result);
} }
return result; return result;
} }
@Override @Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception public Indexes getIndexes(String musicFolderId, boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
if (refresh) if (refresh)
{ {
cachedIndexes.clear(); cachedIndexes.clear();
@ -137,16 +136,16 @@ public class CachedMusicService implements MusicService
Indexes result = cachedIndexes.get(); Indexes result = cachedIndexes.get();
if (result == null) if (result == null)
{ {
result = musicService.getIndexes(musicFolderId, refresh, context, progressListener); result = musicService.getIndexes(musicFolderId, refresh, context);
cachedIndexes.set(result); cachedIndexes.set(result);
} }
return result; return result;
} }
@Override @Override
public Indexes getArtists(boolean refresh, Context context, ProgressListener progressListener) throws Exception public Indexes getArtists(boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
if (refresh) if (refresh)
{ {
cachedArtists.clear(); cachedArtists.clear();
@ -154,24 +153,24 @@ public class CachedMusicService implements MusicService
Indexes result = cachedArtists.get(); Indexes result = cachedArtists.get();
if (result == null) if (result == null)
{ {
result = musicService.getArtists(refresh, context, progressListener); result = musicService.getArtists(refresh, context);
cachedArtists.set(result); cachedArtists.set(result);
} }
return result; return result;
} }
@Override @Override
public MusicDirectory getMusicDirectory(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getMusicDirectory(String id, String name, boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(id); TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(id);
MusicDirectory dir = cache == null ? null : cache.get(); MusicDirectory dir = cache == null ? null : cache.get();
if (dir == null) if (dir == null)
{ {
dir = musicService.getMusicDirectory(id, name, refresh, context, progressListener); dir = musicService.getMusicDirectory(id, name, refresh, context);
cache = new TimeLimitedCache<MusicDirectory>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); cache = new TimeLimitedCache<>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS);
cache.set(dir); cache.set(dir);
cachedMusicDirectories.put(id, cache); cachedMusicDirectories.put(id, cache);
} }
@ -179,15 +178,15 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getArtist(String id, String name, boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedArtist.get(id); TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedArtist.get(id);
MusicDirectory dir = cache == null ? null : cache.get(); MusicDirectory dir = cache == null ? null : cache.get();
if (dir == null) if (dir == null)
{ {
dir = musicService.getArtist(id, name, refresh, context, progressListener); dir = musicService.getArtist(id, name, refresh, context);
cache = new TimeLimitedCache<MusicDirectory>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); cache = new TimeLimitedCache<>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS);
cache.set(dir); cache.set(dir);
cachedArtist.put(id, cache); cachedArtist.put(id, cache);
} }
@ -195,15 +194,15 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedAlbum.get(id); TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedAlbum.get(id);
MusicDirectory dir = cache == null ? null : cache.get(); MusicDirectory dir = cache == null ? null : cache.get();
if (dir == null) if (dir == null)
{ {
dir = musicService.getAlbum(id, name, refresh, context, progressListener); dir = musicService.getAlbum(id, name, refresh, context);
cache = new TimeLimitedCache<MusicDirectory>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); cache = new TimeLimitedCache<>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS);
cache.set(dir); cache.set(dir);
cachedAlbum.put(id, cache); cachedAlbum.put(id, cache);
} }
@ -211,113 +210,113 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public SearchResult search(SearchCriteria criteria, Context context, ProgressListener progressListener) throws Exception public SearchResult search(SearchCriteria criteria, Context context) throws Exception
{ {
return musicService.search(criteria, context, progressListener); return musicService.search(criteria, context);
} }
@Override @Override
public MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getPlaylist(String id, String name, Context context) throws Exception
{ {
return musicService.getPlaylist(id, name, context, progressListener); return musicService.getPlaylist(id, name, context);
} }
@Override @Override
public List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception { public List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context) throws Exception {
checkSettingsChanged(context); checkSettingsChanged();
List<PodcastsChannel> result = refresh ? null : cachedPodcastsChannels.get(); List<PodcastsChannel> result = refresh ? null : cachedPodcastsChannels.get();
if (result == null) if (result == null)
{ {
result = musicService.getPodcastsChannels(refresh, context, progressListener); result = musicService.getPodcastsChannels(refresh, context);
cachedPodcastsChannels.set(result); cachedPodcastsChannels.set(result);
} }
return result; return result;
} }
@Override @Override
public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) throws Exception { public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context) throws Exception {
return musicService.getPodcastEpisodes(podcastChannelId,context,progressListener); return musicService.getPodcastEpisodes(podcastChannelId,context);
} }
@Override @Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<Playlist> getPlaylists(boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
List<Playlist> result = refresh ? null : cachedPlaylists.get(); List<Playlist> result = refresh ? null : cachedPlaylists.get();
if (result == null) if (result == null)
{ {
result = musicService.getPlaylists(refresh, context, progressListener); result = musicService.getPlaylists(refresh, context);
cachedPlaylists.set(result); cachedPlaylists.set(result);
} }
return result; return result;
} }
@Override @Override
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context) throws Exception
{ {
cachedPlaylists.clear(); cachedPlaylists.clear();
musicService.createPlaylist(id, name, entries, context, progressListener); musicService.createPlaylist(id, name, entries, context);
} }
@Override @Override
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception public void deletePlaylist(String id, Context context) throws Exception
{ {
musicService.deletePlaylist(id, context, progressListener); musicService.deletePlaylist(id, context);
} }
@Override @Override
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception public void updatePlaylist(String id, String name, String comment, boolean pub, Context context) throws Exception
{ {
musicService.updatePlaylist(id, name, comment, pub, context, progressListener); musicService.updatePlaylist(id, name, comment, pub, context);
} }
@Override @Override
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception public Lyrics getLyrics(String artist, String title, Context context) throws Exception
{ {
return musicService.getLyrics(artist, title, context, progressListener); return musicService.getLyrics(artist, title, context);
} }
@Override @Override
public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception public void scrobble(String id, boolean submission, Context context) throws Exception
{ {
musicService.scrobble(id, submission, context, progressListener); musicService.scrobble(id, submission, context);
} }
@Override @Override
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getAlbumList(String type, int size, int offset, Context context) throws Exception
{ {
return musicService.getAlbumList(type, size, offset, context, progressListener); return musicService.getAlbumList(type, size, offset, context);
} }
@Override @Override
public MusicDirectory getAlbumList2(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getAlbumList2(String type, int size, int offset, Context context) throws Exception
{ {
return musicService.getAlbumList2(type, size, offset, context, progressListener); return musicService.getAlbumList2(type, size, offset, context);
} }
@Override @Override
public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getRandomSongs(int size, Context context) throws Exception
{ {
return musicService.getRandomSongs(size, context, progressListener); return musicService.getRandomSongs(size, context);
} }
@Override @Override
public SearchResult getStarred(Context context, ProgressListener progressListener) throws Exception public SearchResult getStarred(Context context) throws Exception
{ {
return musicService.getStarred(context, progressListener); return musicService.getStarred(context);
} }
@Override @Override
public SearchResult getStarred2(Context context, ProgressListener progressListener) throws Exception public SearchResult getStarred2(Context context) throws Exception
{ {
return musicService.getStarred2(context, progressListener); return musicService.getStarred2(context);
} }
@Override @Override
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality) throws Exception
{ {
return musicService.getCoverArt(context, entry, size, saveToFile, highQuality, progressListener); return musicService.getCoverArt(context, entry, size, saveToFile, highQuality);
} }
@Override @Override
@ -333,42 +332,42 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context) throws Exception
{ {
return musicService.updateJukeboxPlaylist(ids, context, progressListener); return musicService.updateJukeboxPlaylist(ids, context);
} }
@Override @Override
public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context) throws Exception
{ {
return musicService.skipJukebox(index, offsetSeconds, context, progressListener); return musicService.skipJukebox(index, offsetSeconds, context);
} }
@Override @Override
public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus stopJukebox(Context context) throws Exception
{ {
return musicService.stopJukebox(context, progressListener); return musicService.stopJukebox(context);
} }
@Override @Override
public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus startJukebox(Context context) throws Exception
{ {
return musicService.startJukebox(context, progressListener); return musicService.startJukebox(context);
} }
@Override @Override
public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus getJukeboxStatus(Context context) throws Exception
{ {
return musicService.getJukeboxStatus(context, progressListener); return musicService.getJukeboxStatus(context);
} }
@Override @Override
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus setJukeboxGain(float gain, Context context) throws Exception
{ {
return musicService.setJukeboxGain(gain, context, progressListener); return musicService.setJukeboxGain(gain, context);
} }
private void checkSettingsChanged(Context context) private void checkSettingsChanged()
{ {
String newUrl = activeServerProvider.getValue().getRestUrl(null); String newUrl = activeServerProvider.getValue().getRestUrl(null);
if (!Util.equals(newUrl, restUrl)) if (!Util.equals(newUrl, restUrl))
@ -387,27 +386,27 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public void star(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception public void star(String id, String albumId, String artistId, Context context) throws Exception
{ {
musicService.star(id, albumId, artistId, context, progressListener); musicService.star(id, albumId, artistId, context);
} }
@Override @Override
public void unstar(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception public void unstar(String id, String albumId, String artistId, Context context) throws Exception
{ {
musicService.unstar(id, albumId, artistId, context, progressListener); musicService.unstar(id, albumId, artistId, context);
} }
@Override @Override
public void setRating(String id, int rating, Context context, ProgressListener progressListener) throws Exception public void setRating(String id, int rating, Context context) throws Exception
{ {
musicService.setRating(id, rating, context, progressListener); musicService.setRating(id, rating, context);
} }
@Override @Override
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<Genre> getGenres(boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
if (refresh) if (refresh)
{ {
cachedGenres.clear(); cachedGenres.clear();
@ -416,7 +415,7 @@ public class CachedMusicService implements MusicService
if (result == null) if (result == null)
{ {
result = musicService.getGenres(refresh, context, progressListener); result = musicService.getGenres(refresh, context);
cachedGenres.set(result); cachedGenres.set(result);
} }
@ -433,59 +432,59 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context) throws Exception
{ {
return musicService.getSongsByGenre(genre, count, offset, context, progressListener); return musicService.getSongsByGenre(genre, count, offset, context);
} }
@Override @Override
public List<Share> getShares(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<Share> getShares(boolean refresh, Context context) throws Exception
{ {
return musicService.getShares(refresh, context, progressListener); return musicService.getShares(refresh, context);
} }
@Override @Override
public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception public List<ChatMessage> getChatMessages(Long since, Context context) throws Exception
{ {
return musicService.getChatMessages(since, context, progressListener); return musicService.getChatMessages(since, context);
} }
@Override @Override
public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception public void addChatMessage(String message, Context context) throws Exception
{ {
musicService.addChatMessage(message, context, progressListener); musicService.addChatMessage(message, context);
} }
@Override @Override
public List<Bookmark> getBookmarks(Context context, ProgressListener progressListener) throws Exception public List<Bookmark> getBookmarks(Context context) throws Exception
{ {
return musicService.getBookmarks(context, progressListener); return musicService.getBookmarks(context);
} }
@Override @Override
public void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception public void deleteBookmark(String id, Context context) throws Exception
{ {
musicService.deleteBookmark(id, context, progressListener); musicService.deleteBookmark(id, context);
} }
@Override @Override
public void createBookmark(String id, int position, Context context, ProgressListener progressListener) throws Exception public void createBookmark(String id, int position, Context context) throws Exception
{ {
musicService.createBookmark(id, position, context, progressListener); musicService.createBookmark(id, position, context);
} }
@Override @Override
public MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getVideos(boolean refresh, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(Constants.INTENT_EXTRA_NAME_VIDEOS); TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(Constants.INTENT_EXTRA_NAME_VIDEOS);
MusicDirectory dir = cache == null ? null : cache.get(); MusicDirectory dir = cache == null ? null : cache.get();
if (dir == null) if (dir == null)
{ {
dir = musicService.getVideos(refresh, context, progressListener); dir = musicService.getVideos(refresh, context);
cache = new TimeLimitedCache<MusicDirectory>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); cache = new TimeLimitedCache<>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS);
cache.set(dir); cache.set(dir);
cachedMusicDirectories.put(Constants.INTENT_EXTRA_NAME_VIDEOS, cache); cachedMusicDirectories.put(Constants.INTENT_EXTRA_NAME_VIDEOS, cache);
} }
@ -494,9 +493,9 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception public UserInfo getUser(String username, Context context) throws Exception
{ {
checkSettingsChanged(context); checkSettingsChanged();
TimeLimitedCache<UserInfo> cache = cachedUserInfo.get(username); TimeLimitedCache<UserInfo> cache = cachedUserInfo.get(username);
@ -504,8 +503,8 @@ public class CachedMusicService implements MusicService
if (userInfo == null) if (userInfo == null)
{ {
userInfo = musicService.getUser(username, context, progressListener); userInfo = musicService.getUser(username, context);
cache = new TimeLimitedCache<UserInfo>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); cache = new TimeLimitedCache<>(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS);
cache.set(userInfo); cache.set(userInfo);
cachedUserInfo.put(username, cache); cachedUserInfo.put(username, cache);
} }
@ -514,27 +513,26 @@ public class CachedMusicService implements MusicService
} }
@Override @Override
public List<Share> createShare(List<String> ids, String description, Long expires, Context context, ProgressListener progressListener) throws Exception public List<Share> createShare(List<String> ids, String description, Long expires, Context context) throws Exception
{ {
return musicService.createShare(ids, description, expires, context, progressListener); return musicService.createShare(ids, description, expires, context);
} }
@Override @Override
public void deleteShare(String id, Context context, ProgressListener progressListener) throws Exception public void deleteShare(String id, Context context) throws Exception
{ {
musicService.deleteShare(id, context, progressListener); musicService.deleteShare(id, context);
} }
@Override @Override
public void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception public void updateShare(String id, String description, Long expires, Context context) throws Exception
{ {
musicService.updateShare(id, description, expires, context, progressListener); musicService.updateShare(id, description, expires, context);
} }
@Override @Override
public Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception public Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality) throws Exception
{ {
return musicService.getAvatar(context, username, size, saveToFile, highQuality, progressListener); return musicService.getAvatar(context, username, size, saveToFile, highQuality);
} }
} }

View File

@ -67,7 +67,7 @@ public class DownloadFile
private volatile boolean saveWhenDone; private volatile boolean saveWhenDone;
private volatile boolean completeWhenDone; private volatile boolean completeWhenDone;
private Lazy<Downloader> downloader = inject(Downloader.class); private final Lazy<Downloader> downloader = inject(Downloader.class);
public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) public DownloadFile(Context context, MusicDirectory.Entry song, boolean save)
{ {
@ -456,13 +456,13 @@ public class DownloadFile
return String.format("DownloadTask (%s)", song); return String.format("DownloadTask (%s)", song);
} }
private void downloadAndSaveCoverArt(MusicService musicService) throws Exception private void downloadAndSaveCoverArt(MusicService musicService)
{ {
try try
{ {
if (!TextUtils.isEmpty(song.getCoverArt())) { if (!TextUtils.isEmpty(song.getCoverArt())) {
int size = Util.getMinDisplayMetric(context); int size = Util.getMinDisplayMetric(context);
musicService.getCoverArt(context, song, size, true, true, null); musicService.getCoverArt(context, song, size, true, true);
} }
} }
catch (Exception x) catch (Exception x)

View File

@ -67,13 +67,13 @@ public class JukeboxMediaPlayer
private JukeboxStatus jukeboxStatus; private JukeboxStatus jukeboxStatus;
private float gain = 0.5f; private float gain = 0.5f;
private VolumeToast volumeToast; private VolumeToast volumeToast;
private AtomicBoolean running = new AtomicBoolean(); private final AtomicBoolean running = new AtomicBoolean();
private Thread serviceThread; private Thread serviceThread;
private boolean enabled = false; private boolean enabled = false;
private Context context; private final Context context;
// TODO: These create circular references, try to refactor // TODO: These create circular references, try to refactor
private Lazy<MediaPlayerControllerImpl> mediaPlayerControllerLazy = inject(MediaPlayerControllerImpl.class); private final Lazy<MediaPlayerControllerImpl> mediaPlayerControllerLazy = inject(MediaPlayerControllerImpl.class);
private final Downloader downloader; private final Downloader downloader;
// TODO: Report warning if queue fills up. // TODO: Report warning if queue fills up.
@ -397,7 +397,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().getJukeboxStatus(context, null); return getMusicService().getJukeboxStatus(context);
} }
} }
@ -413,7 +413,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().updateJukeboxPlaylist(ids, context, null); return getMusicService().updateJukeboxPlaylist(ids, context);
} }
} }
@ -431,7 +431,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().skipJukebox(index, offsetSeconds, context, null); return getMusicService().skipJukebox(index, offsetSeconds, context);
} }
} }
@ -440,7 +440,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().stopJukebox(context, null); return getMusicService().stopJukebox(context);
} }
} }
@ -449,7 +449,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().startJukebox(context, null); return getMusicService().startJukebox(context);
} }
} }
@ -466,7 +466,7 @@ public class JukeboxMediaPlayer
@Override @Override
JukeboxStatus execute() throws Exception JukeboxStatus execute() throws Exception
{ {
return getMusicService().setJukeboxGain(gain, context, null); return getMusicService().setJukeboxGain(gain, context);
} }
} }

View File

@ -23,8 +23,6 @@ import android.content.Intent;
import timber.log.Timber; import timber.log.Timber;
import org.koin.java.KoinJavaComponent; import org.koin.java.KoinJavaComponent;
import org.moire.ultrasonic.audiofx.EqualizerController;
import org.moire.ultrasonic.audiofx.VisualizerController;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.MusicDirectory.Entry;
@ -60,9 +58,10 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
private boolean showVisualization; private boolean showVisualization;
private boolean autoPlayStart; private boolean autoPlayStart;
private Context context; private final Context context;
private Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); private final Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class);
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class); private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
private final DownloadQueueSerializer downloadQueueSerializer; private final DownloadQueueSerializer downloadQueueSerializer;
private final ExternalStorageMonitor externalStorageMonitor; private final ExternalStorageMonitor externalStorageMonitor;
private final Downloader downloader; private final Downloader downloader;
@ -522,7 +521,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
try try
{ {
String username = activeServerProvider.getValue().getActiveServer().getUserName(); String username = activeServerProvider.getValue().getActiveServer().getUserName();
UserInfo user = MusicServiceFactory.getMusicService(context).getUser(username, context, null); UserInfo user = MusicServiceFactory.getMusicService(context).getUser(username, context);
return user.getJukeboxRole(); return user.getJukeboxRole();
} }
catch (Exception e) catch (Exception e)
@ -595,7 +594,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
{ {
try try
{ {
MusicServiceFactory.getMusicService(context).setRating(song.getId(), rating, context, null); MusicServiceFactory.getMusicService(context).setRating(song.getId(), rating, context);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -541,7 +541,7 @@ public class MediaPlayerService extends Service
MusicService musicService = MusicServiceFactory.getMusicService(MediaPlayerService.this); MusicService musicService = MusicServiceFactory.getMusicService(MediaPlayerService.this);
try try
{ {
musicService.deleteBookmark(song.getId(), MediaPlayerService.this, null); musicService.deleteBookmark(song.getId(), MediaPlayerService.this);
} }
catch (Exception ignored) catch (Exception ignored)
{ {

View File

@ -36,7 +36,6 @@ import org.moire.ultrasonic.domain.SearchResult;
import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.Share;
import org.moire.ultrasonic.domain.UserInfo; import org.moire.ultrasonic.domain.UserInfo;
import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.CancellableTask;
import org.moire.ultrasonic.util.ProgressListener;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
@ -49,61 +48,61 @@ import kotlin.Pair;
public interface MusicService public interface MusicService
{ {
void ping(Context context, ProgressListener progressListener) throws Exception; void ping(Context context) throws Exception;
boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception; boolean isLicenseValid(Context context) throws Exception;
List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception; List<Genre> getGenres(boolean refresh, Context context) throws Exception;
void star(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception; void star(String id, String albumId, String artistId, Context context) throws Exception;
void unstar(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception; void unstar(String id, String albumId, String artistId, Context context) throws Exception;
void setRating(String id, int rating, Context context, ProgressListener progressListener) throws Exception; void setRating(String id, int rating, Context context) throws Exception;
List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception; List<MusicFolder> getMusicFolders(boolean refresh, Context context) throws Exception;
Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception; Indexes getIndexes(String musicFolderId, boolean refresh, Context context) throws Exception;
Indexes getArtists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; Indexes getArtists(boolean refresh, Context context) throws Exception;
MusicDirectory getMusicDirectory(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getMusicDirectory(String id, String name, boolean refresh, Context context) throws Exception;
MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getArtist(String id, String name, boolean refresh, Context context) throws Exception;
MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getAlbum(String id, String name, boolean refresh, Context context) throws Exception;
SearchResult search(SearchCriteria criteria, Context context, ProgressListener progressListener) throws Exception; SearchResult search(SearchCriteria criteria, Context context) throws Exception;
MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getPlaylist(String id, String name, Context context) throws Exception;
List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception; List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context) throws Exception;
List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; List<Playlist> getPlaylists(boolean refresh, Context context) throws Exception;
void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception; void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context) throws Exception;
void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception; void deletePlaylist(String id, Context context) throws Exception;
void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception; void updatePlaylist(String id, String name, String comment, boolean pub, Context context) throws Exception;
Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception; Lyrics getLyrics(String artist, String title, Context context) throws Exception;
void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception; void scrobble(String id, boolean submission, Context context) throws Exception;
MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getAlbumList(String type, int size, int offset, Context context) throws Exception;
MusicDirectory getAlbumList2(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getAlbumList2(String type, int size, int offset, Context context) throws Exception;
MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getRandomSongs(int size, Context context) throws Exception;
MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context) throws Exception;
SearchResult getStarred(Context context, ProgressListener progressListener) throws Exception; SearchResult getStarred(Context context) throws Exception;
SearchResult getStarred2(Context context, ProgressListener progressListener) throws Exception; SearchResult getStarred2(Context context) throws Exception;
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception; Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality) throws Exception;
/** /**
* Return response {@link InputStream} and a {@link Boolean} that indicates if this response is * Return response {@link InputStream} and a {@link Boolean} that indicates if this response is
@ -113,41 +112,41 @@ public interface MusicService
@Deprecated String getVideoUrl(Context context, String id, boolean useFlash) throws Exception; @Deprecated String getVideoUrl(Context context, String id, boolean useFlash) throws Exception;
JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception; JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context) throws Exception;
JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception; JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context) throws Exception;
JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus stopJukebox(Context context) throws Exception;
JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus startJukebox(Context context) throws Exception;
JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus getJukeboxStatus(Context context) throws Exception;
JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception; JukeboxStatus setJukeboxGain(float gain, Context context) throws Exception;
List<Share> getShares(boolean refresh, Context context, ProgressListener progressListener) throws Exception; List<Share> getShares(boolean refresh, Context context) throws Exception;
List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception; List<ChatMessage> getChatMessages(Long since, Context context) throws Exception;
void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception; void addChatMessage(String message, Context context) throws Exception;
List<Bookmark> getBookmarks(Context context, ProgressListener progressListener) throws Exception; List<Bookmark> getBookmarks(Context context) throws Exception;
void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception; void deleteBookmark(String id, Context context) throws Exception;
void createBookmark(String id, int position, Context context, ProgressListener progressListener) throws Exception; void createBookmark(String id, int position, Context context) throws Exception;
MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getVideos(boolean refresh, Context context) throws Exception;
UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception; UserInfo getUser(String username, Context context) throws Exception;
List<Share> createShare(List<String> ids, String description, Long expires, Context context, ProgressListener progressListener) throws Exception; List<Share> createShare(List<String> ids, String description, Long expires, Context context) throws Exception;
void deleteShare(String id, Context context, ProgressListener progressListener) throws Exception; void deleteShare(String id, Context context) throws Exception;
void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception; void updateShare(String id, String description, Long expires, Context context) throws Exception;
Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception; Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality) throws Exception;
MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context) throws Exception;
} }

View File

@ -76,12 +76,12 @@ import static org.koin.java.KoinJavaComponent.inject;
public class OfflineMusicService implements MusicService public class OfflineMusicService implements MusicService
{ {
private static final Pattern COMPILE = Pattern.compile(" "); private static final Pattern COMPILE = Pattern.compile(" ");
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class); private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
@Override @Override
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception public Indexes getIndexes(String musicFolderId, boolean refresh, Context context)
{ {
List<Artist> artists = new ArrayList<Artist>(); List<Artist> artists = new ArrayList<>();
File root = FileUtil.getMusicDirectory(context); File root = FileUtil.getMusicDirectory(context);
for (File file : FileUtil.listFiles(root)) for (File file : FileUtil.listFiles(root))
{ {
@ -144,13 +144,13 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context)
{ {
File dir = new File(id); File dir = new File(id);
MusicDirectory result = new MusicDirectory(); MusicDirectory result = new MusicDirectory();
result.setName(dir.getName()); result.setName(dir.getName());
Collection<String> names = new HashSet<String>(); Collection<String> names = new HashSet<>();
for (File file : FileUtil.listMediaFiles(dir)) for (File file : FileUtil.listMediaFiles(dir))
{ {
@ -335,7 +335,7 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) public Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality)
{ {
try try
{ {
@ -349,7 +349,7 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean highQuality)
{ {
try try
{ {
@ -363,11 +363,11 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public SearchResult search(SearchCriteria criteria, Context context, ProgressListener progressListener) public SearchResult search(SearchCriteria criteria, Context context)
{ {
List<Artist> artists = new ArrayList<Artist>(); List<Artist> artists = new ArrayList<>();
List<MusicDirectory.Entry> albums = new ArrayList<MusicDirectory.Entry>(); List<MusicDirectory.Entry> albums = new ArrayList<>();
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(); List<MusicDirectory.Entry> songs = new ArrayList<>();
File root = FileUtil.getMusicDirectory(context); File root = FileUtil.getMusicDirectory(context);
int closeness; int closeness;
@ -507,7 +507,7 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) public List<Playlist> getPlaylists(boolean refresh, Context context)
{ {
List<Playlist> playlists = new ArrayList<Playlist>(); List<Playlist> playlists = new ArrayList<Playlist>();
File root = FileUtil.getPlaylistDirectory(context); File root = FileUtil.getPlaylistDirectory(context);
@ -564,7 +564,7 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getPlaylist(String id, String name, Context context) throws Exception
{ {
Reader reader = null; Reader reader = null;
BufferedReader buffer = null; BufferedReader buffer = null;
@ -606,7 +606,7 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context) throws Exception
{ {
File playlistFile = FileUtil.getPlaylistFile(context, activeServerProvider.getValue().getActiveServer().getName(), name); File playlistFile = FileUtil.getPlaylistFile(context, activeServerProvider.getValue().getActiveServer().getName(), name);
FileWriter fw = new FileWriter(playlistFile); FileWriter fw = new FileWriter(playlistFile);
@ -639,10 +639,10 @@ public class OfflineMusicService implements MusicService
@Override @Override
public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) public MusicDirectory getRandomSongs(int size, Context context)
{ {
File root = FileUtil.getMusicDirectory(context); File root = FileUtil.getMusicDirectory(context);
List<File> children = new LinkedList<File>(); List<File> children = new LinkedList<>();
listFilesRecursively(root, children); listFilesRecursively(root, children);
MusicDirectory result = new MusicDirectory(); MusicDirectory result = new MusicDirectory();
@ -677,138 +677,138 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception public void deletePlaylist(String id, Context context) throws Exception
{ {
throw new OfflineException("Playlists not available in offline mode"); throw new OfflineException("Playlists not available in offline mode");
} }
@Override @Override
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception public void updatePlaylist(String id, String name, String comment, boolean pub, Context context) throws Exception
{ {
throw new OfflineException("Updating playlist not available in offline mode"); throw new OfflineException("Updating playlist not available in offline mode");
} }
@Override @Override
public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception public Lyrics getLyrics(String artist, String title, Context context) throws Exception
{ {
throw new OfflineException("Lyrics not available in offline mode"); throw new OfflineException("Lyrics not available in offline mode");
} }
@Override @Override
public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception public void scrobble(String id, boolean submission, Context context) throws Exception
{ {
throw new OfflineException("Scrobbling not available in offline mode"); throw new OfflineException("Scrobbling not available in offline mode");
} }
@Override @Override
public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getAlbumList(String type, int size, int offset, Context context) throws Exception
{ {
throw new OfflineException("Album lists not available in offline mode"); throw new OfflineException("Album lists not available in offline mode");
} }
@Override @Override
public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus stopJukebox(Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus startJukebox(Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception public JukeboxStatus getJukeboxStatus(Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception public JukeboxStatus setJukeboxGain(float gain, Context context) throws Exception
{ {
throw new OfflineException("Jukebox not available in offline mode"); throw new OfflineException("Jukebox not available in offline mode");
} }
@Override @Override
public SearchResult getStarred(Context context, ProgressListener progressListener) throws Exception public SearchResult getStarred(Context context) throws Exception
{ {
throw new OfflineException("Starred not available in offline mode"); throw new OfflineException("Starred not available in offline mode");
} }
@Override @Override
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context) throws Exception
{ {
throw new OfflineException("Getting Songs By Genre not available in offline mode"); throw new OfflineException("Getting Songs By Genre not available in offline mode");
} }
@Override @Override
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<Genre> getGenres(boolean refresh, Context context) throws Exception
{ {
throw new OfflineException("Getting Genres not available in offline mode"); throw new OfflineException("Getting Genres not available in offline mode");
} }
@Override @Override
public UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception public UserInfo getUser(String username, Context context) throws Exception
{ {
throw new OfflineException("Getting user info not available in offline mode"); throw new OfflineException("Getting user info not available in offline mode");
} }
@Override @Override
public List<Share> createShare(List<String> ids, String description, Long expires, Context context, ProgressListener progressListener) throws Exception public List<Share> createShare(List<String> ids, String description, Long expires, Context context) throws Exception
{ {
throw new OfflineException("Creating shares not available in offline mode"); throw new OfflineException("Creating shares not available in offline mode");
} }
@Override @Override
public List<Share> getShares(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<Share> getShares(boolean refresh, Context context) throws Exception
{ {
throw new OfflineException("Getting shares not available in offline mode"); throw new OfflineException("Getting shares not available in offline mode");
} }
@Override @Override
public void deleteShare(String id, Context context, ProgressListener progressListener) throws Exception public void deleteShare(String id, Context context) throws Exception
{ {
throw new OfflineException("Deleting shares not available in offline mode"); throw new OfflineException("Deleting shares not available in offline mode");
} }
@Override @Override
public void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception public void updateShare(String id, String description, Long expires, Context context) throws Exception
{ {
throw new OfflineException("Updating shares not available in offline mode"); throw new OfflineException("Updating shares not available in offline mode");
} }
@Override @Override
public void star(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception public void star(String id, String albumId, String artistId, Context context) throws Exception
{ {
throw new OfflineException("Star not available in offline mode"); throw new OfflineException("Star not available in offline mode");
} }
@Override @Override
public void unstar(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception public void unstar(String id, String albumId, String artistId, Context context) throws Exception
{ {
throw new OfflineException("UnStar not available in offline mode"); throw new OfflineException("UnStar not available in offline mode");
} }
@Override @Override
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception public List<MusicFolder> getMusicFolders(boolean refresh, Context context) throws Exception
{ {
throw new OfflineException("Music folders not available in offline mode"); throw new OfflineException("Music folders not available in offline mode");
} }
@Override @Override
public MusicDirectory getAlbumList2(String type, int size, int offset, Context context, ProgressListener progressListener) { public MusicDirectory getAlbumList2(String type, int size, int offset, Context context) {
Timber.w("OfflineMusicService.getAlbumList2 was called but it isn't available"); Timber.w("OfflineMusicService.getAlbumList2 was called but it isn't available");
return null; return null;
} }
@ -820,73 +820,73 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) { public List<ChatMessage> getChatMessages(Long since, Context context) {
Timber.w("OfflineMusicService.getChatMessages was called but it isn't available"); Timber.w("OfflineMusicService.getChatMessages was called but it isn't available");
return null; return null;
} }
@Override @Override
public void addChatMessage(String message, Context context, ProgressListener progressListener) { public void addChatMessage(String message, Context context) {
Timber.w("OfflineMusicService.addChatMessage was called but it isn't available"); Timber.w("OfflineMusicService.addChatMessage was called but it isn't available");
} }
@Override @Override
public List<Bookmark> getBookmarks(Context context, ProgressListener progressListener) { public List<Bookmark> getBookmarks(Context context) {
Timber.w("OfflineMusicService.getBookmarks was called but it isn't available"); Timber.w("OfflineMusicService.getBookmarks was called but it isn't available");
return null; return null;
} }
@Override @Override
public void deleteBookmark(String id, Context context, ProgressListener progressListener) { public void deleteBookmark(String id, Context context) {
Timber.w("OfflineMusicService.deleteBookmark was called but it isn't available"); Timber.w("OfflineMusicService.deleteBookmark was called but it isn't available");
} }
@Override @Override
public void createBookmark(String id, int position, Context context, ProgressListener progressListener) { public void createBookmark(String id, int position, Context context) {
Timber.w("OfflineMusicService.createBookmark was called but it isn't available"); Timber.w("OfflineMusicService.createBookmark was called but it isn't available");
} }
@Override @Override
public MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) { public MusicDirectory getVideos(boolean refresh, Context context) {
Timber.w("OfflineMusicService.getVideos was called but it isn't available"); Timber.w("OfflineMusicService.getVideos was called but it isn't available");
return null; return null;
} }
@Override @Override
public SearchResult getStarred2(Context context, ProgressListener progressListener) { public SearchResult getStarred2(Context context) {
Timber.w("OfflineMusicService.getStarred2 was called but it isn't available"); Timber.w("OfflineMusicService.getStarred2 was called but it isn't available");
return null; return null;
} }
@Override @Override
public void ping(Context context, ProgressListener progressListener) { public void ping(Context context) {
} }
@Override @Override
public boolean isLicenseValid(Context context, ProgressListener progressListener) { public boolean isLicenseValid(Context context) {
return true; return true;
} }
@Override @Override
public Indexes getArtists(boolean refresh, Context context, ProgressListener progressListener) { public Indexes getArtists(boolean refresh, Context context) {
Timber.w("OfflineMusicService.getArtists was called but it isn't available"); Timber.w("OfflineMusicService.getArtists was called but it isn't available");
return null; return null;
} }
@Override @Override
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) { public MusicDirectory getArtist(String id, String name, boolean refresh, Context context) {
Timber.w("OfflineMusicService.getArtist was called but it isn't available"); Timber.w("OfflineMusicService.getArtist was called but it isn't available");
return null; return null;
} }
@Override @Override
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) { public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context) {
Timber.w("OfflineMusicService.getAlbum was called but it isn't available"); Timber.w("OfflineMusicService.getAlbum was called but it isn't available");
return null; return null;
} }
@Override @Override
public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) { public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context) {
Timber.w("OfflineMusicService.getPodcastEpisodes was called but it isn't available"); Timber.w("OfflineMusicService.getPodcastEpisodes was called but it isn't available");
return null; return null;
} }
@ -898,12 +898,12 @@ public class OfflineMusicService implements MusicService
} }
@Override @Override
public void setRating(String id, int rating, Context context, ProgressListener progressListener) { public void setRating(String id, int rating, Context context) {
Timber.w("OfflineMusicService.setRating was called but it isn't available"); Timber.w("OfflineMusicService.setRating was called but it isn't available");
} }
@Override @Override
public List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) { public List<PodcastsChannel> getPodcastsChannels(boolean refresh, Context context) {
Timber.w("OfflineMusicService.getPodcastsChannels was called but it isn't available"); Timber.w("OfflineMusicService.getPodcastsChannels was called but it isn't available");
return null; return null;
} }

View File

@ -4,7 +4,6 @@ import android.content.Context;
import timber.log.Timber; import timber.log.Timber;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.util.Util;
/** /**
* Scrobbles played songs to Last.fm. * Scrobbles played songs to Last.fm.
@ -19,32 +18,18 @@ public class Scrobbler
public void scrobble(final Context context, final DownloadFile song, final boolean submission) public void scrobble(final Context context, final DownloadFile song, final boolean submission)
{ {
if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled(context)) if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled(context)) return;
{
return;
}
final String id = song.getSong().getId(); final String id = song.getSong().getId();
if (id == null) return;
// Avoid duplicate registrations. // Avoid duplicate registrations.
if (submission && id.equals(lastSubmission)) if (submission && id.equals(lastSubmission)) return;
{
return;
}
if (!submission && id.equals(lastNowPlaying)) if (!submission && id.equals(lastNowPlaying)) return;
{
return;
}
if (submission) if (submission) lastSubmission = id;
{ else lastNowPlaying = id;
lastSubmission = id;
}
else
{
lastNowPlaying = id;
}
new Thread(String.format("Scrobble %s", song)) new Thread(String.format("Scrobble %s", song))
{ {
@ -54,7 +39,7 @@ public class Scrobbler
MusicService service = MusicServiceFactory.getMusicService(context); MusicService service = MusicServiceFactory.getMusicService(context);
try try
{ {
service.scrobble(id, submission, context, null); service.scrobble(id, submission, context);
Timber.i("Scrobbled '%s' for %s", submission ? "submission" : "now playing", song); Timber.i("Scrobbled '%s' for %s", submission ? "submission" : "now playing", song);
} }
catch (Exception x) catch (Exception x)

View File

@ -8,14 +8,14 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
* @author Sindre Mehus * @author Sindre Mehus
* @version $Id$ * @version $Id$
*/ */
public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T> public abstract class FragmentBackgroundTask<T> extends BackgroundTask<T>
{ {
private final boolean changeProgress; private final boolean changeProgress;
private final SwipeRefreshLayout swipe; private final SwipeRefreshLayout swipe;
private final CancellationToken cancel; private final CancellationToken cancel;
public TabActivityBackgroundTask(Activity activity, boolean changeProgress, public FragmentBackgroundTask(Activity activity, boolean changeProgress,
SwipeRefreshLayout swipe, CancellationToken cancel) SwipeRefreshLayout swipe, CancellationToken cancel)
{ {
super(activity); super(activity);
this.changeProgress = changeProgress; this.changeProgress = changeProgress;

View File

@ -19,7 +19,6 @@
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -30,6 +29,9 @@ import timber.log.Timber;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.content.res.ResourcesCompat;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicService;
@ -38,8 +40,6 @@ import org.moire.ultrasonic.service.MusicServiceFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -58,9 +58,9 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
private final int imageSizeLarge; private final int imageSizeLarge;
private Bitmap largeUnknownImage; private Bitmap largeUnknownImage;
private Bitmap unknownAvatarImage; private Bitmap unknownAvatarImage;
private Context context; private final Context context;
private Collection<Thread> threads; private Collection<Thread> threads;
private AtomicBoolean running = new AtomicBoolean(); private final AtomicBoolean running = new AtomicBoolean();
private int concurrency; private int concurrency;
public LegacyImageLoader( public LegacyImageLoader(
@ -71,8 +71,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
this.concurrency = concurrency; this.concurrency = concurrency;
queue = new LinkedBlockingQueue<>(1000); queue = new LinkedBlockingQueue<>(1000);
Resources resources = context.getResources(); Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.unknown_album, null);
Drawable drawable = resources.getDrawable(R.drawable.unknown_album);
// Determine the density-dependent image sizes. // Determine the density-dependent image sizes.
if (drawable != null) { if (drawable != null) {
@ -120,7 +119,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
} }
private void createLargeUnknownImage(Context context) { private void createLargeUnknownImage(Context context) {
Drawable drawable = context.getResources().getDrawable(R.drawable.unknown_album); Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.unknown_album, null);
Timber.i("createLargeUnknownImage"); Timber.i("createLargeUnknownImage");
if (drawable != null) { if (drawable != null) {
@ -129,8 +128,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
} }
private void createUnknownAvatarImage(Context context) { private void createUnknownAvatarImage(Context context) {
Resources res = context.getResources(); Drawable contact = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_contact_picture, null);
Drawable contact = res.getDrawable(R.drawable.ic_contact_picture);
unknownAvatarImage = Util.createBitmapFromDrawable(contact); unknownAvatarImage = Util.createBitmapFromDrawable(contact);
} }
@ -424,8 +422,8 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
MusicService musicService = MusicServiceFactory.getMusicService(view.getContext()); MusicService musicService = MusicServiceFactory.getMusicService(view.getContext());
final boolean isAvatar = this.username != null && this.entry == null; final boolean isAvatar = this.username != null && this.entry == null;
final Bitmap bitmap = this.entry != null ? final Bitmap bitmap = this.entry != null ?
musicService.getCoverArt(view.getContext(), entry, size, saveToFile, highQuality, null) : musicService.getCoverArt(view.getContext(), entry, size, saveToFile, highQuality) :
musicService.getAvatar(view.getContext(), username, size, saveToFile, highQuality, null); musicService.getAvatar(view.getContext(), username, size, saveToFile, highQuality);
if (bitmap == null) { if (bitmap == null) {
Timber.d("Found empty album art."); Timber.d("Found empty album art.");

View File

@ -32,16 +32,25 @@ import static androidx.core.content.PermissionChecker.PERMISSION_DENIED;
*/ */
public class PermissionUtil { public class PermissionUtil {
private final Context context; private Context activityContext;
private Context applicationContext;
public PermissionUtil(Context context) { public PermissionUtil(Context context) {
this.context = context; applicationContext = context;
} }
public interface PermissionRequestFinishedCallback { public interface PermissionRequestFinishedCallback {
void onPermissionRequestFinished(boolean hasPermission); void onPermissionRequestFinished(boolean hasPermission);
} }
public void ForegroundApplicationStarted(Context context) {
this.activityContext = context;
}
public void ForegroundApplicationStopped() {
activityContext = null;
}
/** /**
* This function can be used to handle file access permission failures. * This function can be used to handle file access permission failures.
* *
@ -51,26 +60,30 @@ public class PermissionUtil {
* @param callback callback function to execute after the permission request is finished * @param callback callback function to execute after the permission request is finished
*/ */
public void handlePermissionFailed(final PermissionRequestFinishedCallback callback) { public void handlePermissionFailed(final PermissionRequestFinishedCallback callback) {
// TODO: Test with ApplicationContext String currentCachePath = Util.getPreferences(applicationContext).getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(applicationContext).getPath());
String currentCachePath = Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(context).getPath()); String defaultCachePath = FileUtil.getDefaultMusicDirectory(applicationContext).getPath();
String defaultCachePath = FileUtil.getDefaultMusicDirectory(context).getPath();
// Ultrasonic can do nothing about this error when the Music Directory is already set to the default. // Ultrasonic can do nothing about this error when the Music Directory is already set to the default.
if (currentCachePath.compareTo(defaultCachePath) == 0) return; if (currentCachePath.compareTo(defaultCachePath) == 0) return;
if ((PermissionChecker.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_DENIED) || if ((PermissionChecker.checkSelfPermission(applicationContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_DENIED) ||
(PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_DENIED)) { (PermissionChecker.checkSelfPermission(applicationContext, Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_DENIED)) {
// While we request permission, the Music Directory is temporarily reset to its default location // While we request permission, the Music Directory is temporarily reset to its default location
setCacheLocation(context, FileUtil.getDefaultMusicDirectory(context).getPath()); setCacheLocation(applicationContext, FileUtil.getDefaultMusicDirectory(applicationContext).getPath());
requestFailedPermission(context, currentCachePath, callback); // If the application is not running, we can't notify the user
if (activityContext == null) return;
requestFailedPermission(activityContext, currentCachePath, callback);
} else { } else {
setCacheLocation(context, FileUtil.getDefaultMusicDirectory(context).getPath()); setCacheLocation(applicationContext, FileUtil.getDefaultMusicDirectory(applicationContext).getPath());
new Handler(Looper.getMainLooper()).post(new Runnable() { // If the application is not running, we can't notify the user
@Override if (activityContext != null) {
public void run() { new Handler(Looper.getMainLooper()).post(new Runnable() {
showWarning(context, context.getString(R.string.permissions_message_box_title), context.getString(R.string.permissions_access_error), null); @Override
} public void run() {
}); showWarning(activityContext, activityContext.getString(R.string.permissions_message_box_title), activityContext.getString(R.string.permissions_access_error), null);
}
});
}
callback.onPermissionRequestFinished(false); callback.onPermissionRequestFinished(false);
} }
} }

View File

@ -41,7 +41,7 @@ public class ShufflePlayBuffer
private static final int CAPACITY = 50; private static final int CAPACITY = 50;
private static final int REFILL_THRESHOLD = 40; private static final int REFILL_THRESHOLD = 40;
private final List<MusicDirectory.Entry> buffer = new ArrayList<MusicDirectory.Entry>(); private final List<MusicDirectory.Entry> buffer = new ArrayList<>();
private final Context context; private final Context context;
private ScheduledExecutorService executorService; private ScheduledExecutorService executorService;
private int currentServer; private int currentServer;
@ -78,7 +78,7 @@ public class ShufflePlayBuffer
{ {
clearBufferIfNecessary(); clearBufferIfNecessary();
List<MusicDirectory.Entry> result = new ArrayList<MusicDirectory.Entry>(size); List<MusicDirectory.Entry> result = new ArrayList<>(size);
synchronized (buffer) synchronized (buffer)
{ {
while (!buffer.isEmpty() && result.size() < size) while (!buffer.isEmpty() && result.size() < size)
@ -106,7 +106,7 @@ public class ShufflePlayBuffer
{ {
MusicService service = MusicServiceFactory.getMusicService(context); MusicService service = MusicServiceFactory.getMusicService(context);
int n = CAPACITY - buffer.size(); int n = CAPACITY - buffer.size();
MusicDirectory songs = service.getRandomSongs(n, context, null); MusicDirectory songs = service.getRandomSongs(n, context);
synchronized (buffer) synchronized (buffer)
{ {

View File

@ -23,8 +23,6 @@ import android.graphics.drawable.Drawable;
import timber.log.Timber; import timber.log.Timber;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
@ -44,10 +42,10 @@ public class AlbumView extends UpdateView
private static Drawable starHollowDrawable; private static Drawable starHollowDrawable;
private static String theme; private static String theme;
private Context context; private final Context context;
private MusicDirectory.Entry entry; private MusicDirectory.Entry entry;
private EntryAdapter.AlbumViewHolder viewHolder; private EntryAdapter.AlbumViewHolder viewHolder;
private ImageLoader imageLoader; private final ImageLoader imageLoader;
private boolean maximized = false; private boolean maximized = false;
public AlbumView(Context context, ImageLoader imageLoader) public AlbumView(Context context, ImageLoader imageLoader)
@ -75,10 +73,10 @@ public class AlbumView extends UpdateView
{ {
LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true); LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
viewHolder = new EntryAdapter.AlbumViewHolder(); viewHolder = new EntryAdapter.AlbumViewHolder();
viewHolder.title = (TextView) findViewById(R.id.album_title); viewHolder.title = findViewById(R.id.album_title);
viewHolder.artist = (TextView) findViewById(R.id.album_artist); viewHolder.artist = findViewById(R.id.album_artist);
viewHolder.cover_art = (ImageView) findViewById(R.id.album_coverart); viewHolder.cover_art = findViewById(R.id.album_coverart);
viewHolder.star = (ImageView) findViewById(R.id.album_star); viewHolder.star = findViewById(R.id.album_star);
setTag(viewHolder); setTag(viewHolder);
} }
@ -164,11 +162,11 @@ public class AlbumView extends UpdateView
{ {
if (!isStarred) if (!isStarred)
{ {
musicService.star(!useId3 ? id : null, useId3 ? id : null, null, getContext(), null); musicService.star(!useId3 ? id : null, useId3 ? id : null, null, getContext());
} }
else else
{ {
musicService.unstar(!useId3 ? id : null, useId3 ? id : null, null, getContext(), null); musicService.unstar(!useId3 ? id : null, useId3 ? id : null, null, getContext());
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -15,6 +15,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.FragmentContainerView import androidx.fragment.app.FragmentContainerView
import androidx.navigation.NavController import androidx.navigation.NavController
@ -41,13 +42,13 @@ import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.NowPlayingEventDistributor import org.moire.ultrasonic.util.NowPlayingEventDistributor
import org.moire.ultrasonic.util.NowPlayingEventListener import org.moire.ultrasonic.util.NowPlayingEventListener
import org.moire.ultrasonic.util.PermissionUtil
import org.moire.ultrasonic.util.SubsonicUncaughtExceptionHandler import org.moire.ultrasonic.util.SubsonicUncaughtExceptionHandler
import org.moire.ultrasonic.util.ThemeChangedEventDistributor import org.moire.ultrasonic.util.ThemeChangedEventDistributor
import org.moire.ultrasonic.util.ThemeChangedEventListener import org.moire.ultrasonic.util.ThemeChangedEventListener
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
/** /**
* The main Activity of Ultrasonic which loads all other screens as Fragments * The main Activity of Ultrasonic which loads all other screens as Fragments
*/ */
@ -58,10 +59,12 @@ class NavigationActivity : AppCompatActivity() {
var podcastsMenuItem: MenuItem? = null var podcastsMenuItem: MenuItem? = null
var nowPlayingView: FragmentContainerView? = null var nowPlayingView: FragmentContainerView? = null
var nowPlayingHidden = false var nowPlayingHidden = false
var navigationView: NavigationView? = null
var drawerLayout: DrawerLayout? = null
private lateinit var appBarConfiguration : AppBarConfiguration private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var nowPlayingEventListener : NowPlayingEventListener private lateinit var nowPlayingEventListener: NowPlayingEventListener
private lateinit var themeChangedEventListener : ThemeChangedEventListener private lateinit var themeChangedEventListener: ThemeChangedEventListener
private val serverSettingsModel: ServerSettingsModel by viewModel() private val serverSettingsModel: ServerSettingsModel by viewModel()
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject() private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
@ -69,12 +72,14 @@ class NavigationActivity : AppCompatActivity() {
private val imageLoaderProvider: ImageLoaderProvider by inject() private val imageLoaderProvider: ImageLoaderProvider by inject()
private val nowPlayingEventDistributor: NowPlayingEventDistributor by inject() private val nowPlayingEventDistributor: NowPlayingEventDistributor by inject()
private val themeChangedEventDistributor: ThemeChangedEventDistributor by inject() private val themeChangedEventDistributor: ThemeChangedEventDistributor by inject()
private val permissionUtil: PermissionUtil by inject()
private var infoDialogDisplayed = false private var infoDialogDisplayed = false
private var currentFragmentId: Int = 0 private var currentFragmentId: Int = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setUncaughtExceptionHandler() setUncaughtExceptionHandler()
permissionUtil.ForegroundApplicationStarted(this)
Util.applyTheme(this) Util.applyTheme(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -82,6 +87,8 @@ class NavigationActivity : AppCompatActivity() {
volumeControlStream = AudioManager.STREAM_MUSIC volumeControlStream = AudioManager.STREAM_MUSIC
setContentView(R.layout.navigation_activity) setContentView(R.layout.navigation_activity)
nowPlayingView = findViewById(R.id.now_playing_fragment) nowPlayingView = findViewById(R.id.now_playing_fragment)
navigationView = findViewById(R.id.nav_view)
drawerLayout = findViewById(R.id.drawer_layout)
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
@ -91,13 +98,22 @@ class NavigationActivity : AppCompatActivity() {
val navController = host.navController val navController = host.navController
val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration( appBarConfiguration = AppBarConfiguration(
setOf(R.id.mainFragment, R.id.selectArtistFragment, R.id.searchFragment, setOf(
R.id.playlistsFragment, R.id.sharesFragment, R.id.bookmarksFragment, R.id.mainFragment,
R.id.chatFragment, R.id.podcastFragment, R.id.settingsFragment, R.id.selectArtistFragment,
R.id.aboutFragment, R.id.playerFragment), R.id.searchFragment,
drawerLayout) R.id.playlistsFragment,
R.id.sharesFragment,
R.id.bookmarksFragment,
R.id.chatFragment,
R.id.podcastFragment,
R.id.settingsFragment,
R.id.aboutFragment,
R.id.playerFragment
),
drawerLayout
)
setupActionBar(navController, appBarConfiguration) setupActionBar(navController, appBarConfiguration)
@ -135,8 +151,8 @@ class NavigationActivity : AppCompatActivity() {
nowPlayingEventListener = object : NowPlayingEventListener { nowPlayingEventListener = object : NowPlayingEventListener {
override fun onDismissNowPlaying() { override fun onDismissNowPlaying() {
nowPlayingHidden = true; nowPlayingHidden = true
hideNowPlaying(); hideNowPlaying()
} }
override fun onHideNowPlaying() { override fun onHideNowPlaying() {
@ -174,6 +190,7 @@ class NavigationActivity : AppCompatActivity() {
nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener) nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener)
themeChangedEventDistributor.unsubscribe(themeChangedEventListener) themeChangedEventDistributor.unsubscribe(themeChangedEventListener)
imageLoaderProvider.clearImageLoader() imageLoaderProvider.clearImageLoader()
permissionUtil.ForegroundApplicationStopped()
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@ -189,12 +206,11 @@ class NavigationActivity : AppCompatActivity() {
} }
private fun setupNavigationMenu(navController: NavController) { private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view) navigationView?.setupWithNavController(navController)
sideNavView?.setupWithNavController(navController)
// The exit menu is handled here manually // The exit menu is handled here manually
val exitItem: MenuItem = sideNavView.menu.findItem(R.id.menu_exit) val exitItem: MenuItem? = navigationView?.menu?.findItem(R.id.menu_exit) ?: null
exitItem.setOnMenuItemClickListener { item -> exitItem?.setOnMenuItemClickListener { item ->
if (item.itemId == R.id.menu_exit) { if (item.itemId == R.id.menu_exit) {
setResult(Constants.RESULT_CLOSE_ALL) setResult(Constants.RESULT_CLOSE_ALL)
mediaPlayerController.stopJukeboxService() mediaPlayerController.stopJukeboxService()
@ -205,19 +221,26 @@ class NavigationActivity : AppCompatActivity() {
true true
} }
chatMenuItem = sideNavView.menu.findItem(R.id.chatFragment) chatMenuItem = navigationView?.menu?.findItem(R.id.chatFragment)
bookmarksMenuItem = sideNavView.menu.findItem(R.id.bookmarksFragment) bookmarksMenuItem = navigationView?.menu?.findItem(R.id.bookmarksFragment)
sharesMenuItem = sideNavView.menu.findItem(R.id.sharesFragment) sharesMenuItem = navigationView?.menu?.findItem(R.id.sharesFragment)
podcastsMenuItem = sideNavView.menu.findItem(R.id.podcastFragment) podcastsMenuItem = navigationView?.menu?.findItem(R.id.podcastFragment)
} }
private fun setupActionBar(navController: NavController, appBarConfig: AppBarConfiguration) { private fun setupActionBar(navController: NavController, appBarConfig: AppBarConfiguration) {
setupActionBarWithNavController(navController, appBarConfig) setupActionBarWithNavController(navController, appBarConfig)
} }
override fun onBackPressed() {
if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) {
this.drawerLayout?.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
val retValue = super.onCreateOptionsMenu(menu) val retValue = super.onCreateOptionsMenu(menu)
val navigationView = findViewById<NavigationView>(R.id.nav_view)
if (navigationView == null) { if (navigationView == null) {
menuInflater.inflate(R.menu.navigation, menu) menuInflater.inflate(R.menu.navigation, menu)
return true return true
@ -226,22 +249,21 @@ class NavigationActivity : AppCompatActivity() {
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.nav_host_fragment)) return item.onNavDestinationSelected(findNavController(R.id.nav_host_fragment)) ||
|| super.onOptionsItemSelected(item) super.onOptionsItemSelected(item)
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration) return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
} }
// TODO: Test if this works with external Intents // TODO Test if this works with external Intents
// android.intent.action.SEARCH and android.media.action.MEDIA_PLAY_FROM_SEARCH calls here // android.intent.action.SEARCH and android.media.action.MEDIA_PLAY_FROM_SEARCH calls here
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
if (intent == null) return; if (intent == null) return
if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHOW_PLAYER, false)) if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHOW_PLAYER, false)) {
{
findNavController(R.id.nav_host_fragment).navigate(R.id.playerFragment) findNavController(R.id.nav_host_fragment).navigate(R.id.playerFragment)
return return
} }
@ -250,7 +272,10 @@ class NavigationActivity : AppCompatActivity() {
if (query != null) { if (query != null) {
val autoPlay = intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH val autoPlay = intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
val suggestions = SearchRecentSuggestions(this, SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE) val suggestions = SearchRecentSuggestions(
this,
SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE
)
suggestions.saveRecentQuery(query, null) suggestions.saveRecentQuery(query, null)
val bundle = Bundle() val bundle = Bundle()
@ -265,7 +290,10 @@ class NavigationActivity : AppCompatActivity() {
val preferences = Util.getPreferences(this) val preferences = Util.getPreferences(this)
if (!preferences.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) { if (!preferences.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) {
val editor = preferences.edit() val editor = preferences.edit()
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(this).path) editor.putString(
Constants.PREFERENCES_KEY_CACHE_LOCATION,
FileUtil.getDefaultMusicDirectory(this).path
)
editor.apply() editor.apply()
} }
} }
@ -284,7 +312,7 @@ class NavigationActivity : AppCompatActivity() {
.setIcon(android.R.drawable.ic_dialog_info) .setIcon(android.R.drawable.ic_dialog_info)
.setTitle(R.string.main_welcome_title) .setTitle(R.string.main_welcome_title)
.setMessage(R.string.main_welcome_text) .setMessage(R.string.main_welcome_text)
.setPositiveButton(R.string.common_ok) { dialog, i -> .setPositiveButton(R.string.common_ok) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
findNavController(R.id.nav_host_fragment).navigate(R.id.settingsFragment) findNavController(R.id.nav_host_fragment).navigate(R.id.settingsFragment)
}.show() }.show()
@ -307,7 +335,7 @@ class NavigationActivity : AppCompatActivity() {
// The logic for nowPlayingHidden is that the user can dismiss NowPlaying with a gesture, // The logic for nowPlayingHidden is that the user can dismiss NowPlaying with a gesture,
// and when the MediaPlayerService requests that it should be shown, it returns // and when the MediaPlayerService requests that it should be shown, it returns
nowPlayingHidden = false; nowPlayingHidden = false
// Do not show for Player fragment // Do not show for Player fragment
if (currentFragmentId == R.id.playerFragment) { if (currentFragmentId == R.id.playerFragment) {
hideNowPlaying() hideNowPlaying()
@ -319,7 +347,6 @@ class NavigationActivity : AppCompatActivity() {
if (playerState == PlayerState.PAUSED || playerState == PlayerState.STARTED) { if (playerState == PlayerState.PAUSED || playerState == PlayerState.STARTED) {
val file: DownloadFile? = mediaPlayerController.currentPlaying val file: DownloadFile? = mediaPlayerController.currentPlaying
if (file != null) { if (file != null) {
val song = file.song
nowPlayingView?.visibility = View.VISIBLE nowPlayingView?.visibility = View.VISIBLE
} }
} else { } else {

View File

@ -5,9 +5,9 @@ import org.koin.android.ext.koin.androidContext
import org.koin.android.viewmodel.dsl.viewModel import org.koin.android.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
import org.moire.ultrasonic.fragment.ServerSettingsModel
import org.moire.ultrasonic.data.AppDatabase import org.moire.ultrasonic.data.AppDatabase
import org.moire.ultrasonic.data.MIGRATION_1_2 import org.moire.ultrasonic.data.MIGRATION_1_2
import org.moire.ultrasonic.fragment.ServerSettingsModel
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
const val SP_NAME = "Default_SP" const val SP_NAME = "Default_SP"

View File

@ -1,10 +1,7 @@
package org.moire.ultrasonic.di package org.moire.ultrasonic.di
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.dsl.bind
import org.koin.dsl.module import org.koin.dsl.module
import org.moire.ultrasonic.cache.AndroidDirectories
import org.moire.ultrasonic.cache.Directories
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.subsonic.ImageLoaderProvider import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.util.NowPlayingEventDistributor import org.moire.ultrasonic.util.NowPlayingEventDistributor

View File

@ -8,12 +8,12 @@ import org.koin.android.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.BuildConfig
import org.moire.ultrasonic.fragment.ArtistListModel
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
import org.moire.ultrasonic.cache.PermanentFileStorage import org.moire.ultrasonic.cache.PermanentFileStorage
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.fragment.ArtistListModel
import org.moire.ultrasonic.log.TimberOkHttpLogger import org.moire.ultrasonic.log.TimberOkHttpLogger
import org.moire.ultrasonic.service.ApiCallResponseChecker import org.moire.ultrasonic.service.ApiCallResponseChecker
import org.moire.ultrasonic.service.CachedMusicService import org.moire.ultrasonic.service.CachedMusicService

View File

@ -85,15 +85,15 @@ class ArtistListModel(
try { try {
if (!isOffline && !useId3Tags) { if (!isOffline && !useId3Tags) {
musicFolders.postValue( musicFolders.postValue(
musicService.getMusicFolders(refresh, context, null) musicService.getMusicFolders(refresh, context)
) )
} }
val musicFolderId = activeServerProvider.getActiveServer().musicFolderId val musicFolderId = activeServerProvider.getActiveServer().musicFolderId
val result = if (!isOffline && useId3Tags) val result = if (!isOffline && useId3Tags)
musicService.getArtists(refresh, context, null) musicService.getArtists(refresh, context)
else musicService.getIndexes(musicFolderId, refresh, context, null) else musicService.getIndexes(musicFolderId, refresh, context)
val retrievedArtists: MutableList<Artist> = val retrievedArtists: MutableList<Artist> =
ArrayList(result.shortcuts.size + result.artists.size) ArrayList(result.shortcuts.size + result.artists.size)

View File

@ -11,6 +11,8 @@ import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.switchmaterial.SwitchMaterial import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import java.net.MalformedURLException
import java.net.URL
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.android.viewmodel.ext.android.viewModel import org.koin.android.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.BuildConfig
@ -27,10 +29,8 @@ import org.moire.ultrasonic.util.ErrorDialog
import org.moire.ultrasonic.util.ModalBackgroundTask import org.moire.ultrasonic.util.ModalBackgroundTask
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
import java.net.MalformedURLException
import java.net.URL
class EditServerFragment: Fragment() { class EditServerFragment : Fragment() {
companion object { companion object {
const val EDIT_SERVER_INTENT_INDEX = "index" const val EDIT_SERVER_INTENT_INDEX = "index"
} }
@ -57,8 +57,11 @@ class EditServerFragment: Fragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View? { inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.server_edit, container, false) return inflater.inflate(R.layout.server_edit, container, false)
} }
@ -370,4 +373,4 @@ class EditServerFragment: Fragment() {
findNavController().navigateUp() findNavController().navigateUp()
} }
} }
} }

View File

@ -29,4 +29,4 @@ class FragmentTitle {
return (fragment.activity as AppCompatActivity).supportActionBar?.subtitle return (fragment.activity as AppCompatActivity).supportActionBar?.subtitle
} }
} }
} }

View File

@ -43,8 +43,11 @@ class SelectArtistFragment : Fragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View? { inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.select_artist, container, false) return inflater.inflate(R.layout.select_artist, container, false)
} }
@ -54,14 +57,18 @@ class SelectArtistFragment : Fragment() {
artistListModel.refresh(refreshArtistListView!!) artistListModel.refresh(refreshArtistListView!!)
} }
val shouldShowHeader = (!ActiveServerProvider.isOffline(this.context) && !Util.getShouldUseId3Tags(this.context)) val shouldShowHeader = (
!ActiveServerProvider.isOffline(this.context) &&
!Util.getShouldUseId3Tags(this.context)
)
val title = arguments?.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE) val title = arguments?.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE)
if (title == null) { if (title == null) {
setTitle( setTitle(
this, this,
if (ActiveServerProvider.isOffline(this.context)) R.string.music_library_label_offline if (ActiveServerProvider.isOffline(this.context))
R.string.music_library_label_offline
else R.string.music_library_label else R.string.music_library_label
) )
} else { } else {
@ -156,17 +163,83 @@ class SelectArtistFragment : Fragment() {
private fun onArtistMenuItemSelected(menuItem: MenuItem, artist: Artist): Boolean { private fun onArtistMenuItemSelected(menuItem: MenuItem, artist: Artist): Boolean {
when (menuItem.itemId) { when (menuItem.itemId) {
R.id.artist_menu_play_now -> R.id.artist_menu_play_now ->
downloadHandler.downloadRecursively(this, artist.id, false, false, true, false, false, false, false, true) downloadHandler.downloadRecursively(
this,
artist.id,
false,
false,
true,
false,
false,
false,
false,
true
)
R.id.artist_menu_play_next -> R.id.artist_menu_play_next ->
downloadHandler.downloadRecursively(this, artist.id, false, false, true, true, false, true, false, true) downloadHandler.downloadRecursively(
this,
artist.id,
false,
false,
true,
true,
false,
true,
false,
true
)
R.id.artist_menu_play_last -> R.id.artist_menu_play_last ->
downloadHandler.downloadRecursively(this, artist.id, false, true, false, false, false, false, false, true) downloadHandler.downloadRecursively(
this,
artist.id,
false,
true,
false,
false,
false,
false,
false,
true
)
R.id.artist_menu_pin -> R.id.artist_menu_pin ->
downloadHandler.downloadRecursively(this, artist.id, true, true, false, false, false, false, false, true) downloadHandler.downloadRecursively(
this,
artist.id,
true,
true,
false,
false,
false,
false,
false,
true
)
R.id.artist_menu_unpin -> R.id.artist_menu_unpin ->
downloadHandler.downloadRecursively(this, artist.id, false, false, false, false, false, false, true, true) downloadHandler.downloadRecursively(
this,
artist.id,
false,
false,
false,
false,
false,
false,
true,
true
)
R.id.artist_menu_download -> R.id.artist_menu_download ->
downloadHandler.downloadRecursively(this, artist.id, false, false, false, false, true, false, false, true) downloadHandler.downloadRecursively(
this,
artist.id,
false,
false,
false,
false,
true,
false,
false,
true
)
} }
return true return true
} }
@ -189,4 +262,4 @@ class SelectArtistFragment : Fragment() {
companion object { companion object {
private const val MENU_GROUP_MUSIC_FOLDER = 10 private const val MENU_GROUP_MUSIC_FOLDER = 10
} }
} }

View File

@ -23,7 +23,7 @@ import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
class ServerSelectorFragment: Fragment() { class ServerSelectorFragment : Fragment() {
companion object { companion object {
const val SERVER_SELECTOR_MANAGE_MODE = "manageMode" const val SERVER_SELECTOR_MANAGE_MODE = "manageMode"
} }
@ -40,8 +40,11 @@ class ServerSelectorFragment: Fragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View? { inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.server_selector, container, false) return inflater.inflate(R.layout.server_selector, container, false)
} }
@ -155,4 +158,4 @@ class ServerSelectorFragment: Fragment() {
bundle.putInt(EDIT_SERVER_INTENT_INDEX, index) bundle.putInt(EDIT_SERVER_INTENT_INDEX, index)
findNavController().navigate(R.id.serverSelectorToEditServer, bundle) findNavController().navigate(R.id.serverSelectorToEditServer, bundle)
} }
} }

View File

@ -21,7 +21,6 @@ package org.moire.ultrasonic.service
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.text.TextUtils import android.text.TextUtils
import androidx.annotation.StringRes
import java.io.BufferedWriter import java.io.BufferedWriter
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -31,7 +30,6 @@ import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.Companion.fromName import org.moire.ultrasonic.api.subsonic.models.AlbumListType.Companion.fromName
@ -63,7 +61,6 @@ import org.moire.ultrasonic.domain.toDomainEntityList
import org.moire.ultrasonic.domain.toMusicDirectoryDomainEntity import org.moire.ultrasonic.domain.toMusicDirectoryDomainEntity
import org.moire.ultrasonic.util.CancellableTask import org.moire.ultrasonic.util.CancellableTask
import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.ProgressListener
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
@ -78,16 +75,12 @@ open class RESTMusicService(
) : MusicService { ) : MusicService {
@Throws(Exception::class) @Throws(Exception::class)
override fun ping(context: Context, progressListener: ProgressListener?) { override fun ping(context: Context) {
updateProgressListener(progressListener, R.string.service_connecting)
responseChecker.callWithResponseCheck { api -> api.ping().execute() } responseChecker.callWithResponseCheck { api -> api.ping().execute() }
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun isLicenseValid(context: Context, progressListener: ProgressListener?): Boolean { override fun isLicenseValid(context: Context): Boolean {
updateProgressListener(progressListener, R.string.service_connecting)
val response = responseChecker.callWithResponseCheck { api -> api.getLicense().execute() } val response = responseChecker.callWithResponseCheck { api -> api.getLicense().execute() }
return response.body()!!.license.valid return response.body()!!.license.valid
@ -96,8 +89,7 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getMusicFolders( override fun getMusicFolders(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): List<MusicFolder> { ): List<MusicFolder> {
val cachedMusicFolders = fileStorage.load( val cachedMusicFolders = fileStorage.load(
MUSIC_FOLDER_STORAGE_NAME, getMusicFolderListSerializer() MUSIC_FOLDER_STORAGE_NAME, getMusicFolderListSerializer()
@ -105,8 +97,6 @@ open class RESTMusicService(
if (cachedMusicFolders != null && !refresh) return cachedMusicFolders if (cachedMusicFolders != null && !refresh) return cachedMusicFolders
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getMusicFolders().execute() api.getMusicFolders().execute()
} }
@ -121,14 +111,11 @@ open class RESTMusicService(
override fun getIndexes( override fun getIndexes(
musicFolderId: String?, musicFolderId: String?,
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): Indexes { ): Indexes {
val cachedIndexes = fileStorage.load(INDEXES_STORAGE_NAME, getIndexesSerializer()) val cachedIndexes = fileStorage.load(INDEXES_STORAGE_NAME, getIndexesSerializer())
if (cachedIndexes != null && !refresh) return cachedIndexes if (cachedIndexes != null && !refresh) return cachedIndexes
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getIndexes(musicFolderId, null).execute() api.getIndexes(musicFolderId, null).execute()
} }
@ -141,14 +128,11 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtists( override fun getArtists(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): Indexes { ): Indexes {
val cachedArtists = fileStorage.load(ARTISTS_STORAGE_NAME, getIndexesSerializer()) val cachedArtists = fileStorage.load(ARTISTS_STORAGE_NAME, getIndexesSerializer())
if (cachedArtists != null && !refresh) return cachedArtists if (cachedArtists != null && !refresh) return cachedArtists
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getArtists(null).execute() api.getArtists(null).execute()
} }
@ -163,11 +147,8 @@ open class RESTMusicService(
id: String?, id: String?,
albumId: String?, albumId: String?,
artistId: String?, artistId: String?,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.star(id, albumId, artistId).execute() } responseChecker.callWithResponseCheck { api -> api.star(id, albumId, artistId).execute() }
} }
@ -176,11 +157,8 @@ open class RESTMusicService(
id: String?, id: String?,
albumId: String?, albumId: String?,
artistId: String?, artistId: String?,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.unstar(id, albumId, artistId).execute() } responseChecker.callWithResponseCheck { api -> api.unstar(id, albumId, artistId).execute() }
} }
@ -188,11 +166,8 @@ open class RESTMusicService(
override fun setRating( override fun setRating(
id: String, id: String,
rating: Int, rating: Int,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.setRating(id, rating).execute() } responseChecker.callWithResponseCheck { api -> api.setRating(id, rating).execute() }
} }
@ -201,11 +176,8 @@ open class RESTMusicService(
id: String, id: String,
name: String?, name: String?,
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getMusicDirectory(id).execute() api.getMusicDirectory(id).execute()
} }
@ -218,11 +190,8 @@ open class RESTMusicService(
id: String, id: String,
name: String?, name: String?,
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getArtist(id).execute() } val response = responseChecker.callWithResponseCheck { api -> api.getArtist(id).execute() }
return response.body()!!.artist.toMusicDirectoryDomainEntity() return response.body()!!.artist.toMusicDirectoryDomainEntity()
@ -233,11 +202,8 @@ open class RESTMusicService(
id: String, id: String,
name: String?, name: String?,
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getAlbum(id).execute() } val response = responseChecker.callWithResponseCheck { api -> api.getAlbum(id).execute() }
return response.body()!!.album.toMusicDirectoryDomainEntity() return response.body()!!.album.toMusicDirectoryDomainEntity()
@ -246,18 +212,17 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun search( override fun search(
criteria: SearchCriteria, criteria: SearchCriteria,
context: Context, context: Context
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
return try { return try {
if ( if (
!isOffline(context) && !isOffline(context) &&
Util.getShouldUseId3Tags(context) Util.getShouldUseId3Tags(context)
) search3(criteria, progressListener) ) search3(criteria)
else search2(criteria, progressListener) else search2(criteria)
} catch (ignored: ApiNotSupportedException) { } catch (ignored: ApiNotSupportedException) {
// Ensure backward compatibility with REST 1.3. // Ensure backward compatibility with REST 1.3.
searchOld(criteria, progressListener) searchOld(criteria)
} }
} }
@ -266,11 +231,8 @@ open class RESTMusicService(
*/ */
@Throws(Exception::class) @Throws(Exception::class)
private fun searchOld( private fun searchOld(
criteria: SearchCriteria, criteria: SearchCriteria
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.search(null, null, null, criteria.query, criteria.songCount, null, null) api.search(null, null, null, criteria.query, criteria.songCount, null, null)
.execute() .execute()
@ -284,12 +246,9 @@ open class RESTMusicService(
*/ */
@Throws(Exception::class) @Throws(Exception::class)
private fun search2( private fun search2(
criteria: SearchCriteria, criteria: SearchCriteria
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
requireNotNull(criteria.query) { "Query param is null" } requireNotNull(criteria.query) { "Query param is null" }
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.search2( api.search2(
criteria.query, criteria.artistCount, null, criteria.albumCount, null, criteria.query, criteria.artistCount, null, criteria.albumCount, null,
@ -302,12 +261,9 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
private fun search3( private fun search3(
criteria: SearchCriteria, criteria: SearchCriteria
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
requireNotNull(criteria.query) { "Query param is null" } requireNotNull(criteria.query) { "Query param is null" }
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.search3( api.search3(
criteria.query, criteria.artistCount, null, criteria.albumCount, null, criteria.query, criteria.artistCount, null, criteria.albumCount, null,
@ -322,11 +278,8 @@ open class RESTMusicService(
override fun getPlaylist( override fun getPlaylist(
id: String, id: String,
name: String?, name: String?,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getPlaylist(id).execute() api.getPlaylist(id).execute()
} }
@ -374,11 +327,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getPlaylists( override fun getPlaylists(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): List<Playlist> { ): List<Playlist> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getPlaylists(null).execute() api.getPlaylists(null).execute()
} }
@ -391,8 +341,7 @@ open class RESTMusicService(
id: String?, id: String?,
name: String?, name: String?,
entries: List<MusicDirectory.Entry>, entries: List<MusicDirectory.Entry>,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
val pSongIds: MutableList<String> = ArrayList(entries.size) val pSongIds: MutableList<String> = ArrayList(entries.size)
@ -401,9 +350,6 @@ open class RESTMusicService(
pSongIds.add(id1) pSongIds.add(id1)
} }
} }
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> responseChecker.callWithResponseCheck { api ->
api.createPlaylist(id, name, pSongIds.toList()).execute() api.createPlaylist(id, name, pSongIds.toList()).execute()
} }
@ -412,11 +358,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun deletePlaylist( override fun deletePlaylist(
id: String, id: String,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.deletePlaylist(id).execute() } responseChecker.callWithResponseCheck { api -> api.deletePlaylist(id).execute() }
} }
@ -426,11 +369,8 @@ open class RESTMusicService(
name: String?, name: String?,
comment: String?, comment: String?,
pub: Boolean, pub: Boolean,
context: Context?, context: Context?
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> responseChecker.callWithResponseCheck { api ->
api.updatePlaylist(id, name, comment, pub, null, null) api.updatePlaylist(id, name, comment, pub, null, null)
.execute() .execute()
@ -440,11 +380,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getPodcastsChannels( override fun getPodcastsChannels(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): List<PodcastsChannel> { ): List<PodcastsChannel> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getPodcasts(false, null).execute() api.getPodcasts(false, null).execute()
} }
@ -455,11 +392,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getPodcastEpisodes( override fun getPodcastEpisodes(
podcastChannelId: String?, podcastChannelId: String?,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getPodcasts(true, podcastChannelId).execute() api.getPodcasts(true, podcastChannelId).execute()
} }
@ -485,11 +419,8 @@ open class RESTMusicService(
override fun getLyrics( override fun getLyrics(
artist: String?, artist: String?,
title: String?, title: String?,
context: Context, context: Context
progressListener: ProgressListener?
): Lyrics { ): Lyrics {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getLyrics(artist, title).execute() api.getLyrics(artist, title).execute()
} }
@ -501,11 +432,8 @@ open class RESTMusicService(
override fun scrobble( override fun scrobble(
id: String, id: String,
submission: Boolean, submission: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> responseChecker.callWithResponseCheck { api ->
api.scrobble(id, null, submission).execute() api.scrobble(id, null, submission).execute()
} }
@ -516,11 +444,8 @@ open class RESTMusicService(
type: String, type: String,
size: Int, size: Int,
offset: Int, offset: Int,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getAlbumList(fromName(type), size, offset, null, null, null, null) api.getAlbumList(fromName(type), size, offset, null, null, null, null)
.execute() .execute()
@ -538,11 +463,8 @@ open class RESTMusicService(
type: String, type: String,
size: Int, size: Int,
offset: Int, offset: Int,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getAlbumList2( api.getAlbumList2(
fromName(type), fromName(type),
@ -564,11 +486,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getRandomSongs( override fun getRandomSongs(
size: Int, size: Int,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getRandomSongs( api.getRandomSongs(
size, size,
@ -587,11 +506,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getStarred( override fun getStarred(
context: Context, context: Context
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getStarred(null).execute() api.getStarred(null).execute()
} }
@ -601,11 +517,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getStarred2( override fun getStarred2(
context: Context, context: Context
progressListener: ProgressListener?
): SearchResult { ): SearchResult {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getStarred2(null).execute() api.getStarred2(null).execute()
} }
@ -619,8 +532,7 @@ open class RESTMusicService(
entry: MusicDirectory.Entry?, entry: MusicDirectory.Entry?,
size: Int, size: Int,
saveToFile: Boolean, saveToFile: Boolean,
highQuality: Boolean, highQuality: Boolean
progressListener: ProgressListener?
): Bitmap? { ): Bitmap? {
// Synchronize on the entry so that we don't download concurrently for // Synchronize on the entry so that we don't download concurrently for
// the same song. // the same song.
@ -744,11 +656,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun updateJukeboxPlaylist( override fun updateJukeboxPlaylist(
ids: List<String>?, ids: List<String>?,
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.SET, null, null, ids, null) api.jukeboxControl(JukeboxAction.SET, null, null, ids, null)
.execute() .execute()
@ -761,11 +670,8 @@ open class RESTMusicService(
override fun skipJukebox( override fun skipJukebox(
index: Int, index: Int,
offsetSeconds: Int, offsetSeconds: Int,
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.SKIP, index, offsetSeconds, null, null) api.jukeboxControl(JukeboxAction.SKIP, index, offsetSeconds, null, null)
.execute() .execute()
@ -776,11 +682,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun stopJukebox( override fun stopJukebox(
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.STOP, null, null, null, null) api.jukeboxControl(JukeboxAction.STOP, null, null, null, null)
.execute() .execute()
@ -791,11 +694,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun startJukebox( override fun startJukebox(
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.START, null, null, null, null) api.jukeboxControl(JukeboxAction.START, null, null, null, null)
.execute() .execute()
@ -806,11 +706,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getJukeboxStatus( override fun getJukeboxStatus(
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.STATUS, null, null, null, null) api.jukeboxControl(JukeboxAction.STATUS, null, null, null, null)
.execute() .execute()
@ -822,11 +719,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun setJukeboxGain( override fun setJukeboxGain(
gain: Float, gain: Float,
context: Context, context: Context
progressListener: ProgressListener?
): JukeboxStatus { ): JukeboxStatus {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.jukeboxControl(JukeboxAction.SET_GAIN, null, null, null, gain) api.jukeboxControl(JukeboxAction.SET_GAIN, null, null, null, gain)
.execute() .execute()
@ -838,11 +732,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getShares( override fun getShares(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): List<Share> { ): List<Share> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getShares().execute() } val response = responseChecker.callWithResponseCheck { api -> api.getShares().execute() }
return response.body()!!.shares.toDomainEntitiesList() return response.body()!!.shares.toDomainEntitiesList()
@ -851,11 +742,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getGenres( override fun getGenres(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): List<Genre> { ): List<Genre> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getGenres().execute() } val response = responseChecker.callWithResponseCheck { api -> api.getGenres().execute() }
return response.body()!!.genresList.toDomainEntityList() return response.body()!!.genresList.toDomainEntityList()
@ -866,11 +754,8 @@ open class RESTMusicService(
genre: String, genre: String,
count: Int, count: Int,
offset: Int, offset: Int,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getSongsByGenre(genre, count, offset, null).execute() api.getSongsByGenre(genre, count, offset, null).execute()
} }
@ -884,11 +769,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getUser( override fun getUser(
username: String, username: String,
context: Context, context: Context
progressListener: ProgressListener?
): UserInfo { ): UserInfo {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getUser(username).execute() api.getUser(username).execute()
} }
@ -899,11 +781,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getChatMessages( override fun getChatMessages(
since: Long?, since: Long?,
context: Context, context: Context
progressListener: ProgressListener?
): List<ChatMessage> { ): List<ChatMessage> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.getChatMessages(since).execute() api.getChatMessages(since).execute()
} }
@ -914,21 +793,15 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun addChatMessage( override fun addChatMessage(
message: String, message: String,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.addChatMessage(message).execute() } responseChecker.callWithResponseCheck { api -> api.addChatMessage(message).execute() }
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun getBookmarks( override fun getBookmarks(
context: Context, context: Context
progressListener: ProgressListener?
): List<Bookmark> { ): List<Bookmark> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getBookmarks().execute() } val response = responseChecker.callWithResponseCheck { api -> api.getBookmarks().execute() }
return response.body()!!.bookmarkList.toDomainEntitiesList() return response.body()!!.bookmarkList.toDomainEntitiesList()
@ -938,11 +811,8 @@ open class RESTMusicService(
override fun createBookmark( override fun createBookmark(
id: String, id: String,
position: Int, position: Int,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> responseChecker.callWithResponseCheck { api ->
api.createBookmark(id, position.toLong(), null).execute() api.createBookmark(id, position.toLong(), null).execute()
} }
@ -951,22 +821,16 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun deleteBookmark( override fun deleteBookmark(
id: String, id: String,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.deleteBookmark(id).execute() } responseChecker.callWithResponseCheck { api -> api.deleteBookmark(id).execute() }
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun getVideos( override fun getVideos(
refresh: Boolean, refresh: Boolean,
context: Context, context: Context
progressListener: ProgressListener?
): MusicDirectory { ): MusicDirectory {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> api.getVideos().execute() } val response = responseChecker.callWithResponseCheck { api -> api.getVideos().execute() }
val musicDirectory = MusicDirectory() val musicDirectory = MusicDirectory()
@ -980,11 +844,8 @@ open class RESTMusicService(
ids: List<String>, ids: List<String>,
description: String?, description: String?,
expires: Long?, expires: Long?,
context: Context, context: Context
progressListener: ProgressListener?
): List<Share> { ): List<Share> {
updateProgressListener(progressListener, R.string.parser_reading)
val response = responseChecker.callWithResponseCheck { api -> val response = responseChecker.callWithResponseCheck { api ->
api.createShare(ids, description, expires).execute() api.createShare(ids, description, expires).execute()
} }
@ -995,11 +856,8 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun deleteShare( override fun deleteShare(
id: String, id: String,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> api.deleteShare(id).execute() } responseChecker.callWithResponseCheck { api -> api.deleteShare(id).execute() }
} }
@ -1008,16 +866,13 @@ open class RESTMusicService(
id: String, id: String,
description: String?, description: String?,
expires: Long?, expires: Long?,
context: Context, context: Context
progressListener: ProgressListener?
) { ) {
var expiresValue: Long? = expires var expiresValue: Long? = expires
if (expires != null && expires == 0L) { if (expires != null && expires == 0L) {
expiresValue = null expiresValue = null
} }
updateProgressListener(progressListener, R.string.parser_reading)
responseChecker.callWithResponseCheck { api -> responseChecker.callWithResponseCheck { api ->
api.updateShare(id, description, expiresValue).execute() api.updateShare(id, description, expiresValue).execute()
} }
@ -1029,8 +884,7 @@ open class RESTMusicService(
username: String?, username: String?,
size: Int, size: Int,
saveToFile: Boolean, saveToFile: Boolean,
highQuality: Boolean, highQuality: Boolean
progressListener: ProgressListener?
): Bitmap? { ): Bitmap? {
// Synchronize on the username so that we don't download concurrently for // Synchronize on the username so that we don't download concurrently for
// the same user. // the same user.
@ -1045,7 +899,6 @@ open class RESTMusicService(
if (bitmap == null) { if (bitmap == null) {
var inputStream: InputStream? = null var inputStream: InputStream? = null
try { try {
updateProgressListener(progressListener, R.string.parser_reading)
val response = subsonicAPIClient.getAvatar(username) val response = subsonicAPIClient.getAvatar(username)
if (response.hasError()) return null if (response.hasError()) return null
@ -1079,13 +932,6 @@ open class RESTMusicService(
} }
} }
private fun updateProgressListener(
progressListener: ProgressListener?,
@StringRes messageId: Int
) {
progressListener?.updateProgress(messageId)
}
companion object { companion object {
private const val MUSIC_FOLDER_STORAGE_NAME = "music_folder" private const val MUSIC_FOLDER_STORAGE_NAME = "music_folder"
private const val INDEXES_STORAGE_NAME = "indexes" private const val INDEXES_STORAGE_NAME = "indexes"

View File

@ -3,6 +3,8 @@ package org.moire.ultrasonic.subsonic
import android.app.Activity import android.app.Activity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import java.util.Collections
import java.util.LinkedList
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
@ -12,7 +14,6 @@ import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.ModalBackgroundTask import org.moire.ultrasonic.util.ModalBackgroundTask
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import java.util.*
class DownloadHandler( class DownloadHandler(
val mediaPlayerController: MediaPlayerController, val mediaPlayerController: MediaPlayerController,
@ -20,14 +21,31 @@ class DownloadHandler(
) { ) {
private val MAX_SONGS = 500 private val MAX_SONGS = 500
fun download(fragment: Fragment, append: Boolean, save: Boolean, autoPlay: Boolean, playNext: Boolean, shuffle: Boolean, songs: List<MusicDirectory.Entry?>) { fun download(
fragment: Fragment,
append: Boolean,
save: Boolean,
autoPlay: Boolean,
playNext: Boolean,
shuffle: Boolean,
songs: List<MusicDirectory.Entry?>
) {
val onValid = Runnable { val onValid = Runnable {
if (!append && !playNext) { if (!append && !playNext) {
mediaPlayerController.clear() mediaPlayerController.clear()
} }
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
mediaPlayerController.download(songs, save, autoPlay, playNext, shuffle, false) mediaPlayerController.download(
val playlistName: String? = fragment.arguments?.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME) songs,
save,
autoPlay,
playNext,
shuffle,
false
)
val playlistName: String? = fragment.arguments?.getString(
Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME
)
if (playlistName != null) { if (playlistName != null) {
mediaPlayerController.suggestedPlaylistName = playlistName mediaPlayerController.suggestedPlaylistName = playlistName
} }
@ -37,22 +55,93 @@ class DownloadHandler(
fragment.findNavController().navigate(R.id.playerFragment) fragment.findNavController().navigate(R.id.playerFragment)
} }
} else if (save) { } else if (save) {
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size, songs.size)) Util.toast(
fragment.context,
fragment.resources.getQuantityString(
R.plurals.select_album_n_songs_pinned,
songs.size,
songs.size
)
)
} else if (playNext) { } else if (playNext) {
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_play_next, songs.size, songs.size)) Util.toast(
fragment.context,
fragment.resources.getQuantityString(
R.plurals.select_album_n_songs_play_next,
songs.size,
songs.size
)
)
} else if (append) { } else if (append) {
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_added, songs.size, songs.size)) Util.toast(
fragment.context,
fragment.resources.getQuantityString(
R.plurals.select_album_n_songs_added,
songs.size,
songs.size
)
)
} }
} }
onValid.run() onValid.run()
} }
fun downloadPlaylist(fragment: Fragment, id: String, name: String?, save: Boolean, append: Boolean, autoplay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean) { fun downloadPlaylist(
downloadRecursively(fragment, id, name, false, false, save, append, autoplay, shuffle, background, playNext, unpin, false) fragment: Fragment,
id: String,
name: String?,
save: Boolean,
append: Boolean,
autoplay: Boolean,
shuffle: Boolean,
background: Boolean,
playNext: Boolean,
unpin: Boolean
) {
downloadRecursively(
fragment,
id,
name,
false,
false,
save,
append,
autoplay,
shuffle,
background,
playNext,
unpin,
false
)
} }
fun downloadShare(fragment: Fragment, id: String, name: String?, save: Boolean, append: Boolean, autoplay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean) { fun downloadShare(
downloadRecursively(fragment, id, name, true, false, save, append, autoplay, shuffle, background, playNext, unpin, false) fragment: Fragment,
id: String,
name: String?,
save: Boolean,
append: Boolean,
autoplay: Boolean,
shuffle: Boolean,
background: Boolean,
playNext: Boolean,
unpin: Boolean
) {
downloadRecursively(
fragment,
id,
name,
true,
false,
save,
append,
autoplay,
shuffle,
background,
playNext,
unpin,
false
)
} }
fun downloadRecursively( fun downloadRecursively(
@ -81,7 +170,8 @@ class DownloadHandler(
background = background, background = background,
playNext = playNext, playNext = playNext,
unpin = unpin, unpin = unpin,
isArtist = isArtist) isArtist = isArtist
)
} }
fun downloadRecursively( fun downloadRecursively(
@ -100,7 +190,7 @@ class DownloadHandler(
isArtist: Boolean isArtist: Boolean
) { ) {
val activity = fragment.activity as Activity val activity = fragment.activity as Activity
val task = object: ModalBackgroundTask<List<MusicDirectory.Entry>>( val task = object : ModalBackgroundTask<List<MusicDirectory.Entry>>(
activity, activity,
false false
) { ) {
@ -115,12 +205,12 @@ class DownloadHandler(
} else { } else {
if (isDirectory) { if (isDirectory) {
root = if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) root = if (!isOffline(activity) && Util.getShouldUseId3Tags(activity))
musicService.getAlbum(id, name, false, activity, null) musicService.getAlbum(id, name, false, activity)
else else
musicService.getMusicDirectory(id, name, false, activity, null) musicService.getMusicDirectory(id, name, false, activity)
} else if (isShare) { } else if (isShare) {
root = MusicDirectory() root = MusicDirectory()
val shares = musicService.getShares(true, activity, null) val shares = musicService.getShares(true, activity)
for (share in shares) { for (share in shares) {
if (share.id == id) { if (share.id == id) {
for (entry in share.getEntries()) { for (entry in share.getEntries()) {
@ -130,7 +220,7 @@ class DownloadHandler(
} }
} }
} else { } else {
root = musicService.getPlaylist(id, name, activity, null) root = musicService.getPlaylist(id, name, activity)
} }
getSongsRecursively(root, songs) getSongsRecursively(root, songs)
} }
@ -138,7 +228,10 @@ class DownloadHandler(
} }
@Throws(Exception::class) @Throws(Exception::class)
private fun getSongsRecursively(parent: MusicDirectory, songs: MutableList<MusicDirectory.Entry>) { private fun getSongsRecursively(
parent: MusicDirectory,
songs: MutableList<MusicDirectory.Entry>
) {
if (songs.size > MAX_SONGS) { if (songs.size > MAX_SONGS) {
return return
} }
@ -150,21 +243,32 @@ class DownloadHandler(
val musicService = getMusicService(activity) val musicService = getMusicService(activity)
for ((id1, _, _, title) in parent.getChildren(true, false)) { for ((id1, _, _, title) in parent.getChildren(true, false)) {
var root: MusicDirectory var root: MusicDirectory
root = if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) musicService.getAlbum(id1, title, false, activity, null) root = if (
else musicService.getMusicDirectory(id1, title, false, activity, null) !isOffline(activity) &&
Util.getShouldUseId3Tags(activity)
) musicService.getAlbum(id1, title, false, activity)
else musicService.getMusicDirectory(id1, title, false, activity)
getSongsRecursively(root, songs) getSongsRecursively(root, songs)
} }
} }
@Throws(Exception::class) @Throws(Exception::class)
private fun getSongsForArtist(id: String, songs: MutableCollection<MusicDirectory.Entry>) { private fun getSongsForArtist(
id: String,
songs: MutableCollection<MusicDirectory.Entry>
) {
if (songs.size > MAX_SONGS) { if (songs.size > MAX_SONGS) {
return return
} }
val musicService = getMusicService(activity) val musicService = getMusicService(activity)
val artist = musicService.getArtist(id, "", false, activity, null) val artist = musicService.getArtist(id, "", false, activity)
for ((id1) in artist.getChildren()) { for ((id1) in artist.getChildren()) {
val albumDirectory = musicService.getAlbum(id1, "", false, activity, null) val albumDirectory = musicService.getAlbum(
id1,
"",
false,
activity
)
for (song in albumDirectory.getChildren()) { for (song in albumDirectory.getChildren()) {
if (!song.isVideo) { if (!song.isVideo) {
songs.add(song) songs.add(song)
@ -186,9 +290,22 @@ class DownloadHandler(
if (unpin) { if (unpin) {
mediaPlayerController.unpin(songs) mediaPlayerController.unpin(songs)
} else { } else {
mediaPlayerController.download(songs, save, autoPlay, playNext, shuffle, false) mediaPlayerController.download(
if (!append && Util.getShouldTransitionOnPlaybackPreference(activity)) { songs,
fragment.findNavController().popBackStack(R.id.playerFragment, true) save,
autoPlay,
playNext,
shuffle,
false
)
if (
!append &&
Util.getShouldTransitionOnPlaybackPreference(activity)
) {
fragment.findNavController().popBackStack(
R.id.playerFragment,
true
)
fragment.findNavController().navigate(R.id.playerFragment) fragment.findNavController().navigate(R.id.playerFragment)
} }
} }

View File

@ -9,13 +9,15 @@ import org.moire.ultrasonic.util.ImageLoader
import org.moire.ultrasonic.util.LegacyImageLoader import org.moire.ultrasonic.util.LegacyImageLoader
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
class ImageLoaderProvider (val context: Context) { class ImageLoaderProvider(val context: Context) {
private var imageLoader: ImageLoader? = null private var imageLoader: ImageLoader? = null
@Synchronized @Synchronized
fun clearImageLoader() { fun clearImageLoader() {
if (imageLoader != null && if (
imageLoader!!.isRunning) { imageLoader != null &&
imageLoader!!.isRunning
) {
imageLoader!!.clear() imageLoader!!.clear()
} }
imageLoader = null imageLoader = null
@ -42,4 +44,4 @@ class ImageLoaderProvider (val context: Context) {
} }
return imageLoader!! return imageLoader!!
} }
} }

View File

@ -13,4 +13,4 @@ class NetworkAndStorageChecker(val context: Context) {
Util.toast(context, R.string.select_album_no_network) Util.toast(context, R.string.select_album_no_network)
} }
} }
} }

View File

@ -9,6 +9,7 @@ import android.widget.CheckBox
import android.widget.EditText import android.widget.EditText
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import java.util.regex.Pattern
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
@ -16,13 +17,11 @@ import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.BackgroundTask import org.moire.ultrasonic.util.BackgroundTask
import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.FragmentBackgroundTask
import org.moire.ultrasonic.util.ShareDetails import org.moire.ultrasonic.util.ShareDetails
import org.moire.ultrasonic.util.TabActivityBackgroundTask
import org.moire.ultrasonic.util.TimeSpan import org.moire.ultrasonic.util.TimeSpan
import org.moire.ultrasonic.util.TimeSpanPicker import org.moire.ultrasonic.util.TimeSpanPicker
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import java.util.*
import java.util.regex.Pattern
class ShareHandler(val context: Context) { class ShareHandler(val context: Context) {
private var shareDescription: EditText? = null private var shareDescription: EditText? = null
@ -32,7 +31,12 @@ class ShareHandler(val context: Context) {
private var saveAsDefaultsCheckBox: CheckBox? = null private var saveAsDefaultsCheckBox: CheckBox? = null
private val pattern = Pattern.compile(":") private val pattern = Pattern.compile(":")
fun createShare(fragment: Fragment, entries: List<MusicDirectory.Entry?>?, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { fun createShare(
fragment: Fragment,
entries: List<MusicDirectory.Entry?>?,
swipe: SwipeRefreshLayout?,
cancellationToken: CancellationToken
) {
val askForDetails = Util.getShouldAskForShareDetails(context) val askForDetails = Util.getShouldAskForShareDetails(context)
val shareDetails = ShareDetails() val shareDetails = ShareDetails()
shareDetails.Entries = entries shareDetails.Entries = entries
@ -40,13 +44,25 @@ class ShareHandler(val context: Context) {
showDialog(fragment, shareDetails, swipe, cancellationToken) showDialog(fragment, shareDetails, swipe, cancellationToken)
} else { } else {
shareDetails.Description = Util.getDefaultShareDescription(context) shareDetails.Description = Util.getDefaultShareDescription(context)
shareDetails.Expiration = TimeSpan.getCurrentTime().add(Util.getDefaultShareExpirationInMillis(context)).totalMilliseconds shareDetails.Expiration = TimeSpan.getCurrentTime().add(
Util.getDefaultShareExpirationInMillis(context)
).totalMilliseconds
share(fragment, shareDetails, swipe, cancellationToken) share(fragment, shareDetails, swipe, cancellationToken)
} }
} }
fun share(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { fun share(
val task: BackgroundTask<Share> = object : TabActivityBackgroundTask<Share>(fragment.requireActivity(), true, swipe, cancellationToken) { fragment: Fragment,
shareDetails: ShareDetails,
swipe: SwipeRefreshLayout?,
cancellationToken: CancellationToken
) {
val task: BackgroundTask<Share> = object : FragmentBackgroundTask<Share>(
fragment.requireActivity(),
true,
swipe,
cancellationToken
) {
@Throws(Throwable::class) @Throws(Throwable::class)
override fun doInBackground(): Share { override fun doInBackground(): Share {
val ids: MutableList<String?> = ArrayList() val ids: MutableList<String?> = ArrayList()
@ -62,33 +78,49 @@ class ShareHandler(val context: Context) {
if (shareDetails.Expiration != 0L) { if (shareDetails.Expiration != 0L) {
timeInMillis = shareDetails.Expiration timeInMillis = shareDetails.Expiration
} }
val shares = musicService.createShare(ids, shareDetails.Description, timeInMillis, context, this) val shares =
musicService.createShare(ids, shareDetails.Description, timeInMillis, context)
return shares[0] return shares[0]
} }
override fun done(result: Share) { override fun done(result: Share) {
val intent = Intent(Intent.ACTION_SEND) val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain" intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, String.format("%s\n\n%s", Util.getShareGreeting(context), result.url)) intent.putExtra(
fragment.activity?.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via))) Intent.EXTRA_TEXT,
String.format("%s\n\n%s", Util.getShareGreeting(context), result.url)
)
fragment.activity?.startActivity(
Intent.createChooser(
intent,
context.getResources().getString(R.string.share_via)
)
)
} }
} }
task.execute() task.execute()
} }
private fun showDialog(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken) { private fun showDialog(
fragment: Fragment,
shareDetails: ShareDetails,
swipe: SwipeRefreshLayout?,
cancellationToken: CancellationToken
) {
val layout = LayoutInflater.from(fragment.context).inflate(R.layout.share_details, null) val layout = LayoutInflater.from(fragment.context).inflate(R.layout.share_details, null)
if (layout != null) { if (layout != null) {
shareDescription = layout.findViewById<View>(R.id.share_description) as EditText shareDescription = layout.findViewById<View>(R.id.share_description) as EditText
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
noExpirationCheckBox = layout.findViewById<View>(R.id.timeSpanDisableCheckBox) as CheckBox noExpirationCheckBox = layout.findViewById<View>(
R.id.timeSpanDisableCheckBox
) as CheckBox
saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox
timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker
} }
val builder = AlertDialog.Builder(fragment.context) val builder = AlertDialog.Builder(fragment.context)
builder.setTitle(R.string.share_set_share_options) builder.setTitle(R.string.share_set_share_options)
builder.setPositiveButton(R.string.common_save) { dialog, clickId -> builder.setPositiveButton(R.string.common_save) { _, _ ->
if (!noExpirationCheckBox!!.isChecked) { if (!noExpirationCheckBox!!.isChecked) {
val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan
val now = TimeSpan.getCurrentTime() val now = TimeSpan.getCurrentTime()
@ -101,18 +133,26 @@ class ShareHandler(val context: Context) {
if (saveAsDefaultsCheckBox!!.isChecked) { if (saveAsDefaultsCheckBox!!.isChecked) {
val timeSpanType: String = timeSpanPicker!!.timeSpanType val timeSpanType: String = timeSpanPicker!!.timeSpanType
val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount
Util.setDefaultShareExpiration(context, if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0) String.format("%d:%s", timeSpanAmount, timeSpanType) else "") Util.setDefaultShareExpiration(
context,
if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0)
String.format("%d:%s", timeSpanAmount, timeSpanType) else ""
)
Util.setDefaultShareDescription(context, shareDetails.Description) Util.setDefaultShareDescription(context, shareDetails.Description)
} }
share(fragment, shareDetails, swipe, cancellationToken) share(fragment, shareDetails, swipe, cancellationToken)
} }
builder.setNegativeButton(R.string.common_cancel) { dialog, clickId -> builder.setNegativeButton(R.string.common_cancel) { dialog, _ ->
dialog.cancel() dialog.cancel()
} }
builder.setView(layout) builder.setView(layout)
builder.setCancelable(true) builder.setCancelable(true)
timeSpanPicker!!.setTimeSpanDisableText(context.resources.getString(R.string.no_expiration)) timeSpanPicker!!.setTimeSpanDisableText(context.resources.getString(R.string.no_expiration))
noExpirationCheckBox!!.setOnCheckedChangeListener { _, b -> timeSpanPicker!!.isEnabled = !b } noExpirationCheckBox!!.setOnCheckedChangeListener {
_,
b ->
timeSpanPicker!!.isEnabled = !b
}
val defaultDescription = Util.getDefaultShareDescription(context) val defaultDescription = Util.getDefaultShareDescription(context)
val timeSpan = Util.getDefaultShareExpiration(context) val timeSpan = Util.getDefaultShareExpiration(context)
val split = pattern.split(timeSpan) val split = pattern.split(timeSpan)
@ -136,4 +176,4 @@ class ShareHandler(val context: Context) {
builder.create() builder.create()
builder.show() builder.show()
} }
} }

View File

@ -18,4 +18,4 @@ class VideoPlayer(val context: Context) {
Util.toast(context, e.message, false) Util.toast(context, e.message, false)
} }
} }
} }

View File

@ -28,6 +28,6 @@ class CancellationToken {
* Requests that this token be cancelled * Requests that this token be cancelled
*/ */
fun cancel() { fun cancel() {
isCancellationRequested = true; isCancellationRequested = true
} }
} }

View File

@ -1,7 +1,8 @@
package org.moire.ultrasonic.util package org.moire.ultrasonic.util
class NowPlayingEventDistributor { class NowPlayingEventDistributor {
var eventListenerList: MutableList<NowPlayingEventListener> = listOf<NowPlayingEventListener>().toMutableList() var eventListenerList: MutableList<NowPlayingEventListener> =
listOf<NowPlayingEventListener>().toMutableList()
fun subscribe(listener: NowPlayingEventListener) { fun subscribe(listener: NowPlayingEventListener) {
eventListenerList.add(listener) eventListenerList.add(listener)
@ -12,14 +13,14 @@ class NowPlayingEventDistributor {
} }
fun RaiseShowNowPlayingEvent() { fun RaiseShowNowPlayingEvent() {
eventListenerList.forEach{ listener -> listener.onShowNowPlaying() } eventListenerList.forEach { listener -> listener.onShowNowPlaying() }
} }
fun RaiseHideNowPlayingEvent() { fun RaiseHideNowPlayingEvent() {
eventListenerList.forEach{ listener -> listener.onHideNowPlaying() } eventListenerList.forEach { listener -> listener.onHideNowPlaying() }
} }
fun RaiseNowPlayingDismissedEvent() { fun RaiseNowPlayingDismissedEvent() {
eventListenerList.forEach{ listener -> listener.onDismissNowPlaying() } eventListenerList.forEach { listener -> listener.onDismissNowPlaying() }
} }
} }

View File

@ -4,4 +4,4 @@ interface NowPlayingEventListener {
fun onDismissNowPlaying() fun onDismissNowPlaying()
fun onHideNowPlaying() fun onHideNowPlaying()
fun onShowNowPlaying() fun onShowNowPlaying()
} }

View File

@ -2,19 +2,20 @@ package org.moire.ultrasonic.util
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import timber.log.Timber
import java.io.File import java.io.File
import java.io.PrintWriter import java.io.PrintWriter
import timber.log.Timber
private const val filename = "ultrasonic-stacktrace.txt" private const val filename = "ultrasonic-stacktrace.txt"
/** /**
* Logs the stack trace of uncaught exceptions to a file on the SD card. * Logs the stack trace of uncaught exceptions to a file on the SD card.
*/ */
class SubsonicUncaughtExceptionHandler ( class SubsonicUncaughtExceptionHandler(
private val context: Context private val context: Context
) : Thread.UncaughtExceptionHandler { ) : Thread.UncaughtExceptionHandler {
private val defaultHandler: Thread.UncaughtExceptionHandler? = Thread.getDefaultUncaughtExceptionHandler() private val defaultHandler: Thread.UncaughtExceptionHandler? =
Thread.getDefaultUncaughtExceptionHandler()
override fun uncaughtException(thread: Thread, throwable: Throwable) { override fun uncaughtException(thread: Thread, throwable: Throwable) {
var file: File? = null var file: File? = null
@ -24,8 +25,10 @@ class SubsonicUncaughtExceptionHandler (
file = File(FileUtil.getUltrasonicDirectory(context), filename) file = File(FileUtil.getUltrasonicDirectory(context), filename)
printWriter = PrintWriter(file) printWriter = PrintWriter(file)
val logMessage = String.format( val logMessage = String.format(
"Android API level: %s\nUltrasonic version name: %s\nUltrasonic version code: %s\n\n", "Android API level: %s\nUltrasonic version name: %s\n" +
Build.VERSION.SDK_INT, Util.getVersionName(context), Util.getVersionCode(context)) "Ultrasonic version code: %s\n\n",
Build.VERSION.SDK_INT, Util.getVersionName(context), Util.getVersionCode(context)
)
printWriter.println(logMessage) printWriter.println(logMessage)
throwable.printStackTrace(printWriter) throwable.printStackTrace(printWriter)
Timber.e(throwable, "Uncaught Exception! %s", logMessage) Timber.e(throwable, "Uncaught Exception! %s", logMessage)
@ -37,4 +40,4 @@ class SubsonicUncaughtExceptionHandler (
defaultHandler?.uncaughtException(thread, throwable) defaultHandler?.uncaughtException(thread, throwable)
} }
} }
} }

View File

@ -1,7 +1,8 @@
package org.moire.ultrasonic.util package org.moire.ultrasonic.util
class ThemeChangedEventDistributor { class ThemeChangedEventDistributor {
var eventListenerList: MutableList<ThemeChangedEventListener> = listOf<ThemeChangedEventListener>().toMutableList() var eventListenerList: MutableList<ThemeChangedEventListener> =
listOf<ThemeChangedEventListener>().toMutableList()
fun subscribe(listener: ThemeChangedEventListener) { fun subscribe(listener: ThemeChangedEventListener) {
eventListenerList.add(listener) eventListenerList.add(listener)
@ -12,6 +13,6 @@ class ThemeChangedEventDistributor {
} }
fun RaiseThemeChangedEvent() { fun RaiseThemeChangedEvent() {
eventListenerList.forEach{ listener -> listener.onThemeChanged() } eventListenerList.forEach { listener -> listener.onThemeChanged() }
} }
} }

View File

@ -2,4 +2,4 @@ package org.moire.ultrasonic.util
interface ThemeChangedEventListener { interface ThemeChangedEventListener {
fun onThemeChanged() fun onThemeChanged()
} }

View File

@ -204,9 +204,9 @@ class SongView(context: Context) : UpdateView(context), Checkable {
val musicService = getMusicService(this@SongView.context) val musicService = getMusicService(this@SongView.context)
try { try {
if (!isStarred) { if (!isStarred) {
musicService.star(id, null, null, this@SongView.context, null) musicService.star(id, null, null, this@SongView.context)
} else { } else {
musicService.unstar(id, null, null, this@SongView.context, null) musicService.unstar(id, null, null, this@SongView.context)
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)

View File

@ -4,43 +4,48 @@
a:layout_height="fill_parent" a:layout_height="fill_parent"
a:orientation="vertical" > a:orientation="vertical" >
<ListView <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
a:id="@+id/chat_entries_list" a:id="@+id/chat_refresh"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="0dip" a:layout_height="0dp"
a:layout_weight="1.0" /> a:layout_weight="1.0">
<ListView
a:id="@+id/chat_entries_list"
a:layout_width="fill_parent"
a:layout_height="0dip"
a:layout_weight="1.0" />
<LinearLayout <LinearLayout
a:layout_height="4dip" a:layout_height="4dip"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_marginTop="4dip" a:layout_marginTop="4dip"
a:background="@drawable/drop_shadow" /> a:background="@drawable/drop_shadow" />
<LinearLayout <LinearLayout
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:orientation="horizontal" a:orientation="horizontal"
a:gravity="bottom" > a:gravity="bottom" >
<EditText <EditText
a:id="@+id/chat_edittext" a:id="@+id/chat_edittext"
a:layout_width="0dip" a:layout_width="0dip"
a:layout_height="40dip" a:layout_height="40dip"
a:layout_weight="1" a:layout_weight="1"
a:autoLink="all" a:autoLink="all"
a:hint="@string/chat.send_a_message" a:hint="@string/chat.send_a_message"
a:inputType="textEmailAddress|textMultiLine" a:inputType="textEmailAddress|textMultiLine"
a:linksClickable="true" a:linksClickable="true"
a:paddingBottom="10dip" a:paddingBottom="10dip"
a:paddingTop="10dip" /> a:paddingTop="10dip" />
<ImageButton <ImageButton
a:id="@+id/chat_send" a:id="@+id/chat_send"
a:layout_width="55dip" a:layout_width="55dip"
a:layout_height="40dip" a:layout_height="40dip"
a:background="@color/transparent" a:background="@color/transparent"
a:src="?attr/chat_send" /> a:src="?attr/chat_send" />
</LinearLayout>
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout> </LinearLayout>

View File

@ -3,21 +3,25 @@
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="fill_parent" a:layout_height="fill_parent"
a:orientation="vertical" > a:orientation="vertical" >
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
<TextView a:id="@+id/podcasts_refresh"
a:id="@+id/select_podcasts_empty"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="0dp"
a:padding="10dip" a:layout_weight="1.0">
a:text="@string/podcasts_channels.empty" <TextView
a:visibility="gone" /> a:id="@+id/select_podcasts_empty"
a:layout_width="fill_parent"
<ListView a:layout_height="wrap_content"
a:id="@+id/podcasts_channels_items_list" a:padding="10dip"
a:layout_width="fill_parent" a:text="@string/podcasts_channels.empty"
a:layout_height="0dip" a:visibility="gone" />
a:layout_weight="1.0"
a:fastScrollEnabled="true"
a:textFilterEnabled="true" />
<ListView
a:id="@+id/podcasts_channels_items_list"
a:layout_width="fill_parent"
a:layout_height="0dip"
a:layout_weight="1.0"
a:fastScrollEnabled="true"
a:textFilterEnabled="true" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout> </LinearLayout>