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
{
private static Boolean available = null;
private static MutableLiveData<EqualizerController> instance = new MutableLiveData<>();
private static final MutableLiveData<EqualizerController> instance = new MutableLiveData<>();
private Context context;
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 Boolean available = null;
private static MutableLiveData<VisualizerController> instance = new MutableLiveData<>();
private static final MutableLiveData<VisualizerController> instance = new MutableLiveData<>();
public Visualizer visualizer;
private int audioSessionId;

View File

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

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -38,6 +37,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject;
/**
* Lists the Bookmarks available on the server
*/
public class BookmarksFragment extends Fragment {
private SwipeRefreshLayout refreshAlbumListView;
@ -82,7 +84,8 @@ public class BookmarksFragment extends Fragment {
@Override
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)
{
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(10);
List<MusicDirectory.Entry> songs = new ArrayList<>(10);
if (albumListView != null)
{
@ -236,12 +239,6 @@ public class BookmarksFragment extends Fragment {
return songs;
}
private void refresh()
{
enableButtons();
getBookmarks();
}
private void selectAllOrNone()
{
boolean someUnselected = false;
@ -436,20 +433,4 @@ public class BookmarksFragment extends Fragment {
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;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
@ -44,6 +43,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject;
/**
* Provides online chat functionality
*/
public class ChatFragment extends Fragment {
private ListView chatListView;
@ -51,7 +53,7 @@ public class ChatFragment extends Fragment {
private ImageButton sendButton;
private Timer timer;
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 SwipeRefreshLayout swipeRefresh;
@ -153,12 +155,11 @@ public class ChatFragment extends Fragment {
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Check if user triggered a refresh:
case R.id.menu_refresh:
// Start the refresh background task.
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return true;
// Check if user triggered a refresh:
if (item.getItemId() == R.id.menu_refresh) {
// Start the refresh background task.
load();
return true;
}
// User didn't trigger a refresh, let the superclass handle this action
return super.onOptionsItemSelected(item);
@ -313,20 +314,4 @@ public class ChatFragment extends Fragment {
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 java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import timber.log.Timber;
/**
* Displays the Equalizer
*/
public class EqualizerFragment extends Fragment {
private static final int MENU_GROUP_PRESET = 100;
@ -37,7 +41,7 @@ public class EqualizerFragment extends Fragment {
private Equalizer equalizer;
private LinearLayout equalizerLayout;
private View presetButton;
private CheckBox enabledCheckBox;
private CheckBox enabledCheckBox;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@ -124,7 +128,7 @@ public class EqualizerFragment extends Fragment {
}
catch (Exception ex)
{
//TODO: Show a dialog
//TODO: Show a dialog?
Timber.i(ex, "An exception has occurred in EqualizerFragment onContextItemSelected");
}
@ -179,7 +183,7 @@ public class EqualizerFragment extends Fragment {
}
catch (Exception ex)
{
//TODO: Show a dialog
//TODO: Show a dialog?
Timber.i(ex, "An exception has occurred in EqualizerFragment updateBars");
}
}
@ -200,7 +204,7 @@ public class EqualizerFragment extends Fragment {
{
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;
if (bandBar != null)
@ -209,7 +213,7 @@ public class EqualizerFragment extends Fragment {
final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
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);
bar.setMax(maxEQLevel - minEQLevel);
@ -265,7 +269,7 @@ public class EqualizerFragment extends Fragment {
{
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;
/**
* Displays the lyrics of a song
*/
public class LyricsFragment extends Fragment {
private TextView artistView;

View File

@ -27,6 +27,9 @@ import kotlin.Lazy;
import static java.util.Arrays.asList;
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 {
private static boolean shouldUseId3;
@ -84,7 +87,7 @@ public class MainFragment extends Fragment {
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 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;
/**
* Contains the mini-now playing information box displayed at the bottom of the screen
*/
public class NowPlayingFragment extends Fragment {
private static final int MIN_DISTANCE = 30;
@ -130,7 +134,7 @@ public class NowPlayingFragment extends Fragment {
getView().setOnTouchListener(new View.OnTouchListener() {
@Override
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())
{
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.STOPPED;
/**
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist
*/
public class PlayerFragment extends Fragment implements GestureDetector.OnGestureListener {
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
public boolean onTouch(View view, MotionEvent me)
{
return gestureScanner.onTouchEvent(me);
}
};
albumArtImageView.setOnTouchListener(touchListener);
});
albumArtImageView.setOnClickListener(new View.OnClickListener()
{
@ -265,7 +266,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
mediaPlayerControllerLazy.getValue().previous();
return null;
@ -301,7 +302,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Boolean>(getActivity())
{
@Override
protected Boolean doInBackground() throws Throwable
protected Boolean doInBackground()
{
if (mediaPlayerControllerLazy.getValue().getCurrentPlayingNumberOnPlaylist() < mediaPlayerControllerLazy.getValue().getPlaylistSize() - 1)
{
@ -345,7 +346,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
mediaPlayerControllerLazy.getValue().pause();
return null;
@ -369,7 +370,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
mediaPlayerControllerLazy.getValue().reset();
return null;
@ -395,7 +396,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
start();
return null;
@ -456,7 +457,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
mediaPlayerControllerLazy.getValue().seekTo(getProgressBar().getProgress());
return null;
@ -491,7 +492,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
new SilentBackgroundTask<Void>(getActivity())
{
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
mediaPlayerControllerLazy.getValue().play(position);
return null;
@ -812,7 +813,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
}
@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);
if (view == playlistView)
@ -1345,7 +1346,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
PlayerState playerState;
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
this.mediaPlayerController = mediaPlayerControllerLazy.getValue();
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);
break;
case IDLE:
break;
case PREPARED:
break;
case STOPPED:
break;
case PAUSED:
break;
case COMPLETED:
break;
}
@ -1464,13 +1461,13 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
int seekTo;
@Override
protected Void doInBackground() throws Throwable
protected Void doInBackground()
{
msPlayed = Math.max(0, mediaPlayerController.getPlayerPosition());
duration = mediaPlayerController.getPlayerDuration();
final int msTotal = duration;
seekTo = msPlayed + ms > msTotal ? msTotal : msPlayed + ms;
seekTo = Math.min(msPlayed + ms, msTotal);
mediaPlayerController.seekTo(seekTo);
return null;
}

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.fragment;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.Spannable;
@ -51,6 +50,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays the playlists stored on the server
*/
public class PlaylistsFragment extends Fragment {
private SwipeRefreshLayout refreshPlaylistsListView;
@ -83,9 +85,8 @@ public class PlaylistsFragment extends Fragment {
refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
@Override
public void onRefresh()
{
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
public void onRefresh() {
load(true);
}
});
@ -119,11 +120,6 @@ public class PlaylistsFragment extends Fragment {
super.onDestroyView();
}
private void refresh()
{
load(true);
}
private void load(final boolean refresh)
{
BackgroundTask<List<Playlist>> task = new FragmentBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken)
@ -182,43 +178,34 @@ public class PlaylistsFragment extends Fragment {
}
Bundle bundle;
switch (menuItem.getItemId())
{
case R.id.playlist_menu_pin:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
break;
case R.id.playlist_menu_unpin:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
break;
case R.id.playlist_menu_download:
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
break;
case R.id.playlist_menu_play_now:
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
break;
case R.id.playlist_menu_play_shuffled:
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
break;
case R.id.playlist_menu_delete:
deletePlaylist(playlist);
break;
case R.id.playlist_info:
displayPlaylistInfo(playlist);
break;
case R.id.playlist_update_info:
updatePlaylistInfo(playlist);
break;
default:
return super.onContextItemSelected(menuItem);
int itemId = menuItem.getItemId();
if (itemId == R.id.playlist_menu_pin) {
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
} else if (itemId == R.id.playlist_menu_unpin) {
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
} else if (itemId == R.id.playlist_menu_download) {
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
} else if (itemId == R.id.playlist_menu_play_now) {
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
} else if (itemId == R.id.playlist_menu_play_shuffled) {
bundle = new Bundle();
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
} else if (itemId == R.id.playlist_menu_delete) {
deletePlaylist(playlist);
} else if (itemId == R.id.playlist_info) {
displayPlaylistInfo(playlist);
} else if (itemId == R.id.playlist_update_info) {
updatePlaylistInfo(playlist);
} else {
return super.onContextItemSelected(menuItem);
}
return true;
}
@ -333,7 +320,7 @@ public class PlaylistsFragment extends Fragment {
@Override
protected void done(Void result)
{
refresh();
load(true);
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.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;
/**
* Displays the podcasts available on the server
*/
public class PodcastFragment extends Fragment {
private View emptyTextView;

View File

@ -57,6 +57,9 @@ import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject;
/**
* Initiates a search on the media library and displays the results
*/
public class SearchFragment extends Fragment {
private static int DEFAULT_ARTISTS;
@ -68,7 +71,7 @@ public class SearchFragment extends Fragment {
private View artistsHeading;
private View albumsHeading;
private View songsHeading;
private TextView searchButton;
private TextView notFound;
private View moreArtistsButton;
private View moreAlbumsButton;
private View moreSongsButton;
@ -114,14 +117,14 @@ public class SearchFragment extends Fragment {
DEFAULT_ALBUMS = Util.getDefaultAlbums(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)
{
artistsHeading = buttons.findViewById(R.id.search_artists);
albumsHeading = buttons.findViewById(R.id.search_albums);
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);
moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
moreSongsButton = buttons.findViewById(R.id.search_more_songs);
@ -168,7 +171,7 @@ public class SearchFragment extends Fragment {
}
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 isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory();
MenuInflater inflater = getActivity().getMenuInflater();
if (!isArtist && !isAlbum)
{
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.select_song_context, menu);
}
else
{
MenuInflater inflater = getActivity().getMenuInflater();
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();
searchButton.setText(empty ? R.string.search_no_match : R.string.search_search);
if (empty) mergeAdapter.addView(notFound, false);
}
list.setAdapter(mergeAdapter);
@ -551,19 +553,19 @@ public class SearchFragment extends Fragment {
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();
if (mediaPlayerController != null)
{
if (!append && !playNext)
if (!append)
{
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);
}
@ -581,7 +583,7 @@ public class SearchFragment extends Fragment {
{
if (!searchResult.getSongs().isEmpty())
{
onSongSelected(searchResult.getSongs().get(0), false, false, true, false);
onSongSelected(searchResult.getSongs().get(0), false);
}
else if (!searchResult.getAlbums().isEmpty())
{

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@ -57,6 +56,9 @@ import timber.log.Timber;
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 static final String allSongsId = "-1";
@ -79,7 +81,7 @@ public class SelectAlbumFragment extends Fragment {
private MenuItem playAllButton;
private MenuItem shareButton;
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<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
@ -115,7 +117,7 @@ public class SelectAlbumFragment extends Fragment {
{
@Override
public void onRefresh() {
new SelectAlbumFragment.GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
updateDisplay(true);
}
});
@ -198,7 +200,7 @@ public class SelectAlbumFragment extends Fragment {
@Override
public void onClick(View view)
{
playNow(false, false);
playNow(false);
}
});
playNextButton.setOnClickListener(new View.OnClickListener()
@ -215,7 +217,7 @@ public class SelectAlbumFragment extends Fragment {
@Override
public void onClick(View view)
{
playNow(false, true);
playNow(true);
}
});
pinButton.setOnClickListener(new View.OnClickListener()
@ -333,7 +335,7 @@ public class SelectAlbumFragment extends Fragment {
}
@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);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
@ -397,7 +399,7 @@ public class SelectAlbumFragment extends Fragment {
} else if (itemId == R.id.select_album_play_all) {
playAll();
} 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);
shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken);
return true;
@ -453,18 +455,18 @@ public class SelectAlbumFragment extends Fragment {
super.onDestroyView();
}
private void playNow(final boolean shuffle, final boolean append)
private void playNow(final boolean append)
{
List<MusicDirectory.Entry> selectedSongs = getSelectedSongs(albumListView);
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);
}
else
{
playAll(shuffle, append);
playAll(false, append);
}
}
@ -504,7 +506,7 @@ public class SelectAlbumFragment extends Fragment {
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)
{
@ -521,15 +523,6 @@ public class SelectAlbumFragment extends Fragment {
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)
{
FragmentTitle.Companion.setTitle(this, name);
@ -545,7 +538,7 @@ public class SelectAlbumFragment extends Fragment {
{
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);
for (MusicDirectory.Entry song : songs)
@ -672,7 +665,7 @@ public class SelectAlbumFragment extends Fragment {
{
MusicDirectory root = new MusicDirectory();
Collection<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
Collection<MusicDirectory.Entry> songs = new LinkedList<>();
getSongsForArtist(parentId, songs);
for (MusicDirectory.Entry song : songs)
@ -1095,7 +1088,7 @@ public class SelectAlbumFragment extends Fragment {
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
MusicDirectory dir = load(musicService);
boolean valid = musicService.isLicenseValid(getContext());
return new Pair<MusicDirectory, Boolean>(dir, valid);
return new Pair<>(dir, valid);
}
@Override
@ -1275,20 +1268,4 @@ public class SelectAlbumFragment extends Fragment {
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;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -30,6 +29,9 @@ import java.util.List;
import timber.log.Timber;
/**
* Displays the available genres in the media library
*/
public class SelectGenreFragment extends Fragment {
private SwipeRefreshLayout refreshGenreListView;
@ -60,7 +62,7 @@ public class SelectGenreFragment extends Fragment {
@Override
public void onRefresh()
{
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
load(true);
}
});
@ -93,21 +95,16 @@ public class SelectGenreFragment extends Fragment {
super.onDestroyView();
}
private void refresh()
{
load(true);
}
private void load(final boolean refresh)
{
BackgroundTask<List<Genre>> task = new FragmentBackgroundTask<List<Genre>>(getActivity(), true, refreshGenreListView, cancellationToken)
{
@Override
protected List<Genre> doInBackground() throws Throwable
protected List<Genre> doInBackground()
{
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
List<Genre> genres = new ArrayList<Genre>();
List<Genre> genres = new ArrayList<>();
try
{
@ -134,20 +131,4 @@ public class SelectGenreFragment extends Fragment {
};
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 android.view.View;
import org.jetbrains.annotations.NotNull;
import org.koin.java.KoinJavaComponent;
import org.moire.ultrasonic.R;
import org.moire.ultrasonic.featureflags.Feature;
@ -100,7 +101,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
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)) {
setDebugLogToFile(sharedPreferences.getBoolean(key, false));
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
if (sharedPreferences.getBoolean(key, false)) showArtistPicture.setEnabled(true);
else showArtistPicture.setEnabled(false);
showArtistPicture.setEnabled(sharedPreferences.getBoolean(key, false));
} else if (Constants.PREFERENCES_KEY_THEME.equals(key)) {
themeChangedEventDistributor.getValue().RaiseThemeChangedEvent();
}
@ -214,7 +214,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (dialogFragment != null)
{
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
{
@ -465,8 +465,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
debugLogToFile.setSummary("");
}
if (Util.getShouldUseId3Tags(getActivity())) showArtistPicture.setEnabled(true);
else showArtistPicture.setEnabled(false);
showArtistPicture.setEnabled(Util.getShouldUseId3Tags(getActivity()));
}
private void setImageLoaderConcurrency(int concurrency) {
@ -503,11 +502,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
private void setBluetoothPreferences(boolean enabled) {
if (enabled) {
sendBluetoothAlbumArt.setEnabled(true);
} else {
sendBluetoothAlbumArt.setEnabled(false);
}
sendBluetoothAlbumArt.setEnabled(enabled);
}
private void setCacheLocation(String path) {

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.fragment;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.Spannable;
@ -28,6 +27,7 @@ import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jetbrains.annotations.NotNull;
import org.moire.ultrasonic.R;
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
import org.moire.ultrasonic.domain.Share;
@ -51,6 +51,9 @@ import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject;
/**
* Displays the shares in the media library
*/
public class SharesFragment extends Fragment {
private SwipeRefreshLayout refreshSharesListView;
@ -85,7 +88,7 @@ public class SharesFragment extends Fragment {
@Override
public void onRefresh()
{
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
load(true);
}
});
@ -118,11 +121,6 @@ public class SharesFragment extends Fragment {
super.onDestroyView();
}
private void refresh()
{
load(true);
}
private void load(final boolean refresh)
{
BackgroundTask<List<Share>> task = new FragmentBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken)
@ -145,7 +143,7 @@ public class SharesFragment extends Fragment {
}
@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);
@ -311,7 +309,7 @@ public class SharesFragment extends Fragment {
@Override
protected void done(Void result)
{
refresh();
load(true);
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.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.FileUtil;
/**
* Widget Provider for the Ultrasonic Widgets
*/
public class UltrasonicAppWidgetProvider extends AppWidgetProvider
{
protected int layoutId;
@ -73,7 +76,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
{
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
*/
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 RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);
@ -177,7 +180,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
// Link actions buttons to intents
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)
{
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)
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>> cachedAlbum;
private final LRUCache<String, TimeLimitedCache<UserInfo>> cachedUserInfo;
private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedArtists = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<PodcastsChannel>> cachedPodcastsChannels = new TimeLimitedCache<List<PodcastsChannel>>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<List<Genre>>(10 * 3600, TimeUnit.SECONDS);
private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<>(120, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<Indexes> cachedArtists = new TimeLimitedCache<>(60 * 60, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<PodcastsChannel>> cachedPodcastsChannels = new TimeLimitedCache<>(3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<>(10 * 3600, TimeUnit.SECONDS);
private final TimeLimitedCache<List<Genre>> cachedGenres = new TimeLimitedCache<>(10 * 3600, TimeUnit.SECONDS);
private String restUrl;

View File

@ -196,7 +196,9 @@ public class DownloadFile
{
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;
@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;

View File

@ -44,7 +44,6 @@ import org.moire.ultrasonic.domain.UserInfo;
import org.moire.ultrasonic.util.CancellableTask;
import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.FileUtil;
import org.moire.ultrasonic.util.ProgressListener;
import org.moire.ultrasonic.util.Util;
import java.io.BufferedReader;
@ -509,7 +508,7 @@ public class OfflineMusicService implements MusicService
@Override
public List<Playlist> getPlaylists(boolean refresh, Context context)
{
List<Playlist> playlists = new ArrayList<Playlist>();
List<Playlist> playlists = new ArrayList<>();
File root = FileUtil.getPlaylistDirectory(context);
String lastServer = null;
boolean removeServer = true;
@ -544,11 +543,13 @@ public class OfflineMusicService implements MusicService
// Delete legacy playlist files
try
{
folder.delete();
if (!folder.delete()) {
Timber.w("Failed to delete old playlist file: %s", folder.getName());
}
}
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_NEXT = "org.moire.ultrasonic.CMD_NEXT";
// Notification IDs.
public static final int NOTIFICATION_ID_PLAYING = 100;
// Preferences keys.
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_INSTALL_TIME = "installTime";
public static final String PREFERENCES_KEY_THEME = "theme";
public static final String PREFERENCES_KEY_THEME_LIGHT = "light";
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_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 ALBUM_ART_FILE = "folder.jpeg";

View File

@ -156,9 +156,7 @@ public class FileUtil
File avatarFile = getAvatarFile(context, username);
Bitmap bitmap = null;
ImageLoader imageLoader = null;
imageLoader = imageLoaderProvider.getValue().getImageLoader();
ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
if (imageLoader != null)
{
@ -222,9 +220,7 @@ public class FileUtil
File albumArtFile = getAlbumArtFile(context, entry);
Bitmap bitmap = null;
ImageLoader imageLoader = null;
imageLoader = imageLoaderProvider.getValue().getImageLoader();
ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
if (imageLoader != null)
{
@ -491,10 +487,10 @@ public class FileUtil
if (files == null)
{
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)

View File

@ -40,6 +40,7 @@ import org.moire.ultrasonic.service.MusicServiceFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@ -100,7 +101,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
threads = Collections.synchronizedCollection(new ArrayList<Thread>(this.concurrency));
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);
thread.start();
}
@ -213,7 +214,7 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
}
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
@ -338,10 +339,6 @@ public class LegacyImageLoader implements Runnable, ImageLoader {
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) {
if (resId == -1) resId = R.drawable.unknown_album;
if (large) {

View File

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

View File

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

View File

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

View File

@ -1,17 +1,15 @@
package org.moire.ultrasonic.util;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.DialogPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceDialogFragmentCompat;
import org.moire.ultrasonic.R;
import java.util.Locale;
import java.util.regex.Pattern;
/**
@ -72,7 +70,7 @@ public class TimeSpanPreferenceDialogFragmentCompat extends PreferenceDialogFrag
{
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;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
@ -44,6 +45,8 @@ import timber.log.Timber;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews;
import android.widget.Toast;
@ -61,10 +64,8 @@ import org.moire.ultrasonic.service.DownloadFile;
import java.io.*;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
@ -93,8 +94,6 @@ public class Util
private static boolean mediaButtonsRegisteredForUI;
private static boolean mediaButtonsRegisteredForService;
private static final Map<Integer, Version> SERVER_REST_VERSIONS = new ConcurrentHashMap<Integer, Version>();
// 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 Toast toast;
@ -122,7 +121,7 @@ public class Util
SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_REPEAT_MODE, repeatMode.name());
editor.commit();
editor.apply();
}
public static boolean isNotificationEnabled(Context context)
@ -141,6 +140,7 @@ public class Util
return preferences.getBoolean(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION, false);
}
@SuppressWarnings({"BooleanMethodIsAlwaysInverted"}) // It is inverted for readability
public static boolean isLockScreenEnabled(Context context)
{
SharedPreferences preferences = getPreferences(context);
@ -205,25 +205,6 @@ public class Util
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>.
* <p/>
@ -346,6 +327,7 @@ public class Util
toast(context, message, true);
}
@SuppressLint("ShowToast") // Invalid warning
public static void toast(Context context, CharSequence message, boolean shortDuration)
{
if (toast == null)
@ -381,22 +363,19 @@ public class Util
// More than 1 GB?
if (byteCount >= 1024 * 1024 * 1024)
{
NumberFormat gigaByteFormat = GIGA_BYTE_FORMAT;
return gigaByteFormat.format((double) byteCount / (1024 * 1024 * 1024));
return GIGA_BYTE_FORMAT.format((double) byteCount / (1024 * 1024 * 1024));
}
// More than 1 MB?
if (byteCount >= 1024 * 1024)
{
NumberFormat megaByteFormat = MEGA_BYTE_FORMAT;
return megaByteFormat.format((double) byteCount / (1024 * 1024));
return MEGA_BYTE_FORMAT.format((double) byteCount / (1024 * 1024));
}
// More than 1 KB?
if (byteCount >= 1024)
{
NumberFormat kiloByteFormat = KILO_BYTE_FORMAT;
return kiloByteFormat.format((double) byteCount / 1024);
return KILO_BYTE_FORMAT.format((double) byteCount / 1024);
}
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)
{
int[] attrs = new int[]{attr};
@ -667,7 +641,7 @@ public class Util
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);
}
@ -902,11 +876,7 @@ public class Util
avrcpIntent.putExtra("playing", true);
break;
case STOPPED:
avrcpIntent.putExtra("playing", false);
break;
case PAUSED:
avrcpIntent.putExtra("playing", false);
break;
case COMPLETED:
avrcpIntent.putExtra("playing", false);
break;
@ -1011,7 +981,7 @@ public class Util
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
inSampleSize = Math.min(heightRatio, widthRatio);
}
return inSampleSize;
@ -1085,6 +1055,7 @@ public class Util
views.setOnClickPendingIntent(R.id.notification_five_star_5, pendingIntent);
}
// TODO: Shouldn't this be used when making requests?
public static int getNetworkTimeout(Context context)
{
SharedPreferences preferences = getPreferences(context);
@ -1190,6 +1161,7 @@ public class Util
return Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME, "300"));
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted") // Inverted for readability
public static boolean isNullOrWhiteSpace(String string)
{
return string == null || string.isEmpty() || string.trim().isEmpty();
@ -1239,18 +1211,18 @@ public class Util
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)
{
return String.format("%d:%02d:%02d", hours, minutes, seconds);
return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds);
}
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)
@ -1329,6 +1301,7 @@ public class Util
return versionCode;
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted") // Inverted for readability
public static boolean getShouldSendBluetoothNotifications(Context context)
{
SharedPreferences preferences = getPreferences(context);
@ -1396,7 +1369,7 @@ public class Util
SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(Constants.PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS, shouldAskForShareDetails);
editor.commit();
editor.apply();
}
public static void setDefaultShareExpiration(Context context, String defaultShareExpiration)
@ -1404,7 +1377,7 @@ public class Util
SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION, defaultShareExpiration);
editor.commit();
editor.apply();
}
public static void setDefaultShareDescription(Context context, String defaultShareDescription)
@ -1412,7 +1385,7 @@ public class Util
SharedPreferences preferences = getPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, defaultShareDescription);
editor.commit();
editor.apply();
}
public static boolean getShouldShowAllSongsByArtist(Context context)
@ -1485,4 +1458,12 @@ public class Util
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;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;

View File

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

View File

@ -27,13 +27,13 @@ import static org.koin.java.KoinJavaComponent.inject;
public class ChatAdapter extends ArrayAdapter<ChatMessage>
{
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 Pattern phoneMatcher = Pattern.compile(phoneRegex);
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
private Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
public ChatAdapter(Context context, List<ChatMessage> messages)
{

View File

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

View File

@ -9,9 +9,6 @@ import android.widget.TextView;
import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.Playlist;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@ -34,7 +31,7 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist>
Playlist entry = getItem(position);
PlaylistView view;
if (convertView != null && convertView instanceof PlaylistView)
if (convertView instanceof PlaylistView)
{
PlaylistView currentView = (PlaylistView) convertView;
@ -52,23 +49,6 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist>
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
{
TextView name;

View File

@ -9,9 +9,6 @@ import android.widget.TextView;
import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.Share;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
@ -34,7 +31,7 @@ public class ShareAdapter extends ArrayAdapter<Share>
Share entry = getItem(position);
ShareView view;
if (convertView != null && convertView instanceof ShareView)
if (convertView instanceof ShareView)
{
ShareView currentView = (ShareView) convertView;
@ -52,23 +49,6 @@ public class ShareAdapter extends ArrayAdapter<Share>
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
{
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.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.fragment.OnBackPressedHandler
import org.moire.ultrasonic.fragment.ServerSettingsModel
import org.moire.ultrasonic.provider.SearchSuggestionProvider
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
*/
class NavigationActivity : AppCompatActivity() {
var chatMenuItem: MenuItem? = null
var bookmarksMenuItem: MenuItem? = null
var sharesMenuItem: MenuItem? = null
var podcastsMenuItem: MenuItem? = null
var nowPlayingView: FragmentContainerView? = null
var nowPlayingHidden = false
var navigationView: NavigationView? = null
var drawerLayout: DrawerLayout? = null
private var chatMenuItem: MenuItem? = null
private var bookmarksMenuItem: MenuItem? = null
private var sharesMenuItem: MenuItem? = null
private var podcastsMenuItem: MenuItem? = null
private var nowPlayingView: FragmentContainerView? = null
private var nowPlayingHidden = false
private var navigationView: NavigationView? = null
private var drawerLayout: DrawerLayout? = null
private var host: NavHostFragment? = null
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var nowPlayingEventListener: NowPlayingEventListener
@ -93,10 +95,10 @@ class NavigationActivity : AppCompatActivity() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
val host: NavHostFragment = supportFragmentManager
host = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
val navController = host!!.navController
appBarConfiguration = AppBarConfiguration(
setOf(
@ -123,7 +125,7 @@ class NavigationActivity : AppCompatActivity() {
val dest: String = try {
resources.getResourceName(destination.id)
} catch (e: Resources.NotFoundException) {
Integer.toString(destination.id)
destination.id.toString()
}
Timber.d("Navigated to $dest")
@ -209,7 +211,7 @@ class NavigationActivity : AppCompatActivity() {
navigationView?.setupWithNavController(navController)
// 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 ->
if (item.itemId == R.id.menu_exit) {
setResult(Constants.RESULT_CLOSE_ALL)
@ -235,7 +237,9 @@ class NavigationActivity : AppCompatActivity() {
if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) {
this.drawerLayout?.closeDrawer(GravityCompat.START)
} 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 {
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

View File

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

View File

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

View File

@ -631,7 +631,7 @@ open class RESTMusicService(
id: String,
useFlash: Boolean
): 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
// by videoPlayer.view?id=<id>&maxBitRate=500&autoplay=true, but this url is not
// official Subsonic API call.

View File

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