Fixed back press in Server Editor

Fixed displaying "no results" for Search
Cleaned up code
This commit is contained in:
Nite 2021-02-25 18:40:41 +01:00
parent 8a047c5b78
commit 9910792c11
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
43 changed files with 269 additions and 434 deletions

View File

@ -40,7 +40,7 @@ import java.io.Serializable;
public class EqualizerController public class EqualizerController
{ {
private static Boolean available = null; private static Boolean available = null;
private static MutableLiveData<EqualizerController> instance = new MutableLiveData<>(); private static final MutableLiveData<EqualizerController> instance = new MutableLiveData<>();
private Context context; private Context context;
public Equalizer equalizer; public Equalizer equalizer;

View File

@ -36,7 +36,7 @@ public class VisualizerController
{ {
private static final int PREFERRED_CAPTURE_SIZE = 128; // Must be a power of two. private static final int PREFERRED_CAPTURE_SIZE = 128; // Must be a power of two.
private static Boolean available = null; private static Boolean available = null;
private static MutableLiveData<VisualizerController> instance = new MutableLiveData<>(); private static final MutableLiveData<VisualizerController> instance = new MutableLiveData<>();
public Visualizer visualizer; public Visualizer visualizer;
private int audioSessionId; private int audioSessionId;

View File

@ -16,9 +16,13 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
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.util.Util; import org.moire.ultrasonic.util.Util;
/**
* Displays online help and about information in a webWiew
*/
public class AboutFragment extends Fragment { public class AboutFragment extends Fragment {
private WebView webView; private WebView webView;
@ -109,7 +113,7 @@ public class AboutFragment extends Fragment {
} }
@Override @Override
public void onSaveInstanceState(Bundle state) public void onSaveInstanceState(@NotNull Bundle state)
{ {
webView.saveState(state); webView.saveState(state);
super.onSaveInstanceState(state); super.onSaveInstanceState(state);

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -38,6 +37,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Lists the Bookmarks available on the server
*/
public class BookmarksFragment extends Fragment { public class BookmarksFragment extends Fragment {
private SwipeRefreshLayout refreshAlbumListView; private SwipeRefreshLayout refreshAlbumListView;
@ -82,7 +84,8 @@ public class BookmarksFragment extends Fragment {
@Override @Override
public void onRefresh() public void onRefresh()
{ {
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); enableButtons();
getBookmarks();
} }
}); });
@ -219,7 +222,7 @@ public class BookmarksFragment extends Fragment {
private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView) private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
{ {
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(10); List<MusicDirectory.Entry> songs = new ArrayList<>(10);
if (albumListView != null) if (albumListView != null)
{ {
@ -236,12 +239,6 @@ public class BookmarksFragment extends Fragment {
return songs; return songs;
} }
private void refresh()
{
enableButtons();
getBookmarks();
}
private void selectAllOrNone() private void selectAllOrNone()
{ {
boolean someUnselected = false; boolean someUnselected = false;
@ -436,20 +433,4 @@ public class BookmarksFragment extends Fragment {
albumListView.setAdapter(new EntryAdapter(getContext(), imageLoader.getValue().getImageLoader(), entries, true)); albumListView.setAdapter(new EntryAdapter(getContext(), imageLoader.getValue().getImageLoader(), entries, true));
} }
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
refresh();
return null;
}
}
} }

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -44,6 +43,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Provides online chat functionality
*/
public class ChatFragment extends Fragment { public class ChatFragment extends Fragment {
private ListView chatListView; private ListView chatListView;
@ -51,7 +53,7 @@ public class ChatFragment extends Fragment {
private ImageButton sendButton; private ImageButton sendButton;
private Timer timer; private Timer timer;
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<>();
private CancellationToken cancellationToken; private CancellationToken cancellationToken;
private SwipeRefreshLayout swipeRefresh; private SwipeRefreshLayout swipeRefresh;
@ -153,11 +155,10 @@ public class ChatFragment extends Fragment {
*/ */
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Check if user triggered a refresh: // Check if user triggered a refresh:
case R.id.menu_refresh: if (item.getItemId() == R.id.menu_refresh) {
// Start the refresh background task. // Start the refresh background task.
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); load();
return true; return true;
} }
// User didn't trigger a refresh, let the superclass handle this action // User didn't trigger a refresh, let the superclass handle this action
@ -313,20 +314,4 @@ public class ChatFragment extends Fragment {
task.execute(); task.execute();
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
load();
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
return null;
}
}
} }

View File

@ -24,10 +24,14 @@ import org.moire.ultrasonic.audiofx.EqualizerController;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import timber.log.Timber; import timber.log.Timber;
/**
* Displays the Equalizer
*/
public class EqualizerFragment extends Fragment { public class EqualizerFragment extends Fragment {
private static final int MENU_GROUP_PRESET = 100; private static final int MENU_GROUP_PRESET = 100;
@ -124,7 +128,7 @@ public class EqualizerFragment extends Fragment {
} }
catch (Exception ex) catch (Exception ex)
{ {
//TODO: Show a dialog //TODO: Show a dialog?
Timber.i(ex, "An exception has occurred in EqualizerFragment onContextItemSelected"); Timber.i(ex, "An exception has occurred in EqualizerFragment onContextItemSelected");
} }
@ -179,7 +183,7 @@ public class EqualizerFragment extends Fragment {
} }
catch (Exception ex) catch (Exception ex)
{ {
//TODO: Show a dialog //TODO: Show a dialog?
Timber.i(ex, "An exception has occurred in EqualizerFragment updateBars"); Timber.i(ex, "An exception has occurred in EqualizerFragment updateBars");
} }
} }
@ -200,7 +204,7 @@ public class EqualizerFragment extends Fragment {
{ {
final short band = i; final short band = i;
View bandBar = LayoutInflater.from(getContext()).inflate(R.layout.equalizer_bar, null); View bandBar = LayoutInflater.from(getContext()).inflate(R.layout.equalizer_bar, equalizerLayout, false);
TextView freqTextView; TextView freqTextView;
if (bandBar != null) if (bandBar != null)
@ -209,7 +213,7 @@ public class EqualizerFragment extends Fragment {
final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level); final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar); SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
freqTextView.setText((equalizer.getCenterFreq(band) / 1000) + " Hz"); freqTextView.setText(String.format(Locale.getDefault(), "%d Hz", equalizer.getCenterFreq(band) / 1000));
bars.put(band, bar); bars.put(band, bar);
bar.setMax(maxEQLevel - minEQLevel); bar.setMax(maxEQLevel - minEQLevel);
@ -265,7 +269,7 @@ public class EqualizerFragment extends Fragment {
{ {
if (levelTextView != null) if (levelTextView != null)
{ {
levelTextView.setText(String.format("%s%d dB", level > 0 ? "+" : "", level / 100)); levelTextView.setText(String.format(Locale.getDefault(), "%s%d dB", level > 0 ? "+" : "", level / 100));
} }
} }
} }

View File

@ -23,6 +23,9 @@ import org.moire.ultrasonic.util.Util;
import timber.log.Timber; import timber.log.Timber;
/**
* Displays the lyrics of a song
*/
public class LyricsFragment extends Fragment { public class LyricsFragment extends Fragment {
private TextView artistView; private TextView artistView;

View File

@ -27,6 +27,9 @@ import kotlin.Lazy;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays the Main screen of Ultrasonic, where the music library can be browsed
*/
public class MainFragment extends Fragment { public class MainFragment extends Fragment {
private static boolean shouldUseId3; private static boolean shouldUseId3;
@ -84,7 +87,7 @@ public class MainFragment extends Fragment {
private void setupMenuList(ListView list) private void setupMenuList(ListView list)
{ {
final View buttons = getLayoutInflater().inflate(R.layout.main_buttons, null); final View buttons = getLayoutInflater().inflate(R.layout.main_buttons, list, false);
final View serverButton = buttons.findViewById(R.id.main_select_server); final View serverButton = buttons.findViewById(R.id.main_select_server);
final TextView serverTextView = serverButton.findViewById(R.id.main_select_server_2); final TextView serverTextView = serverButton.findViewById(R.id.main_select_server_2);

View File

@ -29,6 +29,10 @@ import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Contains the mini-now playing information box displayed at the bottom of the screen
*/
public class NowPlayingFragment extends Fragment { public class NowPlayingFragment extends Fragment {
private static final int MIN_DISTANCE = 30; private static final int MIN_DISTANCE = 30;
@ -130,7 +134,7 @@ public class NowPlayingFragment extends Fragment {
getView().setOnTouchListener(new View.OnTouchListener() { getView().setOnTouchListener(new View.OnTouchListener() {
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
return handleOnTouch(v, event); return handleOnTouch(event);
} }
}); });
@ -153,7 +157,7 @@ public class NowPlayingFragment extends Fragment {
} }
} }
private boolean handleOnTouch(View v, MotionEvent event) { private boolean handleOnTouch(MotionEvent event) {
switch (event.getAction()) switch (event.getAction())
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:

View File

@ -82,6 +82,9 @@ import static org.moire.ultrasonic.domain.PlayerState.IDLE;
import static org.moire.ultrasonic.domain.PlayerState.PAUSED; import static org.moire.ultrasonic.domain.PlayerState.PAUSED;
import static org.moire.ultrasonic.domain.PlayerState.STOPPED; import static org.moire.ultrasonic.domain.PlayerState.STOPPED;
/**
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist
*/
public class PlayerFragment extends Fragment implements GestureDetector.OnGestureListener { public class PlayerFragment extends Fragment implements GestureDetector.OnGestureListener {
private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5; private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5;
@ -235,16 +238,14 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
} }
}); });
View.OnTouchListener touchListener = new View.OnTouchListener() albumArtImageView.setOnTouchListener(new View.OnTouchListener()
{ {
@Override @Override
public boolean onTouch(View view, MotionEvent me) public boolean onTouch(View view, MotionEvent me)
{ {
return gestureScanner.onTouchEvent(me); return gestureScanner.onTouchEvent(me);
} }
}; });
albumArtImageView.setOnTouchListener(touchListener);
albumArtImageView.setOnClickListener(new View.OnClickListener() albumArtImageView.setOnClickListener(new View.OnClickListener()
{ {
@ -265,7 +266,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
mediaPlayerControllerLazy.getValue().previous(); mediaPlayerControllerLazy.getValue().previous();
return null; return null;
@ -301,7 +302,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Boolean>(getActivity()) new SilentBackgroundTask<Boolean>(getActivity())
{ {
@Override @Override
protected Boolean doInBackground() throws Throwable protected Boolean doInBackground()
{ {
if (mediaPlayerControllerLazy.getValue().getCurrentPlayingNumberOnPlaylist() < mediaPlayerControllerLazy.getValue().getPlaylistSize() - 1) if (mediaPlayerControllerLazy.getValue().getCurrentPlayingNumberOnPlaylist() < mediaPlayerControllerLazy.getValue().getPlaylistSize() - 1)
{ {
@ -345,7 +346,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
mediaPlayerControllerLazy.getValue().pause(); mediaPlayerControllerLazy.getValue().pause();
return null; return null;
@ -369,7 +370,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
mediaPlayerControllerLazy.getValue().reset(); mediaPlayerControllerLazy.getValue().reset();
return null; return null;
@ -395,7 +396,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
start(); start();
return null; return null;
@ -456,7 +457,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
mediaPlayerControllerLazy.getValue().seekTo(getProgressBar().getProgress()); mediaPlayerControllerLazy.getValue().seekTo(getProgressBar().getProgress());
return null; return null;
@ -491,7 +492,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity()) new SilentBackgroundTask<Void>(getActivity())
{ {
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
mediaPlayerControllerLazy.getValue().play(position); mediaPlayerControllerLazy.getValue().play(position);
return null; return null;
@ -812,7 +813,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
} }
@Override @Override
public void onCreateContextMenu(final ContextMenu menu, final View view, final ContextMenu.ContextMenuInfo menuInfo) public void onCreateContextMenu(final @NotNull ContextMenu menu, final @NotNull View view, final ContextMenu.ContextMenuInfo menuInfo)
{ {
super.onCreateContextMenu(menu, view, menuInfo); super.onCreateContextMenu(menu, view, menuInfo);
if (view == playlistView) if (view == playlistView)
@ -1345,7 +1346,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
PlayerState playerState; PlayerState playerState;
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
this.mediaPlayerController = mediaPlayerControllerLazy.getValue(); this.mediaPlayerController = mediaPlayerControllerLazy.getValue();
isJukeboxEnabled = this.mediaPlayerController.isJukeboxEnabled(); isJukeboxEnabled = this.mediaPlayerController.isJukeboxEnabled();
@ -1409,13 +1410,9 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.common_appname); FragmentTitle.Companion.setTitle(PlayerFragment.this, R.string.common_appname);
break; break;
case IDLE: case IDLE:
break;
case PREPARED: case PREPARED:
break;
case STOPPED: case STOPPED:
break;
case PAUSED: case PAUSED:
break;
case COMPLETED: case COMPLETED:
break; break;
} }
@ -1464,13 +1461,13 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
int seekTo; int seekTo;
@Override @Override
protected Void doInBackground() throws Throwable protected Void doInBackground()
{ {
msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition()); msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition());
duration = mediaPlayerController.getPlayerDuration(); duration = mediaPlayerController.getPlayerDuration();
final int msTotal = duration; final int msTotal = duration;
seekTo = msPlayed + ms > msTotal ? msTotal : msPlayed + ms; seekTo = Math.min(msPlayed + ms, msTotal);
mediaPlayerController.seekTo(seekTo); mediaPlayerController.seekTo(seekTo);
return null; return null;
} }

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.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.Spannable; import android.text.Spannable;
@ -51,6 +50,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays the playlists stored on the server
*/
public class PlaylistsFragment extends Fragment { public class PlaylistsFragment extends Fragment {
private SwipeRefreshLayout refreshPlaylistsListView; private SwipeRefreshLayout refreshPlaylistsListView;
@ -83,9 +85,8 @@ public class PlaylistsFragment extends Fragment {
refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{ {
@Override @Override
public void onRefresh() public void onRefresh() {
{ load(true);
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
}); });
@ -119,11 +120,6 @@ public class PlaylistsFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
} }
private void refresh()
{
load(true);
}
private void load(final boolean refresh) private void load(final boolean refresh)
{ {
BackgroundTask<List<Playlist>> task = new FragmentBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken) BackgroundTask<List<Playlist>> task = new FragmentBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken)
@ -182,42 +178,33 @@ public class PlaylistsFragment extends Fragment {
} }
Bundle bundle; Bundle bundle;
switch (menuItem.getItemId()) int itemId = menuItem.getItemId();
{ if (itemId == R.id.playlist_menu_pin) {
case R.id.playlist_menu_pin:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false); downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
break; } else if (itemId == R.id.playlist_menu_unpin) {
case R.id.playlist_menu_unpin:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true); downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
break; } else if (itemId == R.id.playlist_menu_download) {
case R.id.playlist_menu_download:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false); downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
break; } else if (itemId == R.id.playlist_menu_play_now) {
case R.id.playlist_menu_play_now:
bundle = new Bundle(); bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId()); bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName()); bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true); bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
break; } else if (itemId == R.id.playlist_menu_play_shuffled) {
case R.id.playlist_menu_play_shuffled:
bundle = new Bundle(); bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId()); bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName()); bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true); bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle); Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
break; } else if (itemId == R.id.playlist_menu_delete) {
case R.id.playlist_menu_delete:
deletePlaylist(playlist); deletePlaylist(playlist);
break; } else if (itemId == R.id.playlist_info) {
case R.id.playlist_info:
displayPlaylistInfo(playlist); displayPlaylistInfo(playlist);
break; } else if (itemId == R.id.playlist_update_info) {
case R.id.playlist_update_info:
updatePlaylistInfo(playlist); updatePlaylistInfo(playlist);
break; } else {
default:
return super.onContextItemSelected(menuItem); return super.onContextItemSelected(menuItem);
} }
return true; return true;
@ -333,7 +320,7 @@ public class PlaylistsFragment extends Fragment {
@Override @Override
protected void done(Void result) protected void done(Void result)
{ {
refresh(); load(true);
Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, playlist.getName())); Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, playlist.getName()));
} }
@ -352,20 +339,4 @@ public class PlaylistsFragment extends Fragment {
alertDialog.setNegativeButton(R.string.common_cancel, null); alertDialog.setNegativeButton(R.string.common_cancel, null);
alertDialog.show(); alertDialog.show();
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
refresh();
return null;
}
}
} }

View File

@ -27,6 +27,9 @@ import org.moire.ultrasonic.view.PodcastsChannelsAdapter;
import java.util.List; import java.util.List;
/**
* Displays the podcasts available on the server
*/
public class PodcastFragment extends Fragment { public class PodcastFragment extends Fragment {
private View emptyTextView; private View emptyTextView;

View File

@ -57,6 +57,9 @@ import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Initiates a search on the media library and displays the results
*/
public class SearchFragment extends Fragment { public class SearchFragment extends Fragment {
private static int DEFAULT_ARTISTS; private static int DEFAULT_ARTISTS;
@ -68,7 +71,7 @@ public class SearchFragment extends Fragment {
private View artistsHeading; private View artistsHeading;
private View albumsHeading; private View albumsHeading;
private View songsHeading; private View songsHeading;
private TextView searchButton; private TextView notFound;
private View moreArtistsButton; private View moreArtistsButton;
private View moreAlbumsButton; private View moreAlbumsButton;
private View moreSongsButton; private View moreSongsButton;
@ -114,14 +117,14 @@ public class SearchFragment extends Fragment {
DEFAULT_ALBUMS = Util.getDefaultAlbums(getContext()); DEFAULT_ALBUMS = Util.getDefaultAlbums(getContext());
DEFAULT_SONGS = Util.getDefaultSongs(getContext()); DEFAULT_SONGS = Util.getDefaultSongs(getContext());
View buttons = LayoutInflater.from(getContext()).inflate(R.layout.search_buttons, null); View buttons = LayoutInflater.from(getContext()).inflate(R.layout.search_buttons, list, false);
if (buttons != null) if (buttons != null)
{ {
artistsHeading = buttons.findViewById(R.id.search_artists); artistsHeading = buttons.findViewById(R.id.search_artists);
albumsHeading = buttons.findViewById(R.id.search_albums); albumsHeading = buttons.findViewById(R.id.search_albums);
songsHeading = buttons.findViewById(R.id.search_songs); songsHeading = buttons.findViewById(R.id.search_songs);
searchButton = buttons.findViewById(R.id.search_search); notFound = buttons.findViewById(R.id.search_not_found);
moreArtistsButton = buttons.findViewById(R.id.search_more_artists); moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
moreAlbumsButton = buttons.findViewById(R.id.search_more_albums); moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
moreSongsButton = buttons.findViewById(R.id.search_more_songs); moreSongsButton = buttons.findViewById(R.id.search_more_songs);
@ -168,7 +171,7 @@ public class SearchFragment extends Fragment {
} }
else else
{ {
onSongSelected(entry, false, true, true, false); onSongSelected(entry, true);
} }
} }
@ -262,14 +265,13 @@ public class SearchFragment extends Fragment {
boolean isArtist = selectedItem instanceof Artist; boolean isArtist = selectedItem instanceof Artist;
boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory(); boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory();
MenuInflater inflater = getActivity().getMenuInflater();
if (!isArtist && !isAlbum) if (!isArtist && !isAlbum)
{ {
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.select_song_context, menu); inflater.inflate(R.menu.select_song_context, menu);
} }
else else
{ {
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.select_album_context, menu); inflater.inflate(R.menu.select_album_context, menu);
} }
@ -485,7 +487,7 @@ public class SearchFragment extends Fragment {
} }
boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty(); boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty();
searchButton.setText(empty ? R.string.search_no_match : R.string.search_search); if (empty) mergeAdapter.addView(notFound, false);
} }
list.setAdapter(mergeAdapter); list.setAdapter(mergeAdapter);
@ -551,19 +553,19 @@ public class SearchFragment extends Fragment {
Navigation.findNavController(getView()).navigate(R.id.searchToSelectAlbum, bundle); Navigation.findNavController(getView()).navigate(R.id.searchToSelectAlbum, bundle);
} }
private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) private void onSongSelected(MusicDirectory.Entry song, boolean append)
{ {
MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue(); MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue();
if (mediaPlayerController != null) if (mediaPlayerController != null)
{ {
if (!append && !playNext) if (!append)
{ {
mediaPlayerController.clear(); mediaPlayerController.clear();
} }
mediaPlayerController.download(Collections.singletonList(song), save, false, playNext, false, false); mediaPlayerController.download(Collections.singletonList(song), false, false, false, false, false);
if (autoplay) if (true)
{ {
mediaPlayerController.play(mediaPlayerController.getPlaylistSize() - 1); mediaPlayerController.play(mediaPlayerController.getPlaylistSize() - 1);
} }
@ -581,7 +583,7 @@ public class SearchFragment extends Fragment {
{ {
if (!searchResult.getSongs().isEmpty()) if (!searchResult.getSongs().isEmpty())
{ {
onSongSelected(searchResult.getSongs().get(0), false, false, true, false); onSongSelected(searchResult.getSongs().get(0), false);
} }
else if (!searchResult.getAlbums().isEmpty()) else if (!searchResult.getAlbums().isEmpty())
{ {

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -57,6 +56,9 @@ import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays a group of playable media from the library, which can be an Album, a Playlist, etc.
*/
public class SelectAlbumFragment extends Fragment { public class SelectAlbumFragment extends Fragment {
public static final String allSongsId = "-1"; public static final String allSongsId = "-1";
@ -79,7 +81,7 @@ public class SelectAlbumFragment extends Fragment {
private MenuItem playAllButton; private MenuItem playAllButton;
private MenuItem shareButton; private MenuItem shareButton;
private boolean showHeader = true; private boolean showHeader = true;
private Random random = new java.security.SecureRandom(); private final Random random = new java.security.SecureRandom();
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class); private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class); private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
@ -115,7 +117,7 @@ public class SelectAlbumFragment extends Fragment {
{ {
@Override @Override
public void onRefresh() { public void onRefresh() {
new SelectAlbumFragment.GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); updateDisplay(true);
} }
}); });
@ -198,7 +200,7 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
public void onClick(View view) public void onClick(View view)
{ {
playNow(false, false); playNow(false);
} }
}); });
playNextButton.setOnClickListener(new View.OnClickListener() playNextButton.setOnClickListener(new View.OnClickListener()
@ -215,7 +217,7 @@ public class SelectAlbumFragment extends Fragment {
@Override @Override
public void onClick(View view) public void onClick(View view)
{ {
playNow(false, true); playNow(true);
} }
}); });
pinButton.setOnClickListener(new View.OnClickListener() pinButton.setOnClickListener(new View.OnClickListener()
@ -333,7 +335,7 @@ public class SelectAlbumFragment 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);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
@ -397,7 +399,7 @@ public class SelectAlbumFragment extends Fragment {
} else if (itemId == R.id.select_album_play_all) { } else if (itemId == R.id.select_album_play_all) {
playAll(); playAll();
} else if (itemId == R.id.menu_item_share) { } else if (itemId == R.id.menu_item_share) {
List<MusicDirectory.Entry> entries = new ArrayList<MusicDirectory.Entry>(1); List<MusicDirectory.Entry> entries = new ArrayList<>(1);
entries.add(entry); entries.add(entry);
shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken); shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken);
return true; return true;
@ -453,18 +455,18 @@ public class SelectAlbumFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
} }
private void playNow(final boolean shuffle, final boolean append) private void playNow(final boolean append)
{ {
List<MusicDirectory.Entry> selectedSongs = getSelectedSongs(albumListView); List<MusicDirectory.Entry> selectedSongs = getSelectedSongs(albumListView);
if (!selectedSongs.isEmpty()) if (!selectedSongs.isEmpty())
{ {
downloadHandler.getValue().download(this, append, false, !append, false, shuffle, selectedSongs); downloadHandler.getValue().download(this, append, false, !append, false, false, selectedSongs);
selectAll(false, false); selectAll(false, false);
} }
else else
{ {
playAll(shuffle, append); playAll(false, append);
} }
} }
@ -504,7 +506,7 @@ public class SelectAlbumFragment extends Fragment {
private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView) private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
{ {
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(10); List<MusicDirectory.Entry> songs = new ArrayList<>(10);
if (albumListView != null) if (albumListView != null)
{ {
@ -521,15 +523,6 @@ public class SelectAlbumFragment extends Fragment {
return songs; return songs;
} }
private void refresh()
{
getView().post(new Runnable() {
public void run() {
updateDisplay(true);
}
});
}
private void getMusicDirectory(final boolean refresh, 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);
@ -545,7 +538,7 @@ public class SelectAlbumFragment extends Fragment {
{ {
MusicDirectory musicDirectory = service.getMusicDirectory(parentId, name, refresh, getContext()); MusicDirectory musicDirectory = service.getMusicDirectory(parentId, name, refresh, getContext());
List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>(); List<MusicDirectory.Entry> songs = new LinkedList<>();
getSongsRecursively(musicDirectory, songs); getSongsRecursively(musicDirectory, songs);
for (MusicDirectory.Entry song : songs) for (MusicDirectory.Entry song : songs)
@ -672,7 +665,7 @@ public class SelectAlbumFragment extends Fragment {
{ {
MusicDirectory root = new MusicDirectory(); MusicDirectory root = new MusicDirectory();
Collection<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>(); Collection<MusicDirectory.Entry> songs = new LinkedList<>();
getSongsForArtist(parentId, songs); getSongsForArtist(parentId, songs);
for (MusicDirectory.Entry song : songs) for (MusicDirectory.Entry song : songs)
@ -1095,7 +1088,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()); boolean valid = musicService.isLicenseValid(getContext());
return new Pair<MusicDirectory, Boolean>(dir, valid); return new Pair<>(dir, valid);
} }
@Override @Override
@ -1275,20 +1268,4 @@ public class SelectAlbumFragment extends Fragment {
return header; return header;
} }
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
refresh();
return null;
}
}
} }

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment; package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -30,6 +29,9 @@ import java.util.List;
import timber.log.Timber; import timber.log.Timber;
/**
* Displays the available genres in the media library
*/
public class SelectGenreFragment extends Fragment { public class SelectGenreFragment extends Fragment {
private SwipeRefreshLayout refreshGenreListView; private SwipeRefreshLayout refreshGenreListView;
@ -60,7 +62,7 @@ public class SelectGenreFragment extends Fragment {
@Override @Override
public void onRefresh() public void onRefresh()
{ {
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); load(true);
} }
}); });
@ -93,21 +95,16 @@ public class SelectGenreFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
} }
private void refresh()
{
load(true);
}
private void load(final boolean refresh) private void load(final boolean refresh)
{ {
BackgroundTask<List<Genre>> task = new FragmentBackgroundTask<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()
{ {
MusicService musicService = MusicServiceFactory.getMusicService(getContext()); MusicService musicService = MusicServiceFactory.getMusicService(getContext());
List<Genre> genres = new ArrayList<Genre>(); List<Genre> genres = new ArrayList<>();
try try
{ {
@ -134,20 +131,4 @@ public class SelectGenreFragment extends Fragment {
}; };
task.execute(); task.execute();
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
refresh();
return null;
}
}
} }

View File

@ -21,6 +21,7 @@ import androidx.preference.PreferenceManager;
import timber.log.Timber; import timber.log.Timber;
import android.view.View; import android.view.View;
import org.jetbrains.annotations.NotNull;
import org.koin.java.KoinJavaComponent; import org.koin.java.KoinJavaComponent;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.featureflags.Feature; import org.moire.ultrasonic.featureflags.Feature;
@ -100,7 +101,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
} }
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
FragmentTitle.Companion.setTitle(this, R.string.menu_settings); FragmentTitle.Companion.setTitle(this, R.string.menu_settings);
@ -192,8 +193,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
} else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) { } else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) {
setDebugLogToFile(sharedPreferences.getBoolean(key, false)); setDebugLogToFile(sharedPreferences.getBoolean(key, false));
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) { } else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
if (sharedPreferences.getBoolean(key, false)) showArtistPicture.setEnabled(true); showArtistPicture.setEnabled(sharedPreferences.getBoolean(key, false));
else showArtistPicture.setEnabled(false);
} else if (Constants.PREFERENCES_KEY_THEME.equals(key)) { } else if (Constants.PREFERENCES_KEY_THEME.equals(key)) {
themeChangedEventDistributor.getValue().RaiseThemeChangedEvent(); themeChangedEventDistributor.getValue().RaiseThemeChangedEvent();
} }
@ -214,7 +214,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (dialogFragment != null) if (dialogFragment != null)
{ {
dialogFragment.setTargetFragment(this, 0); dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(this.getFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG"); dialogFragment.show(this.getParentFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
} }
else else
{ {
@ -465,8 +465,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
debugLogToFile.setSummary(""); debugLogToFile.setSummary("");
} }
if (Util.getShouldUseId3Tags(getActivity())) showArtistPicture.setEnabled(true); showArtistPicture.setEnabled(Util.getShouldUseId3Tags(getActivity()));
else showArtistPicture.setEnabled(false);
} }
private void setImageLoaderConcurrency(int concurrency) { private void setImageLoaderConcurrency(int concurrency) {
@ -503,11 +502,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
} }
private void setBluetoothPreferences(boolean enabled) { private void setBluetoothPreferences(boolean enabled) {
if (enabled) { sendBluetoothAlbumArt.setEnabled(enabled);
sendBluetoothAlbumArt.setEnabled(true);
} else {
sendBluetoothAlbumArt.setEnabled(false);
}
} }
private void setCacheLocation(String path) { private void setCacheLocation(String path) {

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.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.Spannable; import android.text.Spannable;
@ -28,6 +27,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.api.subsonic.ApiNotSupportedException; import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.Share;
@ -51,6 +51,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject; import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays the shares in the media library
*/
public class SharesFragment extends Fragment { public class SharesFragment extends Fragment {
private SwipeRefreshLayout refreshSharesListView; private SwipeRefreshLayout refreshSharesListView;
@ -85,7 +88,7 @@ public class SharesFragment extends Fragment {
@Override @Override
public void onRefresh() public void onRefresh()
{ {
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); load(true);
} }
}); });
@ -118,11 +121,6 @@ public class SharesFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
} }
private void refresh()
{
load(true);
}
private void load(final boolean refresh) private void load(final boolean refresh)
{ {
BackgroundTask<List<Share>> task = new FragmentBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken) BackgroundTask<List<Share>> task = new FragmentBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken)
@ -145,7 +143,7 @@ public class SharesFragment 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);
@ -311,7 +309,7 @@ public class SharesFragment extends Fragment {
@Override @Override
protected void done(Void result) protected void done(Void result)
{ {
refresh(); load(true);
Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, share.getName())); Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, share.getName()));
} }
@ -330,20 +328,4 @@ public class SharesFragment extends Fragment {
alertDialog.setNegativeButton(R.string.common_cancel, null); alertDialog.setNegativeButton(R.string.common_cancel, null);
alertDialog.show(); alertDialog.show();
} }
private class GetDataTask extends AsyncTask<Void, Void, String[]>
{
@Override
protected void onPostExecute(String[] result)
{
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params)
{
refresh();
return null;
}
}
} }

