diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/AbstractResponseObject.java b/JMPDComm/src/main/java/com/anpmech/mpd/AbstractResponseObject.java index 0164a7aa83..7f3e48113e 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/AbstractResponseObject.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/AbstractResponseObject.java @@ -164,7 +164,7 @@ public boolean equals(final Object o) { isEqual = Boolean.TRUE; } - return isEqual.booleanValue(); + return isEqual; } /** diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/MPD.java b/JMPDComm/src/main/java/com/anpmech/mpd/MPD.java index 789a43a7e0..aad7441644 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/MPD.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/MPD.java @@ -70,10 +70,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Set; /** * This is a class containing instantiations of commonly used objects and uses for convenience of @@ -338,18 +340,29 @@ public void add(final FilesystemTreeEntry music) throws IOException, MPDExceptio public void add(final Genre genre, final boolean replace, final boolean play) throws IOException, MPDException { + add(Collections.singleton(genre), replace, play); + } + + public void add(final Set genres, final boolean replace, final boolean play) + throws IOException, MPDException { final CommandQueue commandQueue; if (isCommandAvailable(MPDCommand.MPD_CMD_FIND_ADD)) { commandQueue = new CommandQueue(); - commandQueue - .add(MPDCommand.MPD_CMD_FIND_ADD, Music.TAG_GENRE, genre.getName()); + for (final Genre genre : genres) { + commandQueue + .add(MPDCommand.MPD_CMD_FIND_ADD, Music.TAG_GENRE, genre.getName()); + } } else { - final List music = new ArrayList<>(find(Music.TAG_GENRE, genre.getName())); - Collections.sort(music); + final Set music = new HashSet<>(); + for (final Genre genre : genres) { + music.addAll(find(Music.TAG_GENRE, genre.getName())); + } + final List musicList = new ArrayList<>(music); + Collections.sort(musicList); - commandQueue = MPDPlaylist.addAllCommand(music); + commandQueue = MPDPlaylist.addAllCommand(musicList); } add(commandQueue, replace, play); @@ -405,7 +418,6 @@ private void add(final CommandQueue commandQueue, final boolean replace, /** Finally, clean up the last playing song. */ if (replace && isPlaying && !isConsume) { commandQueue.add(MPDPlaylist.MPD_CMD_PLAYLIST_REMOVE, "0"); - } /** @@ -413,7 +425,7 @@ private void add(final CommandQueue commandQueue, final boolean replace, * methods without adding to the command queue. */ if (!commandQueue.isEmpty()) { - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } } @@ -549,7 +561,7 @@ public void addToPlaylist(final PlaylistFile playlist, final Album album) if (mIdleConnection.isCommandAvailable(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST)) { final String[] artistPair = getAlbumArtistPair(album); - mConnection.send(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), + mConnection.submit(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), Music.TAG_ALBUM, album.getName(), artistPair[0], artistPair[1]); } else { final List songs = getSongs(album); @@ -564,7 +576,7 @@ public void addToPlaylist(final PlaylistFile playlist, final Album album) public void addToPlaylist(final PlaylistFile playlist, final Artist artist) throws IOException, MPDException { if (mIdleConnection.isCommandAvailable(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST)) { - mConnection.send(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), + mConnection.submit(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), Music.TAG_ARTIST, artist.getName()); } else { final List songs = getSongs(artist); @@ -586,27 +598,40 @@ public void addToPlaylist(final PlaylistFile playlist, final Collection m .add(MPDCommand.MPD_CMD_PLAYLIST_ADD, playlist.getFullPath(), music.getFullPath()); } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } } @SuppressWarnings("TypeMayBeWeakened") public void addToPlaylist(final PlaylistFile playlist, final FilesystemTreeEntry entry) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_PLAYLIST_ADD, playlist.getFullPath(), + mConnection.submit(MPDCommand.MPD_CMD_PLAYLIST_ADD, playlist.getFullPath(), entry.getFullPath()); } public void addToPlaylist(final PlaylistFile playlist, final Genre genre) throws IOException, MPDException { + addToPlaylist(playlist, Collections.singleton(genre)); + } + + public void addToPlaylist(final PlaylistFile playlist, final Set genres) + throws IOException, MPDException { if (mIdleConnection.isCommandAvailable(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST)) { - mConnection.send(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), - Music.TAG_GENRE, genre.getName()); + final CommandQueue commandQueue = new CommandQueue(); + for (final Genre genre : genres) { + commandQueue.add(MPDCommand.MPD_CMD_SEARCH_ADD_PLAYLIST, playlist.getFullPath(), + Music.TAG_GENRE, genre.getName()); + } + mConnection.submit(commandQueue); } else { - final List music = new ArrayList<>(find(Music.TAG_GENRE, genre.getName())); - Collections.sort(music); + final Set music = new HashSet<>(); + for (final Genre genre : genres) { + music.addAll(find(Music.TAG_GENRE, genre.getName())); + } + final List musicList = new ArrayList<>(music); + Collections.sort(musicList); - addToPlaylist(playlist, music); + addToPlaylist(playlist, musicList); } } @@ -622,7 +647,7 @@ public void addToPlaylist(final PlaylistFile playlist, final Music music) * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void clearError() throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_CLEARERROR); + mConnection.submit(MPDCommand.MPD_CMD_CLEARERROR); } /** @@ -687,7 +712,7 @@ public final void connect(final String server) { } public void disableOutput(final int id) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_OUTPUTDISABLE, Integer.toString(id)); + mConnection.submit(MPDCommand.MPD_CMD_OUTPUTDISABLE, Integer.toString(id)); } /** @@ -800,10 +825,22 @@ public AlbumArtistResponse getAlbumArtists(final Genre genre) throws IOException final CommandResult result = mConnection.submit( MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ALBUM_ARTIST, Music.TAG_GENRE, genre.getName()).get(); - return new AlbumArtistResponse(result); } + public List getAlbumArtists(final Set genres) throws IOException, MPDException { + final CommandQueue commands = new CommandQueue(); + for (final Genre genre : genres) { + commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ALBUM_ARTIST, + Music.TAG_GENRE, genre.getName()); + } + final CommandResult result = mConnection.submit(commands).get(); + + final List artists = new ArrayList<>(new AlbumArtistResponse(result)); + Item.merge(artists, Collections.emptyList()); + return artists; + } + public int getAlbumCount(final Artist artist, final boolean useAlbumArtistTag) throws IOException, MPDException { return listAlbums(artist, useAlbumArtistTag).size(); @@ -900,10 +937,22 @@ public ArtistResponse getArtists() throws IOException, MPDException { public ArtistResponse getArtists(final Genre genre) throws IOException, MPDException { final CommandResult result = mConnection.submit(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ARTIST, Music.TAG_GENRE, genre.getName()).get(); - return new ArtistResponse(result); } + public List getArtists(final Set genres) throws IOException, MPDException { + final CommandQueue commands = new CommandQueue(); + for (final Genre genre : genres) { + commands.add(MPDCommand.MPD_CMD_LIST_TAG, + Music.TAG_ARTIST, Music.TAG_GENRE, genre.getName()); + } + final CommandResult result = mConnection.submit(commands).get(); + + final List artists = new ArrayList<>(new ArtistResponse(result)); + Item.merge(artists, Collections.emptyList()); + return artists; + } + public List getArtistsMerged() throws IOException, MPDException { final CommandQueue commands = new CommandQueue(2); commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ARTIST); @@ -913,11 +962,17 @@ public List getArtistsMerged() throws IOException, MPDException { } public List getArtistsMerged(final Genre genre) throws IOException, MPDException { - final CommandQueue commands = new CommandQueue(2); - final String genreName = genre.getName(); - commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ARTIST, Music.TAG_GENRE, genreName); - commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ALBUM_ARTIST, Music.TAG_GENRE, - genreName); + return getArtistsMerged(Collections.singleton(genre)); + } + + public List getArtistsMerged(final Set genres) throws IOException, MPDException { + final CommandQueue commands = new CommandQueue(); + for (final Genre genre : genres) { + final String genreName = genre.getName(); + commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ARTIST, Music.TAG_GENRE, genreName); + commands.add(MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ALBUM_ARTIST, Music.TAG_GENRE, + genreName); + } return getMergedArtists(mConnection, commands); } @@ -1171,7 +1226,6 @@ public Sticker getStickerManager() { */ public boolean isAlbumInGenre(final Album album, final Genre genre) throws IOException, MPDException { - final List response; final Artist artist = album.getArtist(); String artistName = null; String artistType = null; @@ -1186,13 +1240,13 @@ public boolean isAlbumInGenre(final Album album, final Genre genre) } } - response = mConnection.send(MPDCommand.create( + final CommandResult result = mConnection.submit(MPDCommand.create( MPDCommand.MPD_CMD_LIST_TAG, Music.TAG_ALBUM, Music.TAG_ALBUM, album.getName(), artistType, artistName, - Music.TAG_GENRE, genre.getName())); + Music.TAG_GENRE, genre.getName())).get(); - return !response.isEmpty(); + return !result.isEmpty(); } /** @@ -1382,7 +1436,7 @@ public MusicResponse listAllInfo() throws IOException, MPDException { @SuppressWarnings("TypeMayBeWeakened") public void movePlaylistSong(final PlaylistFile playlist, final int from, final int to) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_PLAYLIST_MOVE, playlist.getFullPath(), + mConnection.submit(MPDCommand.MPD_CMD_PLAYLIST_MOVE, playlist.getFullPath(), Integer.toString(from), Integer.toString(to)); } @@ -1404,7 +1458,7 @@ public void refresh(final RefreshableItem directory) throws IOException, MPDExce * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void refreshDatabase() throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_REFRESH); + mConnection.submit(MPDCommand.MPD_CMD_REFRESH); } /** @@ -1415,7 +1469,7 @@ public void refreshDatabase() throws IOException, MPDException { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void refreshDatabase(final String folder) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_REFRESH, folder); + mConnection.submit(MPDCommand.MPD_CMD_REFRESH, folder); } /** @@ -1437,7 +1491,7 @@ public void removeFromPlaylist(final PlaylistFile playlist, final List position.toString()); } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } @SuppressWarnings("TypeMayBeWeakened") @@ -1448,7 +1502,7 @@ public void removeFromPlaylist(final PlaylistFile playlist, final int pos) private void removeFromPlaylist(final String playlist, final int pos) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_PLAYLIST_DEL, playlist, Integer.toString(pos)); + mConnection.submit(MPDCommand.MPD_CMD_PLAYLIST_DEL, playlist, Integer.toString(pos)); } public void removeSavedStream(final int pos) throws IOException, MPDException { @@ -1456,7 +1510,7 @@ public void removeSavedStream(final int pos) throws IOException, MPDException { } public void saveStream(final String url, final String name) throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_PLAYLIST_ADD, Stream.PLAYLIST_NAME, + mConnection.submit(MPDCommand.MPD_CMD_PLAYLIST_ADD, Stream.PLAYLIST_NAME, Stream.addStreamName(url, name)); } @@ -1499,6 +1553,6 @@ public final void setDefaultPassword(final CharSequence password) { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void shutdown() throws IOException, MPDException { - mConnection.send(MPDCommand.MPD_CMD_KILL); + mConnection.submit(MPDCommand.MPD_CMD_KILL); } } diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/MPDPlaylist.java b/JMPDComm/src/main/java/com/anpmech/mpd/MPDPlaylist.java index 47c342fd81..433cb78b3c 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/MPDPlaylist.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/MPDPlaylist.java @@ -159,7 +159,7 @@ static MPDCommand loadCommand(final String file) { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void add(final FilesystemTreeEntry entry) throws IOException, MPDException { - mConnection.send(addCommand(entry.getFullPath())); + mConnection.submit(addCommand(entry.getFullPath())); } /** @@ -171,7 +171,7 @@ public void add(final FilesystemTreeEntry entry) throws IOException, MPDExceptio * @see Music */ public void addAll(final Iterable collection) throws IOException, MPDException { - mConnection.send(addAllCommand(collection)); + mConnection.submit(addAllCommand(collection)); } /** @@ -182,7 +182,7 @@ public void addAll(final Iterable collection) throws IOException, MPDExce * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void addStream(final String url) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_ADD, url); + mConnection.submit(MPD_CMD_PLAYLIST_ADD, url); } /** @@ -192,7 +192,7 @@ public void addStream(final String url) throws IOException, MPDException { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void clear() throws IOException, MPDException { - mConnection.send(clearCommand()); + mConnection.submit(clearCommand()); } /** @@ -273,7 +273,7 @@ public boolean isValid() { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void load(final String file) throws IOException, MPDException { - mConnection.send(loadCommand(file)); + mConnection.submit(loadCommand(file)); } /** @@ -285,7 +285,7 @@ public void load(final String file) throws IOException, MPDException { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void move(final int songId, final int to) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_MOVE_ID, Integer.toString(songId), + mConnection.submit(MPD_CMD_PLAYLIST_MOVE_ID, Integer.toString(songId), Integer.toString(to)); } @@ -299,7 +299,7 @@ public void move(final int songId, final int to) throws IOException, MPDExceptio * @see #move(int, int) */ public void moveByPosition(final int from, final int to) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_MOVE, Integer.toString(from), + mConnection.submit(MPD_CMD_PLAYLIST_MOVE, Integer.toString(from), Integer.toString(to)); } @@ -320,7 +320,7 @@ public void moveByPosition(final int start, final int number, final int to) final String endRange = Integer.toString(start + number); final String target = Integer.toString(to); mConnection - .send(MPD_CMD_PLAYLIST_MOVE, beginRange + ':' + endRange, target); + .submit(MPD_CMD_PLAYLIST_MOVE, beginRange + ':' + endRange, target); } } @@ -391,7 +391,7 @@ public void removeById(final int... songIds) throws IOException, MPDException { for (final int id : songIds) { commandQueue.add(MPD_CMD_PLAYLIST_REMOVE_ID, Integer.toString(id)); } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } /** @@ -408,7 +408,7 @@ public void removeById(final Collection songIds) throws IOException, MP commandQueue.add(MPD_CMD_PLAYLIST_REMOVE_ID, id.toString()); } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } /** @@ -429,7 +429,7 @@ void removeByIndex(final int... songs) throws IOException, MPDException { commandQueue.add(MPD_CMD_PLAYLIST_REMOVE, Integer.toString(i)); } } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } /** @@ -451,7 +451,7 @@ void removeByIndex(final List songs) throws IOException, MPDException { } } - mConnection.send(commandQueue); + mConnection.submit(commandQueue); } /** @@ -462,7 +462,7 @@ void removeByIndex(final List songs) throws IOException, MPDException { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void removePlaylist(final String file) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_DELETE, file); + mConnection.submit(MPD_CMD_PLAYLIST_DELETE, file); } /** @@ -479,7 +479,7 @@ public void savePlaylist(final String file) throws IOException, MPDException { } catch (final MPDException ignored) { /** We're removing it just in case it exists. */ } - mConnection.send(MPD_CMD_PLAYLIST_SAVE, file); + mConnection.submit(MPD_CMD_PLAYLIST_SAVE, file); } /** @@ -489,7 +489,7 @@ public void savePlaylist(final String file) throws IOException, MPDException { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void shuffle() throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_SHUFFLE); + mConnection.submit(MPD_CMD_PLAYLIST_SHUFFLE); } /** @@ -511,7 +511,7 @@ public int size() { * @throws MPDException Thrown if an error occurs as a result of command execution. */ public void swap(final int song1Id, final int song2Id) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_SWAP_ID, + mConnection.submit(MPD_CMD_PLAYLIST_SWAP_ID, Integer.toString(song1Id), Integer.toString(song2Id)); } @@ -525,7 +525,7 @@ public void swap(final int song1Id, final int song2Id) throws IOException, MPDEx * @see #swap(int, int) */ public void swapByPosition(final int song1, final int song2) throws IOException, MPDException { - mConnection.send(MPD_CMD_PLAYLIST_SWAP, Integer.toString(song1), + mConnection.submit(MPD_CMD_PLAYLIST_SWAP, Integer.toString(song1), Integer.toString(song2)); } diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/connection/MPDConnection.java b/JMPDComm/src/main/java/com/anpmech/mpd/connection/MPDConnection.java index 3f545f0672..b466b7ff6e 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/connection/MPDConnection.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/connection/MPDConnection.java @@ -165,8 +165,6 @@ public abstract class MPDConnection implements MPDConnectionListener { * @see #connect(InetAddress, int) */ MPDConnection(final int readWriteTimeout, final boolean blockingIO) { - super(); - mReadWriteTimeout = readWriteTimeout; if (blockingIO) { diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractItem.java b/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractItem.java index 41fd380008..b536703891 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractItem.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractItem.java @@ -30,6 +30,7 @@ import java.text.Collator; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Locale; @@ -68,46 +69,23 @@ abstract class AbstractItem> implements Comparable } /** - * This method merges two Item type lists. - * - *

This method removes unknown items and items not of the same name, then adds the remainder - * to {@code list1}.

+ * This method merges two Item type lists into {@code list1} and removes unknown items and + * items of the same name. * * @param list1 The first list to merge, the list which will be merged into. - * @param list2 The second list to merge, do not reuse this list after calling this method. + * @param list2 The second list to merge. * @param Anything that extends an AbstractItem. */ public static > void merge(final List list1, final List list2) { + list1.addAll(list2); Collections.sort(list1); - Collections.sort(list2); - - final ListIterator iterator2 = list2.listIterator(); - int position = 0; - ListIterator iterator1 = list1.listIterator(position); - - while (iterator2.hasNext()) { - final T item2 = iterator2.next(); - if (position != iterator1.previousIndex()) { - iterator1 = list1.listIterator(position); - } - while (iterator1.hasNext()) { - final T item1 = iterator1.next(); - - if (item1.isUnknown()) { - iterator1.remove(); - continue; - } - - if (item1.isNameSame(item2)) { - position = iterator1.previousIndex(); - iterator2.remove(); - break; - } + for (int i = list1.size()-1; i>= 0; i--) { + final T item = list1.get(i); + if (item.isUnknown() || i>0 && item.isNameSame(list1.get(i-1))) { + list1.remove(i); } } - - list1.addAll(list2); } /** diff --git a/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractResponseItem.java b/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractResponseItem.java index 98b2e77e4d..ad478c1a16 100644 --- a/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractResponseItem.java +++ b/JMPDComm/src/main/java/com/anpmech/mpd/item/AbstractResponseItem.java @@ -110,7 +110,7 @@ public boolean equals(final Object o) { isEqual = Boolean.TRUE; } - return isEqual.booleanValue(); + return isEqual; } /** diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/AlbumsFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/AlbumsFragment.java index 63f3ef1e6d..d0c0b1d48d 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/AlbumsFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/AlbumsFragment.java @@ -27,6 +27,7 @@ import com.namelessdev.mpdroid.cover.CoverManager; import com.namelessdev.mpdroid.helpers.AlbumInfo; import com.namelessdev.mpdroid.library.ILibraryFragmentActivity; +import com.namelessdev.mpdroid.models.GenresGroup; import com.namelessdev.mpdroid.tools.Tools; import com.namelessdev.mpdroid.views.AlbumDataBinder; import com.namelessdev.mpdroid.views.holders.AlbumViewHolder; @@ -59,11 +60,11 @@ public class AlbumsFragment extends BrowseFragment { private static final String TAG = "AlbumsFragment"; - protected Artist mArtist = null; + protected Artist mArtist; protected ProgressBar mCoverArtProgress; - protected Genre mGenre = null; + private GenresGroup mGenresGroups; protected boolean mIsCountDisplayed; @@ -116,9 +117,9 @@ protected void asyncUpdate() { Collections.sort(mItems); } - if (mGenre != null) { // filter albums not in genre + if (mGenresGroups != null) { // filter albums not in genre for (int i = mItems.size() - 1; i >= 0; i--) { - if (!mApp.getMPD().isAlbumInGenre(mItems.get(i), mGenre)) { + if (!isAlbumInOneGenre(mItems.get(i), mGenresGroups)) { mItems.remove(i); } } @@ -128,6 +129,15 @@ protected void asyncUpdate() { } } + private boolean isAlbumInOneGenre(final Album album, final GenresGroup genres) throws IOException, MPDException { + for (final Genre genre : genres.getGenres()) { + if (mApp.getMPD().isAlbumInGenre(album, genre)) { + return true; + } + } + return false; + } + /** * Uses CoverManager to clean up a cover. * @@ -206,7 +216,7 @@ public void onCreate(final Bundle savedInstanceState) { if (bundle != null) { mArtist = bundle.getParcelable(Artist.EXTRA); - mGenre = bundle.getParcelable(Genre.EXTRA); + mGenresGroups = bundle.getParcelable(GenresGroup.EXTRA); } } @@ -296,8 +306,8 @@ public void onSaveInstanceState(final Bundle outState) { outState.putParcelable(Artist.EXTRA, mArtist); } - if (mGenre != null) { - outState.putParcelable(Genre.EXTRA, mGenre); + if (mGenresGroups != null) { + outState.putParcelable(GenresGroup.EXTRA, mGenresGroups); } super.onSaveInstanceState(outState); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/ArtistsFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/ArtistsFragment.java index fe9fad949e..9d50a28648 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/ArtistsFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/ArtistsFragment.java @@ -25,6 +25,7 @@ import com.namelessdev.mpdroid.MPDApplication; import com.namelessdev.mpdroid.R; import com.namelessdev.mpdroid.library.ILibraryFragmentActivity; +import com.namelessdev.mpdroid.models.GenresGroup; import com.namelessdev.mpdroid.tools.Tools; import android.app.Activity; @@ -38,7 +39,9 @@ import android.widget.AdapterView; import java.io.IOException; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; public class ArtistsFragment extends BrowseFragment { @@ -54,7 +57,7 @@ public class ArtistsFragment extends BrowseFragment { private static final String TAG = "ArtistsFragment"; - private Genre mGenre = null; + private GenresGroup mGenresGroup; public ArtistsFragment() { super(R.string.addArtist, R.string.artistAdded); @@ -89,31 +92,27 @@ protected void asyncUpdate() { try { final SharedPreferences settings = PreferenceManager .getDefaultSharedPreferences(MPDApplication.getInstance()); + final Collection artists; switch (settings.getString(PREFERENCE_ARTIST_TAG_TO_USE, PREFERENCE_ARTIST_TAG_TO_USE_BOTH).toLowerCase()) { case PREFERENCE_ARTIST_TAG_TO_USE_ALBUMARTIST: - if (mGenre == null) { - replaceItems(mApp.getMPD().getAlbumArtists()); - } else { - replaceItems(mApp.getMPD().getAlbumArtists(mGenre)); - } + artists = mGenresGroup == null ? + mApp.getMPD().getAlbumArtists() : + mApp.getMPD().getAlbumArtists(mGenresGroup.getGenres()); break; case PREFERENCE_ARTIST_TAG_TO_USE_ARTIST: - if (mGenre == null) { - replaceItems(mApp.getMPD().getArtists()); - } else { - replaceItems(mApp.getMPD().getArtists(mGenre)); - } + artists = mGenresGroup == null ? + mApp.getMPD().getArtists() : + mApp.getMPD().getArtists(mGenresGroup.getGenres()); break; case PREFERENCE_ARTIST_TAG_TO_USE_BOTH: default: - if (mGenre == null) { - replaceItems(mApp.getMPD().getArtistsMerged()); - } else { - replaceItems(mApp.getMPD().getArtistsMerged(mGenre)); - } + artists = mGenresGroup == null ? + mApp.getMPD().getArtistsMerged() : + mApp.getMPD().getArtistsMerged(mGenresGroup.getGenres()); break; } + replaceItems(artists); Collections.sort(mItems); } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to update.", e); @@ -146,14 +145,14 @@ public int getLoadingText() { public String getTitle() { final String title; - if (mGenre == null) { + if (mGenresGroup == null) { final Bundle bundle = getArguments(); String name = null; if (bundle != null) { - final Genre genre = bundle.getParcelable(Genre.EXTRA); + final GenresGroup genresGroup = bundle.getParcelable(GenresGroup.EXTRA); - if (genre != null) { - name = genre.getName(); + if (genresGroup != null) { + name = genresGroup.getName(); } } @@ -163,7 +162,7 @@ public String getTitle() { title = name; } } else { - title = mGenre.toString(); + title = mGenresGroup.toString(); } return title; @@ -181,7 +180,7 @@ public void onCreate(final Bundle savedInstanceState) { } if (bundle != null) { - mGenre = bundle.getParcelable(Genre.EXTRA); + mGenresGroup = bundle.getParcelable(GenresGroup.EXTRA); } } @@ -195,7 +194,7 @@ public void onItemClick(final AdapterView parent, final View view, final int final Fragment fragment; bundle.putParcelable(Artist.EXTRA, mItems.get(position)); - bundle.putParcelable(Genre.EXTRA, mGenre); + bundle.putParcelable(GenresGroup.EXTRA, mGenresGroup); if (settings.getBoolean(PREFERENCE_ALBUM_LIBRARY, true)) { fragment = Fragment.instantiate(activity, AlbumsGridFragment.class.getName(), bundle); @@ -208,8 +207,8 @@ public void onItemClick(final AdapterView parent, final View view, final int @Override public void onSaveInstanceState(final Bundle outState) { - if (mGenre != null) { - outState.putParcelable(Genre.EXTRA, mGenre); + if (mGenresGroup != null) { + outState.putParcelable(GenresGroup.EXTRA, mGenresGroup); } super.onSaveInstanceState(outState); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/GenresFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/GenresFragment.java index 08c470d6dd..88a67bb999 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/GenresFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/GenresFragment.java @@ -16,25 +16,39 @@ package com.namelessdev.mpdroid.fragments; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; + import com.anpmech.mpd.exception.MPDException; import com.anpmech.mpd.item.Artist; import com.anpmech.mpd.item.Genre; import com.anpmech.mpd.item.PlaylistFile; import com.namelessdev.mpdroid.R; import com.namelessdev.mpdroid.library.ILibraryFragmentActivity; +import com.namelessdev.mpdroid.models.GenresGroup; import com.namelessdev.mpdroid.tools.Tools; -import android.os.Bundle; -import android.support.annotation.StringRes; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.View; -import android.widget.AdapterView; - import java.io.IOException; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; -public class GenresFragment extends BrowseFragment { +public class GenresFragment extends BrowseFragment { + + public static final String PREFERENCE_OPTIMIZE_GENRES = "optimizeGenres"; + + public static final String PREFERENCE_GENRE_SEPARATORS = "genreSeparators"; private static final String TAG = "GenresFragment"; @@ -43,9 +57,9 @@ public GenresFragment() { } @Override - protected void add(final Genre item, final boolean replace, final boolean play) { + protected void add(final GenresGroup item, final boolean replace, final boolean play) { try { - mApp.getMPD().add(item, replace, play); + mApp.getMPD().add(item.getGenres(), replace, play); Tools.notifyUser(mIrAdded, item); } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to add all from playlist.", e); @@ -53,9 +67,9 @@ protected void add(final Genre item, final boolean replace, final boolean play) } @Override - protected void add(final Genre item, final PlaylistFile playlist) { + protected void add(final GenresGroup item, final PlaylistFile playlist) { try { - mApp.getMPD().addToPlaylist(playlist, item); + mApp.getMPD().addToPlaylist(playlist, item.getGenres()); Tools.notifyUser(mIrAdded, item); } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to add all genre to playlist.", e); @@ -65,15 +79,69 @@ protected void add(final Genre item, final PlaylistFile playlist) { @Override protected void asyncUpdate() { try { - replaceItems(mApp.getMPD().getGenreResponse()); + replaceItems(groupGenres(mApp.getMPD().getGenreResponse())); Collections.sort(mItems); } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to update list of genres.", e); } } + private List groupGenres(final Collection genres) { + final Map groupMap = new HashMap<>(); + + final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mApp); + final boolean optimizeGenres = settings.getBoolean(PREFERENCE_OPTIMIZE_GENRES, false); + final String genreSeparators = optimizeGenres + ? settings.getString(PREFERENCE_GENRE_SEPARATORS, "").replace(" ", "") + : ""; + + for (final Genre genre : genres) { + final String[] genreNames = genreSeparators.isEmpty() + ? new String[] { genre.getName() } + : genre.getName().split("[" + Pattern.quote(genreSeparators) + "]"); + for (String genreName : genreNames) { + if (optimizeGenres) { + genreName = capitalize(genreName.replace(" ", " ")); + } + + GenresGroup group = groupMap.get(genreName); + + if (group == null) { + group = new GenresGroup(genreName); + groupMap.put(genreName, group); + } + group.addGenre(genre); + } + } + + return new LinkedList<>(groupMap.values()); + } + + @NonNull + private String capitalize(String s) { + s = s.toLowerCase(); + for (final Character delimiter : " &-".toCharArray()) { + s = capitalize(s, delimiter); + } + return s; + } + + private String capitalize(final String s, final Character delimiter) { + String result = ""; + for (final String word : s.split(delimiter.toString())) { + if (!result.isEmpty()) { + result += delimiter; + } + if (!word.isEmpty()) { + result += word.substring(0, 1).toUpperCase() + + (word.length() > 1 ? word.substring(1) : ""); + } + } + return result; + } + @Override - protected Artist getArtist(final Genre item) { + protected Artist getArtist(final GenresGroup item) { return null; } @@ -101,8 +169,9 @@ public void onItemClick(final AdapterView parent, final View view, final int final Fragment fragment = Fragment.instantiate(getActivity(), ArtistsFragment.class.getName(), bundle); - bundle.putParcelable(Genre.EXTRA, mItems.get(position)); + bundle.putParcelable(GenresGroup.EXTRA, mItems.get(position)); ((ILibraryFragmentActivity) getActivity()).pushLibraryFragment(fragment, Artist.EXTRA); } + } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/models/GenresGroup.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/models/GenresGroup.java new file mode 100644 index 0000000000..29df61c751 --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/models/GenresGroup.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010-2016 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.models; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.anpmech.mpd.ResponseObject; +import com.anpmech.mpd.item.Genre; +import com.anpmech.mpd.item.Item; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class GenresGroup extends Item { + + /** + * This field is used to instantiate this class from a {@link Parcel}. + */ + public static final Creator CREATOR = new GenresGroupParcelCreator(); + + /** + * This is a convenience string to use as a Intent extra tag. + */ + public static final String EXTRA = "Genres"; + + private final String name; + + private Set genres = new HashSet<>(); + + public GenresGroup(final String name) { + assert name != null; + this.name = name; + } + + public void addGenre(final Genre genre) { + genres.add(genre); + } + + public Set getGenres() { + return Collections.unmodifiableSet(genres); + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + dest.writeString(name); + dest.writeTypedList(new ArrayList<>(genres)); + } + + @Override + public boolean equals(final Object o) { + return o != null && (o == this || o.getClass() == this.getClass() && name.equals(((GenresGroup) o).name)); + } + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return !name.isEmpty() ? name : getGenres().iterator().next().toString(); + } + + /** + * This class is used to instantiate a Genre Object from a {@code Parcel}. + */ + private static final class GenresGroupParcelCreator implements Parcelable.Creator { + + @Override + public GenresGroup createFromParcel(final Parcel source) { + final GenresGroup genresGroup = new GenresGroup(source.readString()); + source.readParcelable(ResponseObject.LOADER); + final List genres = new ArrayList<>(); + source.readTypedList(genres, Genre.CREATOR); + genresGroup.genres = new HashSet<>(genres); + return genresGroup; + } + + @Override + public GenresGroup[] newArray(final int size) { + return new GenresGroup[size]; + } + } + +} \ No newline at end of file diff --git a/MPDroid/src/main/res/values-de/strings.xml b/MPDroid/src/main/res/values-de/strings.xml index 4bdeb1bff3..febc3839c1 100644 --- a/MPDroid/src/main/res/values-de/strings.xml +++ b/MPDroid/src/main/res/values-de/strings.xml @@ -318,5 +318,8 @@ Der Stream-Codec wird nicht unterstützt. Wiedergabeliste mischen Wiedergabeliste wurde zufällig gemischt + Genre Trennzeichen + Genre optimieren + Trenne und formatiere Genre diff --git a/MPDroid/src/main/res/values/strings.xml b/MPDroid/src/main/res/values/strings.xml index 5dfbf40200..ebea831365 100644 --- a/MPDroid/src/main/res/values/strings.xml +++ b/MPDroid/src/main/res/values/strings.xml @@ -182,6 +182,9 @@ Sort albums by year Album track count Show number of tracks on album + Optimize genres + Split & trim genre values and adjust case + Genre separators Download local cover art Get cover art from the server running MPD (requires a web server, read the wiki!) Path to music diff --git a/MPDroid/src/main/res/xml/settings.xml b/MPDroid/src/main/res/xml/settings.xml index af330ff63d..d274cf36ff 100644 --- a/MPDroid/src/main/res/xml/settings.xml +++ b/MPDroid/src/main/res/xml/settings.xml @@ -180,6 +180,24 @@ android:persistent="true" android:summary="@string/showAlbumTrackCountDescription" android:title="@string/showAlbumTrackCount" /> + + + + + + + +