diff --git a/JMPDComm/src/main/java/org/a0z/mpd/CommandQueue.java b/JMPDComm/src/main/java/org/a0z/mpd/CommandQueue.java index 257bdc4151..d6d168d894 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/CommandQueue.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/CommandQueue.java @@ -33,10 +33,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; /** A class to generate and send a command queue. */ -final class CommandQueue { +public class CommandQueue implements Iterable { private static final boolean DEBUG = false; @@ -54,14 +55,14 @@ final class CommandQueue { private int mCommandQueueStringLength; - CommandQueue() { + public CommandQueue() { super(); mCommandQueue = new ArrayList<>(); mCommandQueueStringLength = getStartLength(); } - CommandQueue(final int size) { + public CommandQueue(final int size) { super(); mCommandQueue = new ArrayList<>(size); @@ -103,7 +104,7 @@ private static List separatedQueueResults(final Iterable lines * * @param commandQueue The command queue to add to this one. */ - void add(final CommandQueue commandQueue) { + public void add(final CommandQueue commandQueue) { mCommandQueue.addAll(commandQueue.mCommandQueue); mCommandQueueStringLength += commandQueue.mCommandQueueStringLength; } @@ -114,7 +115,7 @@ void add(final CommandQueue commandQueue) { * @param position The position of this command queue to add the new command queue. * @param commandQueue The command queue to add to this one. */ - void add(final int position, final CommandQueue commandQueue) { + public void add(final int position, final CommandQueue commandQueue) { mCommandQueue.addAll(position, commandQueue.mCommandQueue); mCommandQueueStringLength += commandQueue.mCommandQueueStringLength; } @@ -125,7 +126,7 @@ void add(final int position, final CommandQueue commandQueue) { * @param position The position of this command queue to add the new command. * @param command The command to add to this command queue. */ - void add(final int position, final MPDCommand command) { + public void add(final int position, final MPDCommand command) { mCommandQueue.add(position, command); mCommandQueueStringLength += command.toString().length(); } @@ -135,7 +136,7 @@ void add(final int position, final MPDCommand command) { * * @param command Command to add to the queue. */ - void add(final MPDCommand command) { + public void add(final MPDCommand command) { mCommandQueue.add(command); mCommandQueueStringLength += command.toString().length(); } @@ -145,12 +146,12 @@ void add(final MPDCommand command) { * * @param command Command to add to the queue. */ - void add(final String command, final String... args) { + public void add(final String command, final String... args) { add(new MPDCommand(command, args)); } /** Clear the command queue. */ - void clear() { + public void clear() { mCommandQueueStringLength = getStartLength(); mCommandQueue.clear(); } @@ -159,8 +160,18 @@ public boolean isEmpty() { return mCommandQueue.isEmpty(); } + /** + * Returns an {@link java.util.Iterator} for the elements in this object. + * + * @return An {@code Iterator} instance. + */ + @Override + public Iterator iterator() { + return mCommandQueue.iterator(); + } + /** Reverse the command queue order, useful for removing playlist entries. */ - void reverse() { + public void reverse() { Collections.reverse(mCommandQueue); } @@ -172,7 +183,7 @@ void reverse() { * @throws IOException Thrown upon a communication error with the server. * @throws MPDException Thrown if an error occurs as a result of command execution. */ - List send(final MPDConnection mpdConnection) throws IOException, MPDException { + public List send(final MPDConnection mpdConnection) throws IOException, MPDException { return send(mpdConnection, false); } diff --git a/JMPDComm/src/main/java/org/a0z/mpd/MPD.java b/JMPDComm/src/main/java/org/a0z/mpd/MPD.java index 635383b24e..6716299bad 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/MPD.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/MPD.java @@ -40,6 +40,7 @@ import org.a0z.mpd.item.Music; import org.a0z.mpd.item.PlaylistFile; import org.a0z.mpd.item.Stream; +import org.a0z.mpd.subsystem.Sticker; import java.io.IOException; import java.net.InetAddress; @@ -1216,71 +1217,32 @@ public List listAlbumArtists(final List albums) * @throws MPDException Thrown if an error occurs as a result of command execution. */ public List listAlbums() throws IOException, MPDException { - return listAlbums(null, false, true); + return listAlbums(null, false); } /** - * List all albums from database. - * - * @param useAlbumArtist use AlbumArtist instead of Artist - * @return {@code Collection} with all album names from database. - * @throws IOException Thrown upon a communication error with the server. - * @throws MPDException Thrown if an error occurs as a result of command execution. - */ - public List listAlbums(final boolean useAlbumArtist) throws IOException, MPDException { - return listAlbums(null, useAlbumArtist, true); - } - - /** - * List all albums from a given artist, including an entry for songs with no - * album tag. + * List all albums from a given artist. * * @param artist artist to list albums * @param useAlbumArtist use AlbumArtist instead of Artist - * @return {@code Collection} with all album names from database. - * @throws IOException Thrown upon a communication error with the server. - * @throws MPDException Thrown if an error occurs as a result of command execution. - */ - public List listAlbums(final String artist, final boolean useAlbumArtist) - throws IOException, MPDException { - return listAlbums(artist, useAlbumArtist, true); - } - - /** - * List all albums from a given artist. - * - * @param artist artist to list albums - * @param useAlbumArtist use AlbumArtist instead of Artist - * @param includeUnknownAlbum include an entry for songs with no album tag * @return {@code Collection} with all album names from the given * artist present in database. * @throws IOException Thrown upon a communication error with the server. * @throws MPDException Thrown if an error occurs as a result of command execution. */ - public List listAlbums(final String artist, final boolean useAlbumArtist, - final boolean includeUnknownAlbum) throws IOException, MPDException { - boolean foundSongWithoutAlbum = false; - + public List listAlbums(final String artist, final boolean useAlbumArtist) + throws IOException, MPDException { final List response = - mConnection.sendCommand - (listAlbumsCommand(artist, useAlbumArtist)); + mConnection.sendCommand(listAlbumsCommand(artist, useAlbumArtist)); + final List result; - final List result = new ArrayList<>(response.size()); - for (final String line : response) { - final String name = line.substring("Album: ".length()); - if (name.isEmpty()) { - foundSongWithoutAlbum = true; - } else { - result.add(name); - } - } - - // add a single blank entry to host all songs without an album set - if (includeUnknownAlbum && foundSongWithoutAlbum) { - result.add(""); + if (response.isEmpty()) { + result = Collections.emptyList(); + } else { + result = Tools.parseResponse(response, "Album"); + Collections.sort(result); } - Collections.sort(result); return result; } diff --git a/JMPDComm/src/main/java/org/a0z/mpd/MPDCommand.java b/JMPDComm/src/main/java/org/a0z/mpd/MPDCommand.java index 195507c915..784001e8c5 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/MPDCommand.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/MPDCommand.java @@ -46,8 +46,6 @@ public class MPDCommand { public static final String MPD_CMD_CLOSE = "close"; - public static final String MPD_CMD_COMMANDS = "commands"; - public static final String MPD_CMD_CONSUME = "consume"; public static final String MPD_CMD_COUNT = "count"; @@ -89,8 +87,6 @@ public class MPDCommand { public static final String MPD_CMD_PAUSE = "pause"; - public static final String MPD_CMD_PERMISSION = "permission"; - public static final String MPD_CMD_PING = "ping"; public static final String MPD_CMD_PLAY = "play"; diff --git a/JMPDComm/src/main/java/org/a0z/mpd/MusicList.java b/JMPDComm/src/main/java/org/a0z/mpd/MusicList.java index 12e2d89f0e..ede8243e7f 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/MusicList.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/MusicList.java @@ -97,6 +97,12 @@ private void add(final Music music) { mSongID.add(null); } + if (songPos == -1) { + throw new IllegalStateException("Media server protocol error: songPos not " + + "included with the playlist changes included with the following " + + "music. Path:" + music.getFullPath() + " Name: " + music.getName()); + } + mList.set(songPos, music); mSongID.set(songPos, music.getSongId()); } diff --git a/JMPDComm/src/main/java/org/a0z/mpd/Tools.java b/JMPDComm/src/main/java/org/a0z/mpd/Tools.java index 4457a25215..56d5fdfece 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/Tools.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/Tools.java @@ -27,6 +27,8 @@ package org.a0z.mpd; +import org.a0z.mpd.exception.InvalidResponseException; + import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; @@ -253,6 +255,11 @@ public static String[] splitResponse(final String line) { final int delimiterIndex = line.indexOf(':'); final String[] result = new String[2]; + if (delimiterIndex == -1) { + throw new InvalidResponseException("Failed to parse server response key for line: " + + line); + } + result[0] = line.substring(0, delimiterIndex); /** Skip ': ' */ diff --git a/JMPDComm/src/main/java/org/a0z/mpd/connection/MPDConnection.java b/JMPDComm/src/main/java/org/a0z/mpd/connection/MPDConnection.java index a3b27b2fcf..5aef1336fa 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/connection/MPDConnection.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/connection/MPDConnection.java @@ -32,6 +32,7 @@ import org.a0z.mpd.MPDStatusMonitor; import org.a0z.mpd.Tools; import org.a0z.mpd.exception.MPDException; +import org.a0z.mpd.subsystem.Reflection; import java.io.BufferedReader; import java.io.EOFException; @@ -52,8 +53,6 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import static org.a0z.mpd.Tools.VALUE; - /** * Class representing a connection to MPD Server. */ @@ -127,23 +126,6 @@ public abstract class MPDConnection { } } - /** - * Puts all available commands into a set backed collection. - * - * @param response The media server response to the {@link MPDCommand#MPD_CMD_COMMANDS} - * command. - * @return A collection of available commands from the response. - */ - private static Collection getCommands(final Collection response) { - final Collection commands = new HashSet<>(response.size()); - - for (final String[] pair : Tools.splitResponse(response)) { - commands.add(pair[VALUE]); - } - - return commands; - } - /** * Sets up connection to host/port pair with MPD password. * @@ -162,12 +144,14 @@ public final void connect(final InetAddress host, final int port, final String p mPassword = password; mSocketAddress = new InetSocketAddress(host, port); - final MPDCommand mpdCommand = new MPDCommand(MPDCommand.MPD_CMD_COMMANDS); + final MPDCommand mpdCommand = new MPDCommand(Reflection.CMD_ACTION_COMMANDS); final CommandResult commandResult = processCommand(mpdCommand); synchronized (mAvailableCommands) { + final Collection response = Tools. + parseResponse(commandResult.getResult(), Reflection.CMD_RESPONSE_COMMANDS); mAvailableCommands.clear(); - mAvailableCommands.addAll(getCommands(commandResult.getResult())); + mAvailableCommands.addAll(response); } if (!commandResult.isHeaderValid()) { diff --git a/JMPDComm/src/main/java/org/a0z/mpd/subsystem/Reflection.java b/JMPDComm/src/main/java/org/a0z/mpd/subsystem/Reflection.java new file mode 100644 index 0000000000..7501e86999 --- /dev/null +++ b/JMPDComm/src/main/java/org/a0z/mpd/subsystem/Reflection.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2004 Felipe Gustavo de Almeida + * Copyright (C) 2010-2014 The MPDroid Project + * + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice,this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.a0z.mpd.subsystem; + +import org.a0z.mpd.connection.MPDConnection; +import org.a0z.mpd.exception.MPDException; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import static org.a0z.mpd.Tools.parseResponse; + +/** + * A class to manage the + * reflection subsystem + * of the MPD protocol. This will query various + * capabilities and configuration for the currently connected media server. + */ +public class Reflection { + + /** + * Command text required to generate a command to retrieve a list of permitted commands. + *

+ * Protocol command syntax:
{@code commands} + *

+ *
Sample protocol output:
+ * {@code commands}
+ * {@code command: add}
+ * ... (removed for clarity)
+ * {@code OK}
+ */ + public static final String CMD_ACTION_COMMANDS = "commands"; + + /** + * Command text required to generate a command to retrieve server configuration options. + *

+ * This is currently not functional for this library due to lack of socket connection + * functionality. + */ + public static final String CMD_ACTION_CONFIG = "config"; + + /** + * Command text required to generate a command to receive a list of supported decoders. + *

+ * Protocol command syntax: {@code decoders} + *

+ *
Sample protocol output:
+ * {@code decoders}
+ * {@code plugin: mad}
+ * {@code suffix: mp3}
+ * {@code suffix: mp2}
+ * {@code mime_type: audio/mpeg}
+ * {@code OK} + */ + public static final String CMD_ACTION_DECODERS = "decoders"; + + /** + * Command text required to generate a command to receive a list of non-permitted commands. + *

+ * Protocol command syntax:
{@code notcommands} + *

+ *
Sample protocol output:
+ * {@code notcommands}
+ * {@code command: config}
+ * {@code command: kill}
+ * {@code OK}
+ */ + public static final String CMD_ACTION_NOT_COMMANDS = "notcommands"; + + /** + * Command text required to generate a command to receive a list of available metadata for + * {@code Music} objects. + *

+ * Protocol command syntax: {@code tagtypes} + * {@code tagtypes}
+ * {@code tagtype: Artist}
+ * {@code tagtype: ArtistSort}
+ * ... (removed for clarity)
+ * {@code OK}
+ */ + public static final String CMD_ACTION_TAG_TYPES = "tagtypes"; + + /** + * Command text required to generate a command to receive a list of URL handlers. + *

+ * Protocol command syntax:
{@code urlhandlers} + *

+ *
Sample protocol output:
+ * {@code urlhandlers}
+ * {@code handler: file}
+ * {@code handler: http}
+ * {@code handler: https}
+ * {@code handler: local}
+ * {@code OK}
+ */ + public static final String CMD_ACTION_URL_HANDLERS = "urlhandlers"; + + /** + * A response returned from the {@link #CMD_ACTION_COMMANDS} command. + */ + public static final String CMD_RESPONSE_COMMANDS = "command"; + + /** + * A response returned from the {@link #CMD_ACTION_DECODERS} command. + */ + public static final String CMD_RESPONSE_DECODER_MIME_TYPE = "mimetype"; + + /** + * A response returned from the {@link #CMD_ACTION_DECODERS} command. + */ + public static final String CMD_RESPONSE_DECODER_PLUGIN = "plugin"; + + /** + * A response returned from the {@link #CMD_ACTION_DECODERS} command. + */ + public static final String CMD_RESPONSE_DECODER_SUFFIX = "suffix"; + + /** + * A response key returned from the {@link #CMD_ACTION_TAG_TYPES} command. + */ + public static final String CMD_RESPONSE_TAG_TYPES = "tagtype"; + + /** + * A response key returned from the {@link #CMD_ACTION_URL_HANDLERS} command. + */ + public static final String CMD_RESPONSE_URL_HANDLERS = "handler"; + + /** + * The current connection to the media server used to + * query the media server for reflection handling. + */ + private final MPDConnection mConnection; + + /** + * Constructor for the MPD protocol {@code Reflection} subsystem. + * + * @param connection The connection to use to query the media server for reflection handling. + */ + public Reflection(final MPDConnection connection) { + super(); + + mConnection = connection; + } + + /** + * Retrieves and returns a collection of available commands on + * the current media server with the current permissions. + * + * @return A collection of available commands with the + * current permissions on the connected media server. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + * @see org.a0z.mpd.MPDCommand#MPD_CMD_PASSWORD For modifying available commands. + */ + public Collection getCommands() throws IOException, MPDException { + return getList(CMD_ACTION_COMMANDS, CMD_RESPONSE_COMMANDS); + } + + /** + * Retrieves and returns a list of all available file type + * suffixes supported by the connected media server. + * + * @return A collection of supported file type suffixes. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + */ + public Collection getFileSuffixes() throws IOException, MPDException { + return getList(CMD_ACTION_DECODERS, CMD_RESPONSE_DECODER_SUFFIX); + } + + /** + * A generic method to send a {@code command} and retrieve only {@code element} responses. + * + * @param command The command text to send. + * @param element The command response to add to the collection. + * @return A collection of responses matching {@code element} as a key value. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + */ + private Collection getList(final String command, final String element) + throws IOException, MPDException { + final List response = mConnection.sendCommand(command); + + return parseResponse(response, element); + } + + /** + * Retrieves and returns a collection of all available + * mime types supported by the connected media server. + * + * @return A list of all available mime types for the connected media server. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + */ + public Collection getMIMETypes() throws IOException, MPDException { + return getList(CMD_ACTION_DECODERS, CMD_RESPONSE_DECODER_MIME_TYPE); + } + + /** + * Returns a collection of commands explicitly not permitted for use. + * + * Retrieves and returns a collection of commands explicitly not permitted + * to use on the current media server with the current permissions. + * + * @return A collection of commands explicitly not permitted for use on the currently connected + * se + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + * @see org.a0z.mpd.MPDCommand#MPD_CMD_PASSWORD For modifying available commands. + */ + public Collection getNotCommands() throws IOException, MPDException { + return getList(CMD_ACTION_NOT_COMMANDS, CMD_RESPONSE_COMMANDS); + } + + /** + * Returns a list of available {@code Music} metadata tag types which are available from the + * currently connected media server. + * + * @return A collection of metadata tag types from the current server. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + */ + public Collection getTagTypes() throws IOException, MPDException { + return getList(CMD_ACTION_TAG_TYPES, CMD_RESPONSE_TAG_TYPES); + } + + /** + * Retrieves and returns a list of URL handlers. + * + * @return A collection of available URL handlers for the connected media server. + * @throws IOException Thrown upon a communication error with the server. + * @throws MPDException Thrown if an error occurs as a result of command execution. + */ + public Collection getURLHandlers() throws IOException, MPDException { + return getList(CMD_ACTION_URL_HANDLERS, CMD_RESPONSE_URL_HANDLERS); + } +} diff --git a/JMPDComm/src/main/java/org/a0z/mpd/Sticker.java b/JMPDComm/src/main/java/org/a0z/mpd/subsystem/Sticker.java similarity index 99% rename from JMPDComm/src/main/java/org/a0z/mpd/Sticker.java rename to JMPDComm/src/main/java/org/a0z/mpd/subsystem/Sticker.java index 07c1b16213..106d382609 100644 --- a/JMPDComm/src/main/java/org/a0z/mpd/Sticker.java +++ b/JMPDComm/src/main/java/org/a0z/mpd/subsystem/Sticker.java @@ -25,8 +25,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.a0z.mpd; +package org.a0z.mpd.subsystem; +import org.a0z.mpd.CommandQueue; +import org.a0z.mpd.Log; +import org.a0z.mpd.MPDCommand; +import org.a0z.mpd.Tools; import org.a0z.mpd.connection.MPDConnection; import org.a0z.mpd.exception.MPDException; import org.a0z.mpd.item.FilesystemTreeEntry; diff --git a/MPDroid/build.gradle b/MPDroid/build.gradle index 7cb730bf7a..9c39911a10 100644 --- a/MPDroid/build.gradle +++ b/MPDroid/build.gradle @@ -27,8 +27,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 21 - versionCode 53 - versionName "1.07 Final " + gitShortHash() + versionCode 54 + versionName "1.07.2 Final " + gitShortHash() } lintOptions { diff --git a/MPDroid/src/closedbits/AndroidManifest.xml b/MPDroid/src/closedbits/AndroidManifest.xml index bed5575744..4f31e0ecf1 100644 --- a/MPDroid/src/closedbits/AndroidManifest.xml +++ b/MPDroid/src/closedbits/AndroidManifest.xml @@ -6,6 +6,6 @@ + android:value="Get_your_own_key_ugh" /> diff --git a/MPDroid/src/closedbits/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java b/MPDroid/src/closedbits/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java index 98f2b4e5cc..a61ff8c454 100644 --- a/MPDroid/src/closedbits/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java +++ b/MPDroid/src/closedbits/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java @@ -25,4 +25,8 @@ public class CrashlyticsWrapper { public static void start(Context context) { Crashlytics.start(context); } + + public static void logException(final Exception exception) { + Crashlytics.logException(exception); + } } diff --git a/MPDroid/src/foss/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java b/MPDroid/src/foss/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java index 201a71b2bc..a7d3954164 100644 --- a/MPDroid/src/foss/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java +++ b/MPDroid/src/foss/java/com/namelessdev/mpdroid/closedbits/CrashlyticsWrapper.java @@ -24,4 +24,8 @@ public class CrashlyticsWrapper { public static void start(final Context context) { // Stub } + + public static void logException(final Exception exception) { + // Stub + } } diff --git a/MPDroid/src/main/AndroidManifest.xml b/MPDroid/src/main/AndroidManifest.xml index 547d908351..4085c59177 100644 --- a/MPDroid/src/main/AndroidManifest.xml +++ b/MPDroid/src/main/AndroidManifest.xml @@ -85,9 +85,6 @@ android:name="com.namelessdev.mpdroid.library.LibraryTabsSettings" android:label="@string/libraryTabsSettings" android:theme="@style/AppTheme.Settings" /> - mConnectionLocks = new LinkedList<>(); + private final Collection mConnectionLocks = + Collections.synchronizedCollection(new LinkedList<>()); public MPDAsyncHelper oMPDAsyncHelper = null; @@ -398,13 +400,17 @@ public final boolean isTabletUiEnabled() { && mSettings.getBoolean("tabletUI", true); } - public final boolean shouldDisplayGooglePlayDeathWarning() { - return !mSettings.getBoolean("googlePlayDeathWarningShown", false); + public final boolean hasGooglePlayDeathWarningBeenDisplayed() { + return mSettings.getBoolean("googlePlayDeathWarningShown", false); + } + + public final boolean hasGooglePlayThankYouBeenDisplayed() { + return mSettings.getBoolean("googlePlayThankYouShown", false); } @SuppressLint("CommitPrefEdits") - public final void markGooglePlayDeathWarningAsRead() { - mSettings.edit().putBoolean("googlePlayDeathWarningShown", true).commit(); + public final void markGooglePlayThankYouAsRead() { + mSettings.edit().putBoolean("googlePlayThankYouShown", true).commit(); } /** diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDroidActivities.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDroidActivities.java index cd01d76c4d..a4f456562a 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDroidActivities.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDroidActivities.java @@ -18,10 +18,9 @@ import android.annotation.SuppressLint; import android.app.Activity; -import android.app.ListActivity; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v4.app.FragmentActivity; +import android.support.v7.app.ActionBarActivity; public class MPDroidActivities { @@ -55,38 +54,14 @@ private static void applyTheme(final Activity activity) { } @SuppressLint("Registered") - public static class MPDroidActivity extends Activity { + public static class MPDroidActivity extends ActionBarActivity { protected final MPDApplication mApp = MPDApplication.getInstance(); @Override protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - applyTheme(this); - } - } - - @SuppressLint("Registered") - public static class MPDroidFragmentActivity extends FragmentActivity { - - protected final MPDApplication mApp = MPDApplication.getInstance(); - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); applyTheme(this); - } - } - - @SuppressLint("Registered") - public static class MPDroidListActivity extends ListActivity { - - protected final MPDApplication mApp = MPDApplication.getInstance(); - - @Override - protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - applyTheme(this); } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/MainMenuActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/MainMenuActivity.java index 3b8437d65a..965c530d72 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/MainMenuActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/MainMenuActivity.java @@ -16,7 +16,6 @@ package com.namelessdev.mpdroid; -import com.namelessdev.mpdroid.MPDroidActivities.MPDroidFragmentActivity; import com.namelessdev.mpdroid.fragments.BrowseFragment; import com.namelessdev.mpdroid.fragments.LibraryFragment; import com.namelessdev.mpdroid.fragments.OutputsFragment; @@ -32,10 +31,7 @@ import org.a0z.mpd.MPD; import org.a0z.mpd.MPDStatus; -import android.app.ActionBar; -import android.app.ActionBar.OnNavigationListener; import android.app.AlertDialog; -import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -56,6 +52,7 @@ import android.support.v4.view.ViewPager; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.PopupMenuCompat; +import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -73,7 +70,8 @@ import java.util.Collections; import java.util.List; -public class MainMenuActivity extends MPDroidFragmentActivity implements OnNavigationListener, +public class MainMenuActivity extends MPDroidActivities.MPDroidActivity implements + ActionBar.OnNavigationListener, ILibraryFragmentActivity, ILibraryTabActivity, OnBackStackChangedListener, PopupMenu.OnMenuItemClickListener { @@ -189,7 +187,7 @@ private ActionBarDrawerToggle initializeDrawerToggle() { final int drawerImageRes; // Set up the action bar. - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); actionBar.setCustomView(mTextView); @@ -397,7 +395,7 @@ public void onPanelHidden(final View view) { @Override public void onPanelSlide(final View panel, final float slideOffset) { - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); if (slideOffset > 0.3f) { if (actionBar.isShowing()) { @@ -474,18 +472,19 @@ public void onConfigurationChanged(final Configuration newConfig) { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (mApp.shouldDisplayGooglePlayDeathWarning()) { + if (mApp.hasGooglePlayDeathWarningBeenDisplayed() + && !mApp.hasGooglePlayThankYouBeenDisplayed()) { new AlertDialog.Builder(this) - .setTitle(getString(R.string.gpDeathTitle)) - .setMessage(getResources().getString(R.string.gpDeathMessage)) - .setNegativeButton(getString(R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(final DialogInterface dialogInterface, final int i) { - mApp.markGooglePlayDeathWarningAsRead(); - } - }) - .setCancelable(false) - .show(); + .setTitle(getString(R.string.gpThanksTitle)) + .setMessage(getString(R.string.gpThanksMessage)) + .setNegativeButton(getString(R.string.gpThanksOkButton), new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialogInterface, final int i) { + mApp.markGooglePlayThankYouAsRead(); + } + }) + .setCancelable(false) + .show(); } mApp.setupServiceBinder(); @@ -641,9 +640,6 @@ public boolean onOptionsItemSelected(final MenuItem item) { mApp.startStreaming(); } break; - case R.id.GMM_bonjour: - startActivity(new Intent(this, ServerListActivity.class)); - break; case R.id.GMM_Consume: MPDControl.run(MPDControl.ACTION_CONSUME); break; @@ -724,7 +720,7 @@ public void onStop() { @Override public void pageChanged(final int position) { - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); if (mCurrentDisplayMode == DisplayMode.MODE_LIBRARY && actionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST) { actionBar.setSelectedNavigationItem(position); @@ -796,7 +792,7 @@ public void pushLibraryFragment(final Fragment fragment, final String label) { */ private void refreshActionBarTitle() { - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayShowCustomEnabled(true); if (mCurrentDisplayMode == DisplayMode.MODE_OUTPUTS) { diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java index 8d572ea2c4..2b928fb4c7 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java @@ -29,18 +29,16 @@ import org.a0z.mpd.item.Item; import org.a0z.mpd.item.Music; -import android.app.ActionBar; -import android.app.ActionBar.Tab; -import android.app.ActionBar.TabListener; -import android.app.FragmentTransaction; import android.app.SearchManager; import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; import android.provider.SearchRecentSuggestions; import android.support.annotation.StringRes; +import android.support.v4.app.FragmentTransaction; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; import android.util.Log; import android.view.ContextMenu; import android.view.Menu; @@ -60,7 +58,7 @@ import java.util.List; public class SearchActivity extends MPDroidActivity implements OnMenuItemClickListener, - AsyncExecListener, OnItemClickListener, TabListener { + AsyncExecListener, OnItemClickListener, ActionBar.TabListener { public static final int ADD = 0; @@ -119,11 +117,11 @@ public class SearchActivity extends MPDroidActivity implements OnMenuItemClickLi private String mSearchKeywords = null; - private Tab mTabAlbums; + private ActionBar.Tab mTabAlbums; - private Tab mTabArtists; + private ActionBar.Tab mTabArtists; - private Tab mTabSongs; + private ActionBar.Tab mTabSongs; public SearchActivity() { super(); @@ -279,7 +277,7 @@ protected void onCreate(final Bundle savedInstanceState) { setContentView(R.layout.search_results); final SearchResultsPagerAdapter adapter = new SearchResultsPagerAdapter(); - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); mPager = (ViewPager) findViewById(R.id.pager); mPager.setAdapter(adapter); @@ -510,16 +508,16 @@ public void onStop() { } @Override - public void onTabReselected(final Tab tab, final FragmentTransaction ft) { + public void onTabReselected(final ActionBar.Tab tab, final FragmentTransaction ft) { } @Override - public void onTabSelected(final Tab tab, final FragmentTransaction ft) { + public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) { mPager.setCurrentItem(tab.getPosition()); } @Override - public void onTabUnselected(final Tab tab, final FragmentTransaction ft) { + public void onTabUnselected(final ActionBar.Tab tab, final FragmentTransaction ft) { } private void setContextForObject(final Object object) { diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/ServerListActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/ServerListActivity.java deleted file mode 100644 index 1b2b5d6fe1..0000000000 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/ServerListActivity.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010-2014 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; - -import android.app.ListActivity; -import android.os.Bundle; -import android.widget.AbsListView; - -public class ServerListActivity extends ListActivity { - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.server_list); - getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); - } -} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/SettingsActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/SettingsActivity.java index 06c8fcc462..3b6c459060 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/SettingsActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/SettingsActivity.java @@ -18,10 +18,10 @@ import org.a0z.mpd.MPDStatus; import org.a0z.mpd.event.StatusChangeListener; -import android.app.Activity; import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; -public class SettingsActivity extends Activity implements StatusChangeListener { +public class SettingsActivity extends ActionBarActivity implements StatusChangeListener { private final MPDApplication mApp = MPDApplication.getInstance(); @@ -40,6 +40,7 @@ public void libraryStateChanged(final boolean updating, final boolean dbChanged) @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.settings); mSettingsFragment = new SettingsFragment(); mApp.oMPDAsyncHelper.addStatusChangeListener(this); getFragmentManager().beginTransaction() diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/BrowseFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/BrowseFragment.java index f56772197c..1db4d0c825 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/BrowseFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/BrowseFragment.java @@ -33,8 +33,6 @@ import org.a0z.mpd.item.Item; import org.a0z.mpd.item.Music; -import android.app.ActionBar; -import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; @@ -42,6 +40,8 @@ import android.os.Bundle; import android.support.annotation.StringRes; import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -252,9 +252,9 @@ public String getTitle() { @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); + final ActionBarActivity activity = (ActionBarActivity) getActivity(); if (activity != null) { - final ActionBar actionBar = activity.getActionBar(); + final ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/NowPlayingFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/NowPlayingFragment.java index 8cc1f57b84..cf726352f4 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/NowPlayingFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/NowPlayingFragment.java @@ -594,6 +594,78 @@ public void onStopTrackingTouch(final SeekBar seekBar) { return volumeSeekBar; } + /** + * This method handles any simple library activity item ids. + * + * @param itemId The itemId to attempt to handle. + * @return {@code true} if this is handled by a simple library activity, + * {@code false} otherwise. + */ + private boolean isSimpleLibraryItem(final int itemId) { + Intent intent = null; + + switch (itemId) { + case POPUP_ALBUM: + case POPUP_ALBUM_ARTIST: + case POPUP_ARTIST: + case POPUP_FOLDER: + if (mCurrentSong != null) { + intent = simpleLibraryMusicItem(itemId); + } + break; + case POPUP_STREAM: + intent = new Intent(mActivity, SimpleLibraryActivity.class); + intent.putExtra("streams", true); + break; + default: + break; + } + + if (intent != null) { + /** + * Set the result for SimpleLibraryActivity to + * return so getCallingActivity() will work. + */ + startActivityForResult(intent, 1); + } + + return intent != null; + } + + /** + * This method handles any simple library activity item ids which handle music items. + * + * @param itemId The itemId to attempt to handle. + * @return An intent to start the {@link com.namelessdev.mpdroid.library.SimpleLibraryActivity}. + */ + private Intent simpleLibraryMusicItem(final int itemId) { + final Intent intent = new Intent(mActivity, SimpleLibraryActivity.class); + + switch (itemId) { + case POPUP_ALBUM: + intent.putExtra("album", mCurrentSong.getAlbumAsAlbum()); + break; + case POPUP_ALBUM_ARTIST: + intent.putExtra("artist", mCurrentSong.getAlbumArtistAsArtist()); + break; + case POPUP_ARTIST: + intent.putExtra("artist", mCurrentSong.getArtistAsArtist()); + break; + case POPUP_FOLDER: + final String path = mCurrentSong.getFullPath(); + final String parent = mCurrentSong.getParent(); + if (path == null || parent == null) { + break; + } + intent.putExtra("folder", parent); + break; + default: + break; + } + + return intent; + } + @Override public void libraryStateChanged(final boolean updating, final boolean dbChanged) { } @@ -714,26 +786,11 @@ public void onDetach() { @Override public boolean onMenuItemClick(final MenuItem item) { - final Intent intent; final AlbumInfo albumInfo; boolean result = true; + final int itemId = item.getItemId(); switch (item.getItemId()) { - case POPUP_ALBUM: - intent = new Intent(mActivity, SimpleLibraryActivity.class); - intent.putExtra("album", mCurrentSong.getAlbumAsAlbum()); - startActivityForResult(intent, -1); - break; - case POPUP_ALBUM_ARTIST: - intent = new Intent(mActivity, SimpleLibraryActivity.class); - intent.putExtra("artist", mCurrentSong.getAlbumArtistAsArtist()); - startActivityForResult(intent, -1); - break; - case POPUP_ARTIST: - intent = new Intent(mActivity, SimpleLibraryActivity.class); - intent.putExtra("artist", mCurrentSong.getArtistAsArtist()); - startActivityForResult(intent, -1); - break; case POPUP_COVER_BLACKLIST: albumInfo = new AlbumInfo(mCurrentSong); CoverManager.getInstance().markWrongCover(albumInfo); @@ -743,36 +800,21 @@ public boolean onMenuItemClick(final MenuItem item) { case POPUP_COVER_SELECTIVE_CLEAN: albumInfo = new AlbumInfo(mCurrentSong); CoverManager.getInstance().clear(albumInfo); - downloadCover(albumInfo); // Update the - // Queue covers + downloadCover(albumInfo); updateQueueCovers(albumInfo); break; case POPUP_CURRENT: scrollToNowPlaying(); break; - case POPUP_FOLDER: - final String path = mCurrentSong.getFullPath(); - final String parent = mCurrentSong.getParent(); - if (path == null || parent == null) { - break; - } - intent = new Intent(mActivity, SimpleLibraryActivity.class); - intent.putExtra("folder", parent); - startActivityForResult(intent, -1); - break; case POPUP_SHARE: - intent = new Intent(Intent.ACTION_SEND, null); + final Intent intent = new Intent(Intent.ACTION_SEND, null); intent.putExtra(Intent.EXTRA_TEXT, getShareString()); intent.setType("text/plain"); startActivity(intent); break; - case POPUP_STREAM: - intent = new Intent(mActivity, SimpleLibraryActivity.class); - intent.putExtra("streams", true); - startActivityForResult(intent, -1); - break; default: - result = false; + result = isSimpleLibraryItem(itemId); + break; } return result; @@ -856,6 +898,7 @@ public final void onTrackInfoUpdate(final Music updatedSong, final float trackRa mSongNameText.setText(title); mSongRating.setRating(trackRating); mYearNameText.setText(date); + updateAudioNameText(mApp.oMPDAsyncHelper.oMPD.getStatus()); } @Override @@ -926,6 +969,7 @@ private void startPosTimer(final long start, final long total) { public void stateChanged(final MPDStatus mpdStatus, final int oldState) { if (mActivity != null) { updateStatus(mpdStatus); + updateAudioNameText(mpdStatus); } } @@ -1053,7 +1097,7 @@ private void updateAudioNameText(final MPDStatus status) { if (optionalTrackInfo.length() > 0) { optionalTrackInfo.append(separator); } - optionalTrackInfo.append(sampleRate / 1000); + optionalTrackInfo.append(Math.abs(sampleRate / 1000.0f)); optionalTrackInfo.append("kHz"); } @@ -1084,8 +1128,6 @@ private void updateStatus(final MPDStatus status) { mPlayPauseButton.setImageResource(getPlayPauseResource(status.getState())); - updateAudioNameText(status); - View.OnTouchListener currentListener = null; if (mCurrentSong != null) { if (mCurrentSong.isStream()) { diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/CachedMPD.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/CachedMPD.java index 8aabf0c07e..8284c78ec9 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/CachedMPD.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/CachedMPD.java @@ -172,9 +172,14 @@ public List getAllAlbums(final boolean trackCountNeeded) albums.add(album); } - allAlbums = new ArrayList<>(albums); - Collections.sort(allAlbums); - getAlbumDetails(allAlbums, true); + + if (albums.isEmpty()) { + allAlbums = Collections.emptyList(); + } else { + allAlbums = new ArrayList<>(albums); + Collections.sort(allAlbums); + getAlbumDetails(allAlbums, true); + } } else { allAlbums = super.getAllAlbums(trackCountNeeded); } @@ -224,22 +229,21 @@ public List listAlbumArtists(final List albums) /** * List all albums of given artist from database. * - * @param artist artist to list albums - * @param useAlbumArtist use AlbumArtist instead of Artist - * @param includeUnknownAlbum include an entry for songs with no album tag + * @param artist artist to list albums + * @param useAlbumArtist use AlbumArtist instead of Artist * @return List of albums. * @throws IOException Thrown upon a communication error with the server. * @throws MPDException Thrown if an error occurs as a result of command execution. */ @Override - public List listAlbums(final String artist, final boolean useAlbumArtist, - final boolean includeUnknownAlbum) throws IOException, MPDException { + public List listAlbums(final String artist, final boolean useAlbumArtist) + throws IOException, MPDException { final List albums; if (isCached()) { albums = new ArrayList(mCache.getAlbums(artist, useAlbumArtist)); } else { - albums = super.listAlbums(artist, useAlbumArtist, includeUnknownAlbum); + albums = super.listAlbums(artist, useAlbumArtist); } return albums; diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncHelper.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncHelper.java index 33aee42588..757c19ba07 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncHelper.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncHelper.java @@ -26,7 +26,6 @@ import org.a0z.mpd.event.StatusChangeListener; import org.a0z.mpd.event.TrackPositionListener; -import android.content.Context; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; @@ -50,27 +49,25 @@ public class MPDAsyncHelper implements Handler.Callback { static final int EVENT_CONNECTION_STATE = LOCAL_UID + 4; - static final int EVENT_NETWORK_CONNECTED = LOCAL_UID + 5; + static final int EVENT_PLAYLIST = LOCAL_UID + 5; - static final int EVENT_PLAYLIST = LOCAL_UID + 6; + static final int EVENT_RANDOM = LOCAL_UID + 6; - static final int EVENT_RANDOM = LOCAL_UID + 7; + static final int EVENT_REPEAT = LOCAL_UID + 7; - static final int EVENT_REPEAT = LOCAL_UID + 8; + static final int EVENT_SET_USE_CACHE = LOCAL_UID + 8; - static final int EVENT_SET_USE_CACHE = LOCAL_UID + 9; + static final int EVENT_STATE = LOCAL_UID + 9; - static final int EVENT_STATE = LOCAL_UID + 10; + static final int EVENT_TRACK = LOCAL_UID + 10; - static final int EVENT_TRACK = LOCAL_UID + 11; + static final int EVENT_TRACK_POSITION = LOCAL_UID + 11; - static final int EVENT_TRACK_POSITION = LOCAL_UID + 12; + static final int EVENT_UPDATE_STATE = LOCAL_UID + 12; - static final int EVENT_UPDATE_STATE = LOCAL_UID + 13; + static final int EVENT_VOLUME = LOCAL_UID + 13; - static final int EVENT_VOLUME = LOCAL_UID + 14; - - static final int EVENT_STICKER_CHANGED = LOCAL_UID + 15; + static final int EVENT_STICKER_CHANGED = LOCAL_UID + 14; private static final String TAG = "MPDAsyncHelper"; @@ -84,8 +81,6 @@ public class MPDAsyncHelper implements Handler.Callback { private final Collection mConnectionListeners; - private final Collection mNetworkMonitorListeners; - private final Collection mStatusChangeListeners; private final Collection mTrackPositionListeners; @@ -96,8 +91,6 @@ public class MPDAsyncHelper implements Handler.Callback { private ConnectionInfo mConnectionInfo = new ConnectionInfo(); - private NetworkActivityHandler mNetworkActivityHandler; - public MPDAsyncHelper() { this(PreferenceManager.getDefaultSharedPreferences(MPDApplication.getInstance()) .getBoolean(MPDAsyncWorker.USE_LOCAL_ALBUM_CACHE_KEY, false)); @@ -117,7 +110,6 @@ public MPDAsyncHelper(final boolean cached) { mAsyncExecListeners = new WeakLinkedList<>("AsyncExecListener"); mConnectionListeners = new WeakLinkedList<>("ConnectionListener"); mConnectionInfoListeners = new WeakLinkedList<>("ConnectionInfoListener"); - mNetworkMonitorListeners = new WeakLinkedList<>("NetworkMonitorListener"); mStatusChangeListeners = new WeakLinkedList<>("StatusChangeListener"); mTrackPositionListeners = new WeakLinkedList<>("TrackPositionListener"); } @@ -140,12 +132,6 @@ public void addConnectionListener(final ConnectionListener listener) { } } - public void addNetworkMonitorListener(final NetworkMonitorListener listener) { - if (!mNetworkMonitorListeners.contains(listener)) { - mNetworkMonitorListeners.add(listener); - } - } - public void addStatusChangeListener(final StatusChangeListener listener) { if (!mStatusChangeListeners.contains(listener)) { mStatusChangeListeners.add(listener); @@ -225,11 +211,6 @@ public final boolean handleMessage(final Message msg) { listener.onConnectionConfigChange(mConnectionInfo); } break; - case EVENT_NETWORK_CONNECTED: - for (final NetworkMonitorListener listener : mNetworkMonitorListeners) { - listener.onNetworkConnect(); - } - break; case EVENT_PLAYLIST: for (final StatusChangeListener listener : mStatusChangeListeners) { listener.playlistChanged((MPDStatus) args[0], (Integer) args[1]); @@ -307,20 +288,10 @@ public final boolean handleMessage(final Message msg) { return result; } - public boolean isNetworkMonitorAlive() { - final boolean isNetworkMonitorActive = false; - return isNetworkMonitorActive; - } - public boolean isStatusMonitorAlive() { return oMPDAsyncWorker.isStatusMonitorAlive(); } - /** Don't use this unless you know what you're doing. */ - public void reconnect() { - mWorkerHandler.sendEmptyMessage(MPDAsyncWorker.EVENT_RECONNECT); - } - public void removeAsyncExecListener(final AsyncExecListener listener) { mAsyncExecListeners.remove(listener); } @@ -333,10 +304,6 @@ public void removeConnectionListener(final ConnectionListener listener) { mConnectionListeners.remove(listener); } - public void removeNetworkMonitorListener(final NetworkMonitorListener listener) { - mNetworkMonitorListeners.remove(listener); - } - public void removeStatusChangeListener(final StatusChangeListener listener) { mStatusChangeListeners.remove(listener); } @@ -359,28 +326,11 @@ public void setUseCache(final boolean useCache) { ((CachedMPD) oMPD).setUseCache(useCache); } - public final void startNetworkMonitor(final Context context) { - /** if (!mIsNetworkMonitorActive) { - mNetworkActivityHandler = new NetworkActivityHandler(this); - final IntentFilter intentFilter = - new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); - context.registerReceiver(mNetworkActivityHandler, intentFilter); - mIsNetworkMonitorActive = true; - } */ - } - public void startStatusMonitor(final String[] idleSubsystems) { Message.obtain(mWorkerHandler, MPDAsyncWorker.EVENT_START_STATUS_MONITOR, idleSubsystems) .sendToTarget(); } - public final void stopNetworkMonitor(final Context context) { - /** if (mIsNetworkMonitorActive) { - context.unregisterReceiver(mNetworkActivityHandler); - mIsNetworkMonitorActive = false; - } */ - } - public void stopStatusMonitor() { mWorkerHandler.sendEmptyMessage(MPDAsyncWorker.EVENT_STOP_STATUS_MONITOR); } @@ -403,9 +353,4 @@ public interface ConnectionListener { void connectionSucceeded(String message); } - - public interface NetworkMonitorListener { - - void onNetworkConnect(); - } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncWorker.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncWorker.java index a60278cc7b..00395c40bc 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncWorker.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/MPDAsyncWorker.java @@ -60,11 +60,9 @@ public class MPDAsyncWorker implements Handler.Callback, static final int EVENT_EXEC_ASYNC_FINISHED = LOCAL_UID + 5; - static final int EVENT_RECONNECT = LOCAL_UID + 6; + static final int EVENT_START_STATUS_MONITOR = LOCAL_UID + 6; - static final int EVENT_START_STATUS_MONITOR = LOCAL_UID + 7; - - static final int EVENT_STOP_STATUS_MONITOR = LOCAL_UID + 8; + static final int EVENT_STOP_STATUS_MONITOR = LOCAL_UID + 7; private static final String TAG = "MPDAsyncWorker"; @@ -136,9 +134,6 @@ public final boolean handleMessage(final Message msg) { case EVENT_CONNECT: connect(); break; - case EVENT_RECONNECT: - //reconnect(); - break; case EVENT_START_STATUS_MONITOR: mIdleSubsystems = (String[]) msg.obj; startStatusMonitor(); @@ -226,28 +221,6 @@ public void randomChanged(final boolean random) { .sendToTarget(); } - /** Reconnects the MPD object after the settings have changed. */ - private void reconnect() { - final boolean isMonitorAlive = isStatusMonitorAlive(); - - /** Don't continue before the monitor is stopped. */ - if (isMonitorAlive) { - stopStatusMonitor(); - - try { - /** Give up waiting after a couple of seconds. */ - mStatusMonitor.join(2L * DateUtils.SECOND_IN_MILLIS); - } catch (final InterruptedException ignored) { - } - } - - connect(); - - if (isMonitorAlive) { - startStatusMonitor(); - } - } - @Override public void repeatChanged(final boolean repeating) { mHelperHandler.obtainMessage(MPDAsyncHelper.EVENT_REPEAT, Tools.toObjectArray(repeating)) @@ -269,13 +242,6 @@ public final void setConnectionSettings(final ConnectionInfo connectionInfo) { connectionInfo.isNotificationPersistent) { mHelperHandler.obtainMessage(EVENT_CONNECTION_CONFIG, mConInfo).sendToTarget(); mConInfo = connectionInfo; - - if (mConInfo.serverInfoChanged) { - Log.d(TAG, "Connection changed, connecting to " + mConInfo.server); - mWorkerHandler.sendEmptyMessage(EVENT_RECONNECT); - } else { - Log.d(TAG, "Server info had not changed!"); - } } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/NetworkActivityHandler.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/NetworkActivityHandler.java deleted file mode 100644 index 9960e46615..0000000000 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/NetworkActivityHandler.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) 2010-2014 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.helpers; - -import com.namelessdev.mpdroid.tools.SettingsHelper; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.ConnectivityManager; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; - -/** - * This is a class which detects an available network connection, checks it against the current - * preference and if it matches up sends out a callback for other objects to take action. - */ -public class NetworkActivityHandler extends BroadcastReceiver implements Runnable { - - private static final boolean DEBUG = false; - - private static final String TAG = "NetworkActivityHandler"; - - /** The handler for the MPDAsyncHelper. */ - private final Handler mHelperHandler; - - /** The MPDAsyncHelper object which this will be processed in. */ - private final MPDAsyncHelper mMPDAsyncHelper; - - /** Necessary to keep the MPD connection up to date after a network change. */ - private final SettingsHelper mSettingsHelper; - - /** The intent received by the BroadcastReceiver. */ - private Intent mIntent; - - /** The SharedPreferences from the current context. */ - private SharedPreferences mSettings; - - NetworkActivityHandler(final MPDAsyncHelper mpdAsyncHelper) { - super(); - - mMPDAsyncHelper = mpdAsyncHelper; - mHelperHandler = new Handler(mpdAsyncHelper); - mSettingsHelper = new SettingsHelper(mpdAsyncHelper); - } - - /** - * Get the default shared preferences with Context MODE_MULTI_PROCESS so we always have an - * updated copy from the main process. Because we update the preferences object per the context - * in this object, this could be overkill. - * - * @param context The current context. - * @return The {@code SharedPreference} object. - */ - public static SharedPreferences getPreferences(final Context context) { - return context.getSharedPreferences( - context.getPackageName() + "_preferences", - Context.MODE_MULTI_PROCESS); - } - - /** - * A method to display the intent, formatted for debugging. - * - * @param intent The incoming ConnectivityManager.CONNECTIVITY_ACTION intent. - */ - private static void visualizeIntent(final Intent intent) { - final Bundle extras = intent.getExtras(); - - Log.v(TAG, "action: " + intent.getAction()); - Log.v(TAG, "component: " + intent.getComponent()); - if (extras != null) { - for (final String key : extras.keySet()) { - Log.v(TAG, "key [" + key + "]: " + extras.get(key)); - } - } else { - Log.v(TAG, "no extras"); - } - } - - /** - * This checks for localhost MPD connectivity. - * - * @return True if a localhost MPD is available, false otherwise. - */ - private boolean canConnectToLocalhost() { - final int timeout = 1000; // Should easily take less than a second. - final int port = Integer.parseInt(mSettings.getString("port", "6600")); - final InetSocketAddress endPoint = new InetSocketAddress("127.0.0.1", port); - - boolean isLocalHostAvailable = false; - - if (endPoint.isUnresolved()) { - if (DEBUG) { - Log.d(TAG, "Failure " + endPoint); - } - } else { - final Socket socket = new Socket(); - - try { - socket.connect(endPoint, timeout); - isLocalHostAvailable = true; - if (DEBUG) { - Log.d(TAG, "Success:" + endPoint); - } - } catch (final IOException e) { - if (DEBUG) { - Log.d(TAG, "Failed to connect to 127.0.0.1", e); - } - } finally { - try { - socket.close(); - } catch (final IOException ignored) { - } - } - } - - return isLocalHostAvailable; - } - - /** - * Checks if the MPD object connection configuration is current - * to the current WIFI/SSID network configuration. - * - * @param bundle The incoming ConnectivityManager intent bundle. - * @return True if the MPD object connection configuration is current to the current - * WIFI/SSID network configuration, false otherwise. - */ - private boolean isConnectedToWIFI(final Bundle bundle) { - final String potentialSSID = bundle.getString(ConnectivityManager.EXTRA_EXTRA_INFO); - /** Remove quotes */ - final String hostPreferenceName = - potentialSSID.substring(1, potentialSSID.length() - 1) + "hostname"; - - final String hostname = mSettings.getString(hostPreferenceName, null); - - return isLinkedToHostname(hostname); - } - - /** - * Checks if the MPD object connection configuration is current - * to the network type shared preference configuration. - * - * @param bundle The incoming ConnectivityManager intent bundle. - * @return True if the shared preference configuration is the same as the configured MPD - * object - * configuration, false otherwise. - */ - private boolean isHostnameLinked(final Bundle bundle) { - final boolean result; - - if (bundle.getInt(ConnectivityManager.EXTRA_NETWORK_TYPE) == - ConnectivityManager.TYPE_WIFI) { - result = isConnectedToWIFI(bundle); - } else { - /** "Default" connection */ - result = isLinkedToHostname(mSettings.getString("hostname", null)); - } - - return result; - } - - /** - * Checks that the MPD object connection configuration is current - * to the network type shared preference configuration hostname. - * - * @param hostname The shared preference hostname. - * @return True if the shared preference hostname configuration is the same - * as the MPD object connection hostname configuration, false otherwise. - */ - private boolean isLinkedToHostname(final String hostname) { - final boolean isConnectedTo; - - if (hostname == null) { - isConnectedTo = false; - } else { - InetAddress hostAddress = null; - - hostAddress = mMPDAsyncHelper.oMPD.getHostAddress(); - - if (hostAddress == null) { - Log.e(TAG, "This should not happen."); - isConnectedTo = false; - } else { - if (DEBUG) { - Log.d(TAG, "Shared preference hostname: " + hostname - + " Canonical host name: " + hostAddress.getCanonicalHostName() - + " host address: " + hostAddress.getHostAddress() - + " host name: " + hostAddress.getHostName()); - } - - if (hostname.equals(hostAddress.getHostAddress()) || - hostname.equals(hostAddress.getHostName()) || - hostname.equals(hostAddress.getCanonicalHostName())) { - if (DEBUG) { - Log.d(TAG, "Connected to this address."); - } - isConnectedTo = true; - } else { - if (DEBUG) { - Log.d(TAG, "Not connected to this address."); - } - isConnectedTo = false; - } - } - } - - return isConnectedTo; - } - - /** - * This is the BroadcastReceiver receiver; this method sets - * up this object for runnable processing off the UI thread. - */ - @Override - public final void onReceive(final Context context, final Intent intent) { - if (intent != null && ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - mSettings = getPreferences(context); - mIntent = intent; - mMPDAsyncHelper.execAsync(this); - } - } - - /** - * The runnable method for execution off the UI thread, then calls back if appropriate. - */ - @Override - public final void run() { - final Bundle extras = mIntent.getExtras(); - - if (DEBUG) { - visualizeIntent(mIntent); - } - - /** - * !mIntent.getBooleanExtra("noConnectivity") == is connected - * !mIntent.getBooleanExtra("noConnectivity", false) if doesn't exist is connected - */ - final boolean isNetworkConnected = !mIntent.getBooleanExtra("noConnectivity", false); - boolean resolved = false; - - if (isNetworkConnected) { - if (DEBUG) { - Log.d(TAG, "Connected."); - } - - if (!isHostnameLinked(extras)) { - mSettingsHelper.updateConnectionSettings(); - mMPDAsyncHelper.reconnect(); - } - - if (isHostnameLinked(extras)) { - resolved = true; - if (mMPDAsyncHelper.oMPD.isConnected()) { - if (DEBUG) { - Log.d(TAG, "Media player is already linked and connected."); - } - } else { - if (DEBUG) { - Log.d(TAG, "Linked, but not connected, sending callback."); - } - mHelperHandler.sendEmptyMessage(MPDAsyncHelper.EVENT_NETWORK_CONNECTED); - } - } else if (DEBUG) { - Log.w(TAG, "Host not linked to the current MPD object."); - } - } else if (DEBUG) { - Log.d(TAG, "Not connected to network."); - } - - /** Specific to a localhost MPD server. */ - if (!resolved) { - /** - * If network is connected, SettingsHelper has already - * updated ConnectionInfo has already been updated. - */ - if (!isNetworkConnected) { - mSettingsHelper.updateConnectionSettings(); - mMPDAsyncHelper.reconnect(); - } - - if ("127.0.0.1".equals(mMPDAsyncHelper.getConnectionSettings().server) && - canConnectToLocalhost()) { - mHelperHandler.sendEmptyMessage(MPDAsyncHelper.EVENT_NETWORK_CONNECTED); - } - } - } -} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/UpdateTrackInfo.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/UpdateTrackInfo.java index ccdc848be2..47fa2e5e66 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/UpdateTrackInfo.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/helpers/UpdateTrackInfo.java @@ -20,9 +20,9 @@ import com.namelessdev.mpdroid.R; import org.a0z.mpd.MPDStatus; -import org.a0z.mpd.Sticker; import org.a0z.mpd.exception.MPDException; import org.a0z.mpd.item.Music; +import org.a0z.mpd.subsystem.Sticker; import android.content.SharedPreferences; import android.os.AsyncTask; diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/library/PlaylistEditActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/library/PlaylistEditActivity.java index 6faa58d8ba..76eb6d3f90 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/library/PlaylistEditActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/library/PlaylistEditActivity.java @@ -18,7 +18,7 @@ import com.mobeta.android.dslv.DragSortController; import com.mobeta.android.dslv.DragSortListView; -import com.namelessdev.mpdroid.MPDroidActivities.MPDroidListActivity; +import com.namelessdev.mpdroid.MPDroidActivities; import com.namelessdev.mpdroid.R; import com.namelessdev.mpdroid.tools.Tools; @@ -33,6 +33,7 @@ import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; +import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListAdapter; @@ -47,8 +48,9 @@ import java.util.LinkedList; import java.util.List; -public class PlaylistEditActivity extends MPDroidListActivity implements StatusChangeListener, - OnClickListener { +public class PlaylistEditActivity extends MPDroidActivities.MPDroidActivity + implements StatusChangeListener, + OnClickListener, AdapterView.OnItemClickListener { private static final String TAG = "PlaylistEditActivity"; @@ -66,6 +68,8 @@ public void remove(final int which) { private String mPlaylistName = null; + private ListView listView; + private final DragSortListView.DropListener mDropListener = new DragSortListView.DropListener() { @Override @@ -125,7 +129,7 @@ public void onClick(final View v) { mApp.oMPDAsyncHelper.oMPD.removeFromPlaylist(mPlaylistName, positions); } if (copy.size() != mSongList.size()) { - ((BaseAdapter) getListAdapter()).notifyDataSetChanged(); + ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged(); } Tools.notifyUser(R.string.removeCountSongs, count); } catch (final Exception e) { @@ -143,6 +147,10 @@ public void onCreate(final Bundle savedInstanceState) { mIsPlayQueue = false; } setContentView(R.layout.playlist_editlist_activity); + + listView = (ListView) findViewById(android.R.id.list); + listView.setOnItemClickListener(this); + if (mIsPlayQueue) { setTitle(R.string.nowPlaying); } else { @@ -151,7 +159,7 @@ public void onCreate(final Bundle savedInstanceState) { update(); mApp.oMPDAsyncHelper.addStatusChangeListener(this); - final DragSortListView trackList = (DragSortListView) getListView(); + final DragSortListView trackList = (DragSortListView) listView; trackList.setOnCreateContextMenuListener(this); trackList.setDropListener(mDropListener); @@ -169,7 +177,7 @@ public void onCreate(final Bundle savedInstanceState) { final Button button = (Button) findViewById(R.id.Remove); button.setOnClickListener(this); - getActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @Override @@ -182,10 +190,9 @@ public void onDestroy() { * Marks the selected item for deletion */ @Override - protected void onListItemClick(final ListView l, final View v, final int position, - final long id) { - super.onListItemClick(l, v, position, id); - final AbstractMap item = mSongList.get(position); + public void onItemClick(final AdapterView adapterView, final View view, final int i, + final long l) { + final AbstractMap item = mSongList.get(i); item.get("marked"); if (item.get("marked").equals(true)) { item.put("marked", false); @@ -193,7 +200,7 @@ protected void onListItemClick(final ListView l, final View v, final int positio item.put("marked", true); } - ((BaseAdapter) getListAdapter()).notifyDataSetChanged(); + ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged(); } @Override @@ -248,13 +255,13 @@ public void trackChanged(final MPDStatus mpdStatus, final int oldTrack) { if (mIsPlayQueue) { // Mark running track... for (final AbstractMap song : mSongList) { - if (((Integer) song.get("songid")).intValue() == mpdStatus.getSongId()) { + if (((Integer) song.get("songid")) == mpdStatus.getSongId()) { song.put("play", android.R.drawable.ic_media_play); } else { song.put("play", 0); } } - final SimpleAdapter adapter = (SimpleAdapter) getListAdapter(); + final SimpleAdapter adapter = (SimpleAdapter) listView.getAdapter(); if (adapter != null) { adapter.notifyDataSetChanged(); } @@ -273,8 +280,8 @@ protected void update() { } mSongList = new ArrayList<>(); final int playingID = mApp.oMPDAsyncHelper.oMPD.getStatus().getSongId(); - final int pos = null == getListView() ? -1 : getListView().getFirstVisiblePosition(); - final View view = null == getListView() ? null : getListView().getChildAt(0); + final int pos = null == listView ? -1 : listView.getFirstVisiblePosition(); + final View view = null == listView ? null : listView.getChildAt(0); final int top = null == view ? -1 : view.getTop(); int listPlayingId = 0; int playlistPosition = 0; @@ -306,15 +313,17 @@ protected void update() { R.id.picture, android.R.id.text1, android.R.id.text2, R.id.removeCBox }); - setListAdapter(songs); - if (mIsFirstRefresh) { - mIsFirstRefresh = false; - if (listPlayingId > 0) { - setSelection(listPlayingId); - } - } else { - if (-1 != pos && -1 != top) { - getListView().setSelectionFromTop(pos, top); + if (listView != null) { + listView.setAdapter(songs); + if (mIsFirstRefresh) { + mIsFirstRefresh = false; + if (listPlayingId > 0) { + listView.setSelection(listPlayingId); + } + } else { + if (-1 != pos && -1 != top) { + listView.setSelectionFromTop(pos, top); + } } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/library/SimpleLibraryActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/library/SimpleLibraryActivity.java index 48f9ae594b..3eed6b111f 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/library/SimpleLibraryActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/library/SimpleLibraryActivity.java @@ -16,7 +16,7 @@ package com.namelessdev.mpdroid.library; -import com.namelessdev.mpdroid.MPDroidActivities.MPDroidFragmentActivity; +import com.namelessdev.mpdroid.MPDroidActivities; import com.namelessdev.mpdroid.R; import com.namelessdev.mpdroid.fragments.AlbumsFragment; import com.namelessdev.mpdroid.fragments.AlbumsGridFragment; @@ -30,7 +30,7 @@ import org.a0z.mpd.item.Album; import org.a0z.mpd.item.Artist; -import android.app.ActionBar; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -41,12 +41,13 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.widget.TextView; -public class SimpleLibraryActivity extends MPDroidFragmentActivity implements +public class SimpleLibraryActivity extends MPDroidActivities.MPDroidActivity implements ILibraryFragmentActivity, OnBackStackChangedListener { private static final String EXTRA_ALBUM = "album"; @@ -57,41 +58,59 @@ public class SimpleLibraryActivity extends MPDroidFragmentActivity implements private static final String EXTRA_STREAM = "streams"; + private static final String TAG = "SimpleLibraryActivity"; + private TextView mTitleView; + private String debugIntent(final Intent intent) { + final ComponentName callingActivity = getCallingActivity(); + final StringBuilder stringBuilder = new StringBuilder(); + final Bundle extras = intent.getExtras(); + + stringBuilder.append("SimpleLibraryActivity started with invalid extra"); + + if (callingActivity != null) { + stringBuilder.append(", calling activity: "); + stringBuilder.append(callingActivity.getClassName()); + } + + if (extras != null) { + for (final String what : extras.keySet()) { + stringBuilder.append(", intent extra: "); + stringBuilder.append(what); + } + } + + stringBuilder.append('.'); + return stringBuilder.toString(); + } + private Fragment getRootFragment() { final Intent intent = getIntent(); - Fragment rootFragment = null; + final Fragment rootFragment; - if (intent.getBooleanExtra(EXTRA_STREAM, false)) { - rootFragment = new StreamsFragment(); - } else { - Object targetElement = intent.getParcelableExtra(EXTRA_ALBUM); - if (targetElement == null) { - targetElement = intent.getParcelableExtra(EXTRA_ARTIST); - } - if (targetElement == null) { - targetElement = intent.getStringExtra(EXTRA_FOLDER); - } - if (targetElement == null) { - throw new RuntimeException( - "Error : cannot start SimpleLibraryActivity without an extra"); + if (intent.hasExtra(EXTRA_ALBUM)) { + final Album album = intent.getParcelableExtra(EXTRA_ALBUM); + + rootFragment = new SongsFragment().init(album); + } else if (intent.hasExtra(EXTRA_ARTIST)) { + final Artist artist = intent.getParcelableExtra(EXTRA_ARTIST); + final SharedPreferences settings = PreferenceManager + .getDefaultSharedPreferences(mApp); + + if (settings.getBoolean(LibraryFragment.PREFERENCE_ALBUM_LIBRARY, true)) { + rootFragment = new AlbumsGridFragment(artist); } else { - if (targetElement instanceof Artist) { - final SharedPreferences settings = PreferenceManager - .getDefaultSharedPreferences(mApp); - - if (settings.getBoolean(LibraryFragment.PREFERENCE_ALBUM_LIBRARY, true)) { - rootFragment = new AlbumsGridFragment((Artist) targetElement); - } else { - rootFragment = new AlbumsFragment((Artist) targetElement); - } - } else if (targetElement instanceof Album) { - rootFragment = new SongsFragment().init((Album) targetElement); - } else if (targetElement instanceof String) { - rootFragment = new FSFragment().init((String) targetElement); - } + rootFragment = new AlbumsFragment(artist); } + } else if (intent.hasExtra(EXTRA_FOLDER)) { + final String folder = intent.getStringExtra(EXTRA_FOLDER); + + rootFragment = new FSFragment().init(folder); + } else if (intent.hasExtra(EXTRA_STREAM)) { + rootFragment = new StreamsFragment(); + } else { + throw new IllegalStateException(debugIntent(intent)); } return rootFragment; @@ -117,7 +136,7 @@ protected void onCreate(final Bundle savedInstanceState) { mTitleView.setSelected(true); mTitleView.requestFocus(); - final ActionBar actionBar = getActionBar(); + final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setCustomView(mTitleView); actionBar.setDisplayShowTitleEnabled(false); @@ -127,18 +146,18 @@ protected void onCreate(final Bundle savedInstanceState) { if (savedInstanceState == null) { final Fragment rootFragment = getRootFragment(); - if (rootFragment != null) { - if (rootFragment instanceof BrowseFragment) { - setTitle(((BrowseFragment) rootFragment).getTitle()); - } - final FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - - ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); - ft.replace(R.id.root_frame, rootFragment); - ft.commit(); - } else { + if (rootFragment == null) { throw new RuntimeException("Error : SimpleLibraryActivity root fragment is null"); } + + if (rootFragment instanceof BrowseFragment) { + setTitle(((BrowseFragment) rootFragment).getTitle()); + } + final FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.replace(R.id.root_frame, rootFragment); + ft.commit(); } else { refreshTitleFromCurrentFragment(); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/locale/EditActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/locale/EditActivity.java index 8ccbd7d539..a09af0a24d 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/locale/EditActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/locale/EditActivity.java @@ -23,11 +23,11 @@ import org.a0z.mpd.MPDCommand; -import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -38,7 +38,7 @@ import java.util.ArrayList; import java.util.List; -public class EditActivity extends Activity implements AdapterView.OnItemClickListener { +public class EditActivity extends ActionBarActivity implements AdapterView.OnItemClickListener { public static final String BUNDLE_ACTION_EXTRA = "ACTION_EXTRA"; diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/service/MPDroidService.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/service/MPDroidService.java index 0405d5a95c..d5e3a22aa4 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/service/MPDroidService.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/service/MPDroidService.java @@ -54,7 +54,6 @@ public final class MPDroidService extends Service implements AudioManager.OnAudioFocusChangeListener, MPDAsyncHelper.ConnectionInfoListener, - MPDAsyncHelper.NetworkMonitorListener, StatusChangeListener { /** Enable this to get various DEBUG messages from this module. */ @@ -81,6 +80,8 @@ public final class MPDroidService extends Service implements /** The main process connection changed. */ public static final int CONNECTION_INFO_CHANGED = LOCAL_UID + 6; + public static final int REFRESH_COVER = LOCAL_UID + 7; + /** The {@code MPDAsyncHelper} for this service. */ static final MPDAsyncHelper MPD_ASYNC_HELPER = new MPDAsyncHelper(false); @@ -239,12 +240,7 @@ private void initializeAsyncHelper() { }); } - if (!MPD_ASYNC_HELPER.isNetworkMonitorAlive()) { - MPD_ASYNC_HELPER.startNetworkMonitor(this); - } - MPD_ASYNC_HELPER.addStatusChangeListener(this); - MPD_ASYNC_HELPER.addNetworkMonitorListener(this); /** * From here, upon successful connection, it will go from connectionStateChanged to * stateChanged() where handlers will be started as required. @@ -263,7 +259,7 @@ private void initializeNotification() { mNotificationHandler = new NotificationHandler(this); mAlbumCoverHandler = new AlbumCoverHandler(this); mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); - mRemoteControlClientHandler = new RemoteControlClientHandler(this); + mRemoteControlClientHandler = new RemoteControlClientHandler(this, mHandler); } else { mNotificationHandler.start(); mRemoteControlClientHandler.start(); @@ -382,21 +378,6 @@ public void onDestroy() { mHandler.removeCallbacksAndMessages(null); } - /** - * This method is called when a network has connected that matches the MPD server settings. - */ - @Override - public void onNetworkConnect() { - if (DEBUG) { - Log.d(TAG, "onNetworkConnect"); - } - if (isNotificationPersistent()) { - windDownHandlers(false); - mIsNotificationStarted = false; - startNotification(); - } - } - /** * This is one of the methods to start the service, and to keep the service running * semi-persistently. This method also receives incoming intents. @@ -793,9 +774,9 @@ private void updateTrack(final MPDStatus mpdStatus) { mCurrentTrack = MPD_ASYNC_HELPER.oMPD.getPlaylist().getByIndex(songPos); if (mNotificationHandler != null && mCurrentTrack != null) { + mRemoteControlClientHandler.update(mCurrentTrack); mAlbumCoverHandler.update(new AlbumInfo(mCurrentTrack)); mNotificationHandler.setNewTrack(mCurrentTrack); - mRemoteControlClientHandler.update(mCurrentTrack); } } @@ -837,7 +818,6 @@ private void windDownHandlers(final boolean stopSelf) { * causes a bug with the weak linked list, somehow. */ MPD_ASYNC_HELPER.stopStatusMonitor(); - MPD_ASYNC_HELPER.stopNetworkMonitor(this); MPD_ASYNC_HELPER.disconnect(); } } @@ -990,6 +970,9 @@ private void handleServiceMessages(final Message msg) { case STOP_SELF: haltService(); break; + case REFRESH_COVER: + mAlbumCoverHandler.update(new AlbumInfo(mCurrentTrack)); + break; case UPDATE_CLIENT_STATUS: sendHandlerStatus(); break; diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/service/RemoteControlClientHandler.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/service/RemoteControlClientHandler.java index 670dc9c810..0c6991901a 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/service/RemoteControlClientHandler.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/service/RemoteControlClientHandler.java @@ -18,17 +18,20 @@ import com.namelessdev.mpdroid.RemoteControlReceiver; +import org.a0z.mpd.MPD; import org.a0z.mpd.MPDStatus; import org.a0z.mpd.item.Music; import android.app.PendingIntent; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; import android.os.Build; +import android.os.Handler; import android.text.format.DateUtils; import android.util.Log; @@ -64,11 +67,15 @@ public class RemoteControlClientHandler implements AlbumCoverHandler.FullSizeCal /** The RemoteControlClient Seekbar handled by the {@code RemoteControlClientSeekBarHandler}. */ private RemoteControlSeekBarHandler mSeekBar = null; - RemoteControlClientHandler(final MPDroidService serviceContext) { + private final Handler mServiceHandler; + + RemoteControlClientHandler(final MPDroidService serviceContext, final Handler serviceHandler) { super(); + mServiceHandler = serviceHandler; + mAudioManager = - (AudioManager) serviceContext.getSystemService(serviceContext.AUDIO_SERVICE); + (AudioManager) serviceContext.getSystemService(Context.AUDIO_SERVICE); mMediaButtonReceiverComponent = new ComponentName(serviceContext, RemoteControlReceiver.class); @@ -134,9 +141,46 @@ private void getRemoteState(final int state) { */ @Override public final void onCoverUpdate(final Bitmap albumCover) { - mRemoteControlClient.editMetadata(false) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, albumCover) - .apply(); + if (albumCover != null && albumCover.isRecycled()) { + Log.e(TAG, "onCoverUpdate() entered with recycled bitmap."); + mServiceHandler.sendEmptyMessage(MPDroidService.REFRESH_COVER); + } else { + new Thread(new Runnable() { + @Override + public void run() { + try { + /** + * This is an ugly fix for now. I will clean this up sooner or later. + */ + final MPD mpd = MPDroidService.MPD_ASYNC_HELPER.oMPD; + final int songPos = mpd.getStatus().getSongPos(); + final Music currentTrack = mpd.getPlaylist().getByIndex(songPos); + + mRemoteControlClient.editMetadata(true) + .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, + albumCover) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, + currentTrack.getAlbum()) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, + currentTrack.getAlbumArtist()) + .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, + currentTrack.getArtist()) + .putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, + (long) currentTrack.getTrack()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, + (long) currentTrack.getDisc()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, + currentTrack.getTime() * DateUtils.SECOND_IN_MILLIS) + .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, + currentTrack.getTitle()) + .apply(); + } catch (final IllegalStateException e) { + Log.w(TAG, "RemoteControlClient bug workaround", e); + mServiceHandler.sendEmptyMessage(MPDroidService.REFRESH_COVER); + } + } + }).start(); + } } /** @@ -203,17 +247,6 @@ final void stop() { } } - /** - * Updates the current album art. - * - * @param albumCover The current track album art. - */ - final void update(final Bitmap albumCover) { - mRemoteControlClient.editMetadata(false) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, albumCover) - .apply(); - } - /** * Update the current metadata information. * @@ -223,22 +256,26 @@ final void update(final Music currentTrack) { new Thread(new Runnable() { @Override public void run() { - mRemoteControlClient.editMetadata(false) - .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, - currentTrack.getAlbum()) - .putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, - currentTrack.getAlbumArtist()) - .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, - currentTrack.getArtist()) - .putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, - (long) currentTrack.getTrack()) - .putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, - (long) currentTrack.getDisc()) - .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, - currentTrack.getTime() * DateUtils.SECOND_IN_MILLIS) - .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, - currentTrack.getTitle()) - .apply(); + try { + mRemoteControlClient.editMetadata(false) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, + currentTrack.getAlbum()) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, + currentTrack.getAlbumArtist()) + .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, + currentTrack.getArtist()) + .putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, + (long) currentTrack.getTrack()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, + (long) currentTrack.getDisc()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, + currentTrack.getTime() * DateUtils.SECOND_IN_MILLIS) + .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, + currentTrack.getTitle()) + .apply(); + } catch (final IllegalStateException e) { + Log.w(TAG, "RemoteControlClient bug workaround", e); + } } }).start(); } diff --git a/MPDroid/src/main/res/layout-land/main_activity_nagvigation_tablet.xml b/MPDroid/src/main/res/layout-land/main_activity_nagvigation_tablet.xml index 2b918acf61..6d191c81f8 100644 --- a/MPDroid/src/main/res/layout-land/main_activity_nagvigation_tablet.xml +++ b/MPDroid/src/main/res/layout-land/main_activity_nagvigation_tablet.xml @@ -21,7 +21,7 @@ + android:paddingTop="?attr/actionBarSize"> + android:paddingTop="?attr/actionBarSize"> + android:paddingTop="?attr/actionBarSize"> + + \ 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 61d8f6c18a..1a293cdd9d 100644 --- a/MPDroid/src/main/res/values-de/strings.xml +++ b/MPDroid/src/main/res/values-de/strings.xml @@ -1,6 +1,4 @@ - - - + - @color/nowplaying_background 0dp + 0dp - - - - - - - - - - + -