View File

@ -21,6 +21,9 @@ import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.FileUtil; import org.moire.ultrasonic.util.FileUtil;
/**
* Widget Provider for the Ultrasonic Widgets
*/
public class UltrasonicAppWidgetProvider extends AppWidgetProvider public class UltrasonicAppWidgetProvider extends AppWidgetProvider
{ {
protected int layoutId; protected int layoutId;
@ -73,7 +76,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
{ {
if (hasInstances(context)) if (hasInstances(context))
{ {
performUpdate(context, currentSong, null, playing, setAlbum); performUpdate(context, currentSong, playing, setAlbum);
} }
} }
@ -96,7 +99,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
/** /**
* Update all active widget instances by pushing changes * Update all active widget instances by pushing changes
*/ */
private void performUpdate(Context context, MusicDirectory.Entry currentSong, int[] appWidgetIds, boolean playing, boolean setAlbum) private void performUpdate(Context context, MusicDirectory.Entry currentSong, boolean playing, boolean setAlbum)
{ {
final Resources res = context.getResources(); final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId); final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);
@ -177,7 +180,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
// Link actions buttons to intents // Link actions buttons to intents
linkButtons(context, views, currentSong != null); linkButtons(context, views, currentSong != null);
pushUpdate(context, appWidgetIds, views); pushUpdate(context, null, views);
} }
/** /**
@ -185,7 +188,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
*/ */
private static void linkButtons(Context context, RemoteViews views, boolean playerActive) private static void linkButtons(Context context, RemoteViews views, boolean playerActive)
{ {
Intent intent = new Intent(context, NavigationActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);; Intent intent = new Intent(context, NavigationActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (playerActive) if (playerActive)
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHOW_PLAYER, true); intent.putExtra(Constants.INTENT_EXTRA_NAME_SHOW_PLAYER, true);

View File

@ -67,13 +67,13 @@ public class CachedMusicService implements MusicService
private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedArtist; private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedArtist;
private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedAlbum; private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedAlbum;
private final LRUCache<String, TimeLimitedCache<UserInfo>> cachedUserInfo; private final LRUCache<String, TimeLimitedCache<UserInfo>> cachedUserInfo;
private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS); private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<>(120, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedArtists = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache<Indexes> cachedArtists = new TimeLimitedCache<>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<PodcastsChannel>> cachedPodcastsChannels = new TimeLimitedCache<List<PodcastsChannel>>(3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<PodcastsChannel>> cachedPodcastsChannels = new TimeLimitedCache<>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<>(10 * 3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS); private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<>(10 * 3600, TimeUnit.SECONDS);
private String restUrl; private String restUrl;

View File

@ -196,7 +196,9 @@ public class DownloadFile
{ {
if (saveFile.exists()) if (saveFile.exists())
{ {
saveFile.renameTo(completeFile); if (!saveFile.renameTo(completeFile)){
Timber.w("Renaming file failed. Original file: %s; Rename to: %s", saveFile.getName(), completeFile.getName());
}
} }
} }

View File

@ -110,7 +110,8 @@ public interface MusicService
*/ */
Pair<InputStream, Boolean> getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception; Pair<InputStream, Boolean> getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception;
@Deprecated String getVideoUrl(Context context, String id, boolean useFlash) throws Exception; // TODO: Refactor and remove this call (see RestMusicService implementation)
String getVideoUrl(Context context, String id, boolean useFlash) throws Exception;
JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context) throws Exception; JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context) throws Exception;

View File

@ -44,7 +44,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.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 java.io.BufferedReader; import java.io.BufferedReader;
@ -509,7 +508,7 @@ public class OfflineMusicService implements MusicService
@Override @Override
public List<Playlist> getPlaylists(boolean refresh, Context context) public List<Playlist> getPlaylists(boolean refresh, Context context)
{ {
List<Playlist> playlists = new ArrayList<Playlist>(); List<Playlist> playlists = new ArrayList<>();
File root = FileUtil.getPlaylistDirectory(context); File root = FileUtil.getPlaylistDirectory(context);
String lastServer = null; String lastServer = null;
boolean removeServer = true; boolean removeServer = true;
@ -544,11 +543,13 @@ public class OfflineMusicService implements MusicService
// Delete legacy playlist files // Delete legacy playlist files
try try
{ {
folder.delete(); if (!folder.delete()) {
Timber.w("Failed to delete old playlist file: %s", folder.getName());
}
} }
catch (Exception e) catch (Exception e)
{ {
Timber.w("Failed to delete old playlist file: %s", folder.getName()); Timber.w(e, "Failed to delete old playlist file: %s", folder.getName());
} }
} }
} }

View File

@ -69,14 +69,9 @@ public final class Constants
public static final String CMD_PREVIOUS = "org.moire.ultrasonic.CMD_PREVIOUS"; public static final String CMD_PREVIOUS = "org.moire.ultrasonic.CMD_PREVIOUS";
public static final String CMD_NEXT = "org.moire.ultrasonic.CMD_NEXT"; public static final String CMD_NEXT = "org.moire.ultrasonic.CMD_NEXT";
// Notification IDs.
public static final int NOTIFICATION_ID_PLAYING = 100;
// Preferences keys. // Preferences keys.
public static final String PREFERENCES_KEY_SERVER_INSTANCE = "serverInstanceId"; public static final String PREFERENCES_KEY_SERVER_INSTANCE = "serverInstanceId";
public static final String PREFERENCES_KEY_SERVERS_KEY = "serversKey";
public static final String PREFERENCES_KEY_SERVERS_EDIT = "editServers"; public static final String PREFERENCES_KEY_SERVERS_EDIT = "editServers";
public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime";
public static final String PREFERENCES_KEY_THEME = "theme"; public static final String PREFERENCES_KEY_THEME = "theme";
public static final String PREFERENCES_KEY_THEME_LIGHT = "light"; public static final String PREFERENCES_KEY_THEME_LIGHT = "light";
public static final String PREFERENCES_KEY_THEME_DARK = "dark"; public static final String PREFERENCES_KEY_THEME_DARK = "dark";
@ -145,12 +140,6 @@ public final class Constants
public static final int PREFERENCE_VALUE_A2DP = 1; public static final int PREFERENCE_VALUE_A2DP = 1;
public static final int PREFERENCE_VALUE_DISABLED = 2; public static final int PREFERENCE_VALUE_DISABLED = 2;
// Number of free trial days for non-licensed servers.
public static final int FREE_TRIAL_DAYS = 30;
// URL for project donations.
public static final String DONATION_URL = "http://www.subsonic.org/pages/premium.jsp";
public static final String FILENAME_DOWNLOADS_SER = "downloadstate.ser"; public static final String FILENAME_DOWNLOADS_SER = "downloadstate.ser";
public static final String ALBUM_ART_FILE = "folder.jpeg"; public static final String ALBUM_ART_FILE = "folder.jpeg";

View File

@ -156,9 +156,7 @@ public class FileUtil
File avatarFile = getAvatarFile(context, username); File avatarFile = getAvatarFile(context, username);
Bitmap bitmap = null; Bitmap bitmap = null;
ImageLoader imageLoader = null; ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
imageLoader = imageLoaderProvider.getValue().getImageLoader();
if (imageLoader != null) if (imageLoader != null)
{ {
@ -222,9 +220,7 @@ public class FileUtil
File albumArtFile = getAlbumArtFile(context, entry); File albumArtFile = getAlbumArtFile(context, entry);
Bitmap bitmap = null; Bitmap bitmap = null;
ImageLoader imageLoader = null; ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
imageLoader = imageLoaderProvider.getValue().getImageLoader();
if (imageLoader != null) if (imageLoader != null)
{ {
@ -491,10 +487,10 @@ public class FileUtil
if (files == null) if (files == null)
{ {
Timber.w("Failed to list children for %s", dir.getPath()); Timber.w("Failed to list children for %s", dir.getPath());
return new TreeSet<File>(); return new TreeSet<>();
} }
return new TreeSet<File>(Arrays.asList(files)); return new TreeSet<>(Arrays.asList(files));
} }
public static SortedSet<File> listMediaFiles(File dir) public static SortedSet<File> listMediaFiles(File dir)

View File

@ -40,6 +40,7 @@ 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.Locale;
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;
@ -100,7 +101,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
threads = Collections.synchronizedCollection(new ArrayList<Thread>(this.concurrency)); threads = Collections.synchronizedCollection(new ArrayList<Thread>(this.concurrency));
for (int i = 0; i < this.concurrency; i++) { for (int i = 0; i < this.concurrency; i++) {
Thread thread = new Thread(this, String.format("ImageLoader_%d", i)); Thread thread = new Thread(this, String.format(Locale.US, "ImageLoader_%d", i));
threads.add(thread); threads.add(thread);
thread.start(); thread.start();
} }
@ -213,7 +214,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
} }
private static String getKey(String coverArtId, int size) { private static String getKey(String coverArtId, int size) {
return String.format("%s:%d", coverArtId, size); return String.format(Locale.US, "%s:%d", coverArtId, size);
} }
@Override @Override
@ -338,10 +339,6 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
setAvatarImageBitmap(view, null, unknownAvatarImage, false); setAvatarImageBitmap(view, null, unknownAvatarImage, false);
} }
private void setUnknownImage(View view, boolean large) {
setUnknownImage(view, large, -1);
}
private void setUnknownImage(View view, boolean large, int resId) { private void setUnknownImage(View view, boolean large, int resId) {
if (resId == -1) resId = R.drawable.unknown_album; if (resId == -1) resId = R.drawable.unknown_album;
if (large) { if (large) {

View File

@ -1,8 +1,6 @@
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
/** /**

View File

@ -33,7 +33,7 @@ import static androidx.core.content.PermissionChecker.PERMISSION_DENIED;
public class PermissionUtil { public class PermissionUtil {
private Context activityContext; private Context activityContext;
private Context applicationContext; private final Context applicationContext;
public PermissionUtil(Context context) { public PermissionUtil(Context context) {
applicationContext = context; applicationContext = context;

View File

@ -2,14 +2,9 @@ package org.moire.ultrasonic.util;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import androidx.preference.DialogPreference; import androidx.preference.DialogPreference;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import java.util.regex.Pattern;
/** /**
* Created by Joshua Bahnsen on 12/22/13. * Created by Joshua Bahnsen on 12/22/13.
*/ */

View File

@ -1,17 +1,15 @@
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.preference.DialogPreference; import androidx.preference.DialogPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceDialogFragmentCompat; import androidx.preference.PreferenceDialogFragmentCompat;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import java.util.Locale;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -72,7 +70,7 @@ public class TimeSpanPreferenceDialogFragmentCompat extends PreferenceDialogFrag
{ {
String tsType = picker.getTimeSpanType(); String tsType = picker.getTimeSpanType();
persisted = String.format("%d:%s", tsAmount, tsType); persisted = String.format(Locale.US, "%d:%s", tsAmount, tsType);
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -44,6 +45,8 @@ import timber.log.Timber;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.Toast; import android.widget.Toast;
@ -61,10 +64,8 @@ import org.moire.ultrasonic.service.DownloadFile;
import java.io.*; import java.io.*;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -93,8 +94,6 @@ public class Util
private static boolean mediaButtonsRegisteredForUI; private static boolean mediaButtonsRegisteredForUI;
private static boolean mediaButtonsRegisteredForService; private static boolean mediaButtonsRegisteredForService;
private static final Map<Integer, Version> SERVER_REST_VERSIONS = new ConcurrentHashMap<Integer, Version>();
// Used by hexEncode() // Used by hexEncode()
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static Toast toast; private static Toast toast;
@ -122,7 +121,7 @@ public class Util
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_REPEAT_MODE, repeatMode.name()); editor.putString(Constants.PREFERENCES_KEY_REPEAT_MODE, repeatMode.name());
editor.commit(); editor.apply();
} }
public static boolean isNotificationEnabled(Context context) public static boolean isNotificationEnabled(Context context)
@ -141,6 +140,7 @@ public class Util
return preferences.getBoolean(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION, false); return preferences.getBoolean(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION, false);
} }
@SuppressWarnings({"BooleanMethodIsAlwaysInverted"}) // It is inverted for readability
public static boolean isLockScreenEnabled(Context context) public static boolean isLockScreenEnabled(Context context)
{ {
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
@ -205,25 +205,6 @@ public class Util
return PreferenceManager.getDefaultSharedPreferences(context); return PreferenceManager.getDefaultSharedPreferences(context);
} }
public static int getRemainingTrialDays(Context context)
{
SharedPreferences preferences = getPreferences(context);
long installTime = preferences.getLong(Constants.PREFERENCES_KEY_INSTALL_TIME, 0L);
if (installTime == 0L)
{
installTime = System.currentTimeMillis();
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(Constants.PREFERENCES_KEY_INSTALL_TIME, installTime);
editor.commit();
}
long now = System.currentTimeMillis();
long millisPerDay = 24L * 60L * 60L * 1000L;
int daysSinceInstall = (int) ((now - installTime) / millisPerDay);
return Math.max(0, Constants.FREE_TRIAL_DAYS - daysSinceInstall);
}
/** /**
* Get the contents of an <code>InputStream</code> as a <code>byte[]</code>. * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
* <p/> * <p/>
@ -346,6 +327,7 @@ public class Util
toast(context, message, true); toast(context, message, true);
} }
@SuppressLint("ShowToast") // Invalid warning
public static void toast(Context context, CharSequence message, boolean shortDuration) public static void toast(Context context, CharSequence message, boolean shortDuration)
{ {
if (toast == null) if (toast == null)
@ -381,22 +363,19 @@ public class Util
// More than 1 GB? // More than 1 GB?
if (byteCount >= 1024 * 1024 * 1024) if (byteCount >= 1024 * 1024 * 1024)
{ {
NumberFormat gigaByteFormat = GIGA_BYTE_FORMAT; return GIGA_BYTE_FORMAT.format((double) byteCount / (1024 * 1024 * 1024));
return gigaByteFormat.format((double) byteCount / (1024 * 1024 * 1024));
} }
// More than 1 MB? // More than 1 MB?
if (byteCount >= 1024 * 1024) if (byteCount >= 1024 * 1024)
{ {
NumberFormat megaByteFormat = MEGA_BYTE_FORMAT; return MEGA_BYTE_FORMAT.format((double) byteCount / (1024 * 1024));
return megaByteFormat.format((double) byteCount / (1024 * 1024));
} }
// More than 1 KB? // More than 1 KB?
if (byteCount >= 1024) if (byteCount >= 1024)
{ {
NumberFormat kiloByteFormat = KILO_BYTE_FORMAT; return KILO_BYTE_FORMAT.format((double) byteCount / 1024);
return kiloByteFormat.format((double) byteCount / 1024);
} }
return byteCount + " B"; return byteCount + " B";
@ -627,11 +606,6 @@ public class Util
} }
} }
public static void disablePendingTransition(Activity activity)
{
activity.overridePendingTransition(0, 0);
}
public static Drawable getDrawableFromAttribute(Context context, int attr) public static Drawable getDrawableFromAttribute(Context context, int attr)
{ {
int[] attrs = new int[]{attr}; int[] attrs = new int[]{attr};
@ -667,7 +641,7 @@ public class Util
public static WifiManager.WifiLock createWifiLock(Context context, String tag) public static WifiManager.WifiLock createWifiLock(Context context, String tag)
{ {
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
return wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, tag); return wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, tag);
} }
@ -902,11 +876,7 @@ public class Util
avrcpIntent.putExtra("playing", true); avrcpIntent.putExtra("playing", true);
break; break;
case STOPPED: case STOPPED:
avrcpIntent.putExtra("playing", false);
break;
case PAUSED: case PAUSED:
avrcpIntent.putExtra("playing", false);
break;
case COMPLETED: case COMPLETED:
avrcpIntent.putExtra("playing", false); avrcpIntent.putExtra("playing", false);
break; break;
@ -1011,7 +981,7 @@ public class Util
// guarantee // guarantee
// a final image with both dimensions larger than or equal to the // a final image with both dimensions larger than or equal to the
// requested height and width. // requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; inSampleSize = Math.min(heightRatio, widthRatio);
} }
return inSampleSize; return inSampleSize;
@ -1085,6 +1055,7 @@ public class Util
views.setOnClickPendingIntent(R.id.notification_five_star_5, pendingIntent); views.setOnClickPendingIntent(R.id.notification_five_star_5, pendingIntent);
} }
// TODO: Shouldn't this be used when making requests?
public static int getNetworkTimeout(Context context) public static int getNetworkTimeout(Context context)
{ {
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
@ -1190,6 +1161,7 @@ public class Util
return Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME, "300")); return Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME, "300"));
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted") // Inverted for readability
public static boolean isNullOrWhiteSpace(String string) public static boolean isNullOrWhiteSpace(String string)
{ {
return string == null || string.isEmpty() || string.trim().isEmpty(); return string == null || string.isEmpty() || string.trim().isEmpty();
@ -1239,18 +1211,18 @@ public class Util
if (hours >= 10) if (hours >= 10)
{ {
return String.format("%02d:%02d:%02d", hours, minutes, seconds); return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds);
} }
else if (hours > 0) else if (hours > 0)
{ {
return String.format("%d:%02d:%02d", hours, minutes, seconds); return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds);
} }
else if (minutes >= 10) else if (minutes >= 10)
{ {
return String.format("%02d:%02d", minutes, seconds); return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
} }
else return minutes > 0 ? String.format("%d:%02d", minutes, seconds) : String.format("0:%02d", seconds); else return minutes > 0 ? String.format(Locale.getDefault(), "%d:%02d", minutes, seconds) : String.format(Locale.getDefault(), "0:%02d", seconds);
} }
public static VideoPlayerType getVideoPlayerType(Context context) public static VideoPlayerType getVideoPlayerType(Context context)
@ -1329,6 +1301,7 @@ public class Util
return versionCode; return versionCode;
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted") // Inverted for readability
public static boolean getShouldSendBluetoothNotifications(Context context) public static boolean getShouldSendBluetoothNotifications(Context context)
{ {
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
@ -1396,7 +1369,7 @@ public class Util
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(Constants.PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS, shouldAskForShareDetails); editor.putBoolean(Constants.PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS, shouldAskForShareDetails);
editor.commit(); editor.apply();
} }
public static void setDefaultShareExpiration(Context context, String defaultShareExpiration) public static void setDefaultShareExpiration(Context context, String defaultShareExpiration)
@ -1404,7 +1377,7 @@ public class Util
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION, defaultShareExpiration); editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION, defaultShareExpiration);
editor.commit(); editor.apply();
} }
public static void setDefaultShareDescription(Context context, String defaultShareDescription) public static void setDefaultShareDescription(Context context, String defaultShareDescription)
@ -1412,7 +1385,7 @@ public class Util
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, defaultShareDescription); editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, defaultShareDescription);
editor.commit(); editor.apply();
} }
public static boolean getShouldShowAllSongsByArtist(Context context) public static boolean getShouldShowAllSongsByArtist(Context context)
@ -1485,4 +1458,12 @@ public class Util
return preferences.getBoolean(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE, false); return preferences.getBoolean(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE, false);
} }
public static void hideKeyboard(Activity activity) {
InputMethodManager inputManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
View currentFocusedView = activity.getCurrentFocus();
if (currentFocusedView != null) {
inputManager.hideSoftInputFromWindow(currentFocusedView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
} }

View File

@ -18,7 +18,6 @@
*/ */
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;

View File

@ -97,11 +97,7 @@ public class AlbumView extends UpdateView
} }
public void maximizeOrMinimize() { public void maximizeOrMinimize() {
if (maximized) { maximized = !maximized;
maximized = false;
} else {
maximized = true;
}
if (this.viewHolder.title != null) { if (this.viewHolder.title != null) {
this.viewHolder.title.setSingleLine(!maximized); this.viewHolder.title.setSingleLine(!maximized);
} }

View File

@ -27,13 +27,13 @@ import static org.koin.java.KoinJavaComponent.inject;
public class ChatAdapter extends ArrayAdapter<ChatMessage> public class ChatAdapter extends ArrayAdapter<ChatMessage>
{ {
private final Context context; private final Context context;
private List<ChatMessage> messages; private final List<ChatMessage> messages;
private static final String phoneRegex = "1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})"; private static final String phoneRegex = "1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})";
private static final Pattern phoneMatcher = Pattern.compile(phoneRegex); private static final Pattern phoneMatcher = Pattern.compile(phoneRegex);
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class); private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
private Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class); private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
public ChatAdapter(Context context, List<ChatMessage> messages) public ChatAdapter(Context context, List<ChatMessage> messages)
{ {

View File

@ -59,7 +59,7 @@ public class EntryAdapter extends ArrayAdapter<Entry>
{ {
AlbumView view; AlbumView view;
if (convertView != null && convertView instanceof AlbumView) if (convertView instanceof AlbumView)
{ {
AlbumView currentView = (AlbumView) convertView; AlbumView currentView = (AlbumView) convertView;
@ -87,7 +87,7 @@ public class EntryAdapter extends ArrayAdapter<Entry>
{ {
SongView view; SongView view;
if (convertView != null && convertView instanceof SongView) if (convertView instanceof SongView)
{ {
SongView currentView = (SongView) convertView; SongView currentView = (SongView) convertView;

View File

@ -9,9 +9,6 @@ import android.widget.TextView;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.Playlist; import org.moire.ultrasonic.domain.Playlist;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
@ -34,7 +31,7 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist>
Playlist entry = getItem(position); Playlist entry = getItem(position);
PlaylistView view; PlaylistView view;
if (convertView != null && convertView instanceof PlaylistView) if (convertView instanceof PlaylistView)
{ {
PlaylistView currentView = (PlaylistView) convertView; PlaylistView currentView = (PlaylistView) convertView;
@ -52,23 +49,6 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist>
return view; return view;
} }
public static class PlaylistComparator implements Comparator<Playlist>, Serializable
{
private static final long serialVersionUID = -6201663557439120008L;
@Override
public int compare(Playlist playlist1, Playlist playlist2)
{
return playlist1.getName().compareToIgnoreCase(playlist2.getName());
}
public static List<Playlist> sort(List<Playlist> playlists)
{
Collections.sort(playlists, new PlaylistComparator());
return playlists;
}
}
static class ViewHolder static class ViewHolder
{ {
TextView name; TextView name;

View File

@ -9,9 +9,6 @@ import android.widget.TextView;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.Share;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
@ -34,7 +31,7 @@ public class ShareAdapter extends ArrayAdapter<Share>
Share entry = getItem(position); Share entry = getItem(position);
ShareView view; ShareView view;
if (convertView != null && convertView instanceof ShareView) if (convertView instanceof ShareView)
{ {
ShareView currentView = (ShareView) convertView; ShareView currentView = (ShareView) convertView;
@ -52,23 +49,6 @@ public class ShareAdapter extends ArrayAdapter<Share>
return view; return view;
} }
public static class ShareComparator implements Comparator<Share>, Serializable
{
private static final long serialVersionUID = -7169409928471418921L;
@Override
public int compare(Share share1, Share share2)
{
return share1.getId().compareToIgnoreCase(share2.getId());
}
public static List<Share> sort(List<Share> shares)
{
Collections.sort(shares, new ShareComparator());
return shares;
}
}
static class ViewHolder static class ViewHolder
{ {
TextView url; TextView url;

View File

@ -32,6 +32,7 @@ import org.koin.android.viewmodel.ext.android.viewModel
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.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.fragment.OnBackPressedHandler
import org.moire.ultrasonic.fragment.ServerSettingsModel import org.moire.ultrasonic.fragment.ServerSettingsModel
import org.moire.ultrasonic.provider.SearchSuggestionProvider import org.moire.ultrasonic.provider.SearchSuggestionProvider
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
@ -53,14 +54,15 @@ 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
*/ */
class NavigationActivity : AppCompatActivity() { class NavigationActivity : AppCompatActivity() {
var chatMenuItem: MenuItem? = null private var chatMenuItem: MenuItem? = null
var bookmarksMenuItem: MenuItem? = null private var bookmarksMenuItem: MenuItem? = null
var sharesMenuItem: MenuItem? = null private var sharesMenuItem: MenuItem? = null
var podcastsMenuItem: MenuItem? = null private var podcastsMenuItem: MenuItem? = null
var nowPlayingView: FragmentContainerView? = null private var nowPlayingView: FragmentContainerView? = null
var nowPlayingHidden = false private var nowPlayingHidden = false
var navigationView: NavigationView? = null private var navigationView: NavigationView? = null
var drawerLayout: DrawerLayout? = null private var drawerLayout: DrawerLayout? = null
private var host: NavHostFragment? = null
private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var nowPlayingEventListener: NowPlayingEventListener private lateinit var nowPlayingEventListener: NowPlayingEventListener
@ -93,10 +95,10 @@ class NavigationActivity : AppCompatActivity() {
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
val host: NavHostFragment = supportFragmentManager host = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController val navController = host!!.navController
appBarConfiguration = AppBarConfiguration( appBarConfiguration = AppBarConfiguration(
setOf( setOf(
@ -123,7 +125,7 @@ class NavigationActivity : AppCompatActivity() {
val dest: String = try { val dest: String = try {
resources.getResourceName(destination.id) resources.getResourceName(destination.id)
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
Integer.toString(destination.id) destination.id.toString()
} }
Timber.d("Navigated to $dest") Timber.d("Navigated to $dest")
@ -209,7 +211,7 @@ class NavigationActivity : AppCompatActivity() {
navigationView?.setupWithNavController(navController) navigationView?.setupWithNavController(navController)
// The exit menu is handled here manually // The exit menu is handled here manually
val exitItem: MenuItem? = navigationView?.menu?.findItem(R.id.menu_exit) ?: null val exitItem: MenuItem? = navigationView?.menu?.findItem(R.id.menu_exit)
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)
@ -235,7 +237,9 @@ class NavigationActivity : AppCompatActivity() {
if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) { if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) {
this.drawerLayout?.closeDrawer(GravityCompat.START) this.drawerLayout?.closeDrawer(GravityCompat.START)
} else { } else {
super.onBackPressed() val currentFragment = host!!.childFragmentManager.fragments.last()
if (currentFragment is OnBackPressedHandler) currentFragment.onBackPressed()
else super.onBackPressed()
} }
} }
@ -254,7 +258,13 @@ class NavigationActivity : AppCompatActivity() {
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration) val currentFragment = host!!.childFragmentManager.fragments.last()
return if (currentFragment is OnBackPressedHandler) {
currentFragment.onBackPressed()
true
} else {
findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
}
} }
// TODO Test if this works with external Intents // TODO Test if this works with external Intents

View File

@ -18,6 +18,10 @@ import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
import timber.log.Timber.DebugTree import timber.log.Timber.DebugTree
/**
* The Main class of the Application
*/
@Suppress("unused")
class UApp : MultiDexApplication() { class UApp : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()

View File

@ -30,7 +30,7 @@ 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
class EditServerFragment : Fragment() { class EditServerFragment : Fragment(), OnBackPressedHandler {
companion object { companion object {
const val EDIT_SERVER_INTENT_INDEX = "index" const val EDIT_SERVER_INTENT_INDEX = "index"
} }
@ -141,6 +141,10 @@ class EditServerFragment : Fragment() {
} }
} }
override fun onBackPressed() {
finishActivity()
}
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
savedInstanceState.putString( savedInstanceState.putString(
::serverNameEditText.name, serverNameEditText!!.editText?.text.toString() ::serverNameEditText.name, serverNameEditText!!.editText?.text.toString()
@ -269,10 +273,10 @@ class EditServerFragment : Fragment() {
*/ */
private fun areFieldsChanged(): Boolean { private fun areFieldsChanged(): Boolean {
if (currentServerSetting == null || currentServerSetting!!.id == -1) { if (currentServerSetting == null || currentServerSetting!!.id == -1) {
return !serverNameEditText!!.editText?.text!!.isBlank() || return serverNameEditText!!.editText?.text!!.isNotBlank() ||
serverAddressEditText!!.editText?.text.toString() != "http://" || serverAddressEditText!!.editText?.text.toString() != "http://" ||
!userNameEditText!!.editText?.text!!.isBlank() || userNameEditText!!.editText?.text!!.isNotBlank() ||
!passwordEditText!!.editText?.text!!.isBlank() passwordEditText!!.editText?.text!!.isNotBlank()
} }
return currentServerSetting!!.name != serverNameEditText!!.editText?.text.toString() || return currentServerSetting!!.name != serverNameEditText!!.editText?.text.toString() ||
@ -363,6 +367,7 @@ class EditServerFragment : Fragment() {
.setMessage(R.string.server_editor_leave_confirmation) .setMessage(R.string.server_editor_leave_confirmation)
.setPositiveButton(R.string.common_ok) { dialog, _ -> .setPositiveButton(R.string.common_ok) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
Util.hideKeyboard(activity)
findNavController().navigateUp() findNavController().navigateUp()
} }
.setNegativeButton(R.string.common_cancel) { dialog, _ -> .setNegativeButton(R.string.common_cancel) { dialog, _ ->
@ -370,6 +375,7 @@ class EditServerFragment : Fragment() {
} }
.show() .show()
} else { } else {
Util.hideKeyboard(activity)
findNavController().navigateUp() findNavController().navigateUp()
} }
} }

View File

@ -0,0 +1,8 @@
package org.moire.ultrasonic.fragment
/**
* Interface for fragments handling their own Back button
*/
interface OnBackPressedHandler {
fun onBackPressed()
}

View File

@ -30,7 +30,7 @@ import org.moire.ultrasonic.di.OFFLINE_MUSIC_SERVICE
import org.moire.ultrasonic.di.ONLINE_MUSIC_SERVICE import org.moire.ultrasonic.di.ONLINE_MUSIC_SERVICE
import org.moire.ultrasonic.di.musicServiceModule import org.moire.ultrasonic.di.musicServiceModule
@Deprecated("Use DI way to get MusicService") // TODO Refactor everywhere to use DI way to get MusicService, and then remove this class
object MusicServiceFactory : KoinComponent { object MusicServiceFactory : KoinComponent {
@JvmStatic @JvmStatic
fun getMusicService(context: Context): MusicService { fun getMusicService(context: Context): MusicService {

View File

@ -631,7 +631,7 @@ open class RESTMusicService(
id: String, id: String,
useFlash: Boolean useFlash: Boolean
): String { ): String {
// This method should not exists as video should be loaded using stream method // TODO This method should not exists as video should be loaded using stream method
// Previous method implementation uses assumption that video will be available // Previous method implementation uses assumption that video will be available
// by videoPlayer.view?id=<id>&maxBitRate=500&autoplay=true, but this url is not // by videoPlayer.view?id=<id>&maxBitRate=500&autoplay=true, but this url is not
// official Subsonic API call. // official Subsonic API call.

View File

@ -5,12 +5,11 @@
a:layout_height="wrap_content"> a:layout_height="wrap_content">
<TextView <TextView
a:id="@+id/search_search" a:id="@+id/search_not_found"
a:text="@string/search.search" a:text="@string/search.no_match"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:drawablePadding="0dp" a:drawablePadding="0dp"
a:drawableLeft="?attr/search"
a:textAppearance="?android:attr/textAppearanceMedium" a:textAppearance="?android:attr/textAppearanceMedium"
a:gravity="center" a:gravity="center"
a:padding="12dp"/> a:padding="12dp"/>