@Override protected void asyncUpdate() { try { final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MPDApplication.getInstance()); 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)); } break; case PREFERENCE_ARTIST_TAG_TO_USE_ARTIST: if (mGenre == null) { replaceItems(mApp.getMPD().getArtists()); } else { replaceItems(mApp.getMPD().getArtists(mGenre)); } break; case PREFERENCE_ARTIST_TAG_TO_USE_BOTH: default: if (mGenre == null) { replaceItems(mApp.getMPD().getArtistsMerged()); } else { replaceItems(mApp.getMPD().getArtistsMerged(mGenre)); } break; } Collections.sort(mItems); } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to update.", e); } }
/** * This sets up an AsyncTask to gather and parse all the information to update the track information * outside of the UI thread, then sends a callback to the resource listeners. */ public class UpdateTrackInfo { private static final boolean DEBUG = false; private static final String TAG = "UpdateTrackInfo"; private final MPDApplication mApp = MPDApplication.getInstance(); private final MPDStatus mMPDStatus = mApp.getMPD().getStatus(); private final SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(mApp); private final Sticker mSticker; private boolean mForceCoverUpdate = false; private FullTrackInfoUpdate mFullTrackInfoListener = null; private String mLastAlbum = null; private String mLastArtist = null; private TrackInfoUpdate mTrackInfoListener = null; public UpdateTrackInfo() { mSticker = mApp.getMPD().getStickerManager(); } public final void addCallback(final FullTrackInfoUpdate listener) { mFullTrackInfoListener = listener; } public final void addCallback(final TrackInfoUpdate listener) { mTrackInfoListener = listener; } public final void refresh() { refresh(false); } public final void refresh(final boolean forceCoverUpdate) { mForceCoverUpdate = forceCoverUpdate; new UpdateTrackInfoAsync().execute(); } public final void removeCallback(final TrackInfoUpdate ignored) { mTrackInfoListener = null; } public final void removeCallback(final FullTrackInfoUpdate ignored) { mFullTrackInfoListener = null; } public interface FullTrackInfoUpdate { /** * This is called when cover art needs to be updated due to server information change. * * @param albumInfo The current albumInfo */ void onCoverUpdate(AlbumInfo albumInfo); /** * Called when a track information change has been detected. * * @param updatedSong The currentSong item object. * @param trackRating The current song rating. * @param album The album change. * @param artist The artist change. * @param date The date change. * @param title The title change. */ void onTrackInfoUpdate( Music updatedSong, final float trackRating, CharSequence album, CharSequence artist, CharSequence date, CharSequence title); } public interface TrackInfoUpdate { /** * This is called when cover art needs to be updated due to server information change. * * @param albumInfo The current albumInfo */ void onCoverUpdate(AlbumInfo albumInfo); /** * Called when a track information change has been detected. * * @param artist The artist change. * @param title The title change. */ void onTrackInfoUpdate(CharSequence artist, CharSequence title); } private class UpdateTrackInfoAsync extends AsyncTask<Void, Music, Music> { private AlbumInfo mAlbumInfo = null; private String mAlbumName = null; private String mArtistName = null; private String mDate = null; private boolean mHasCoverChanged = false; private String mTitle = null; private float mTrackRating; /** * Gather and parse all song track information necessary after change. * * @param params A {@code MPDStatus} object array. * @return A null {@code Void} object, ignore it. */ @Override protected final Music doInBackground(final Void... params) { try { mMPDStatus.waitForValidity(); mApp.getMPD().getPlaylist().waitForValidity(); } catch (final InterruptedException ignored) { } final Music currentTrack = mApp.getMPD().getCurrentTrack(); if (currentTrack != null) { if (currentTrack.isStream()) { final String title = currentTrack.getTitle(); if (title != null && !title.isEmpty()) { mAlbumName = currentTrack.getName(); mTitle = currentTrack.getTitle(); } else { mTitle = currentTrack.getName(); } mArtistName = currentTrack.getArtistName(); mAlbumInfo = new AlbumInfo(mArtistName, mAlbumName); } else { mAlbumName = currentTrack.getAlbumName(); mDate = Long.toString(currentTrack.getDate()); if (mDate.isEmpty() || mDate.charAt(0) == '-') { mDate = ""; } else { mDate = " - " + mDate; } mTitle = currentTrack.getTitle(); mTitle = addDiscAndTrackNumber(mTitle, currentTrack); setArtist(currentTrack); mAlbumInfo = new AlbumInfo(currentTrack); } mHasCoverChanged = hasCoverChanged(); mTrackRating = getTrackRating(currentTrack); if (DEBUG) { Log.i(TAG, toString()); } } mLastAlbum = mAlbumName; mLastArtist = mArtistName; return currentTrack; } /** * This method retrieves the current rating sticker from the connected media server. * * @param currentTrack The current playing {@link Music} item. * @return Returns the current rating sticker from the connected media server. */ private float getTrackRating(final FilesystemTreeEntry currentTrack) { float rating = 0.0f; if (currentTrack != null && mSticker.isAvailable() && mSettings.getBoolean("enableRating", false)) { try { rating = (float) mSticker.getRating(currentTrack) / 2.0f; } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to get the current track rating.", e); } } return rating; } private boolean hasCoverChanged() { final boolean invalid = mArtistName == null || mAlbumName == null; return invalid || !mArtistName.equals(mLastArtist) || !mAlbumName.equals(mLastAlbum); } /** Send out the messages to listeners. */ @Override protected final void onPostExecute(final Music result) { super.onPostExecute(result); final boolean sendCoverUpdate = mHasCoverChanged || result == null || mForceCoverUpdate; if (result == null) { mTitle = mApp.getResources().getString(R.string.noSongInfo); } if (mFullTrackInfoListener != null) { mFullTrackInfoListener.onTrackInfoUpdate( result, mTrackRating, mAlbumName, mArtistName, mDate, mTitle); if (sendCoverUpdate) { if (DEBUG) { Log.e(TAG, "Sending cover update to full track info listener."); } mFullTrackInfoListener.onCoverUpdate(mAlbumInfo); } } if (mTrackInfoListener != null) { mTrackInfoListener.onTrackInfoUpdate(mAlbumName, mTitle); if (sendCoverUpdate) { if (DEBUG) { Log.d(TAG, "Sending cover update to track info listener."); } mTrackInfoListener.onCoverUpdate(mAlbumInfo); } } } /** * If not a stream, this sets up the mArtistName based on artist and album information. * * @param currentTrack The current playing {@link Music} item. */ private void setArtist(final Music currentTrack) { final boolean showAlbumArtist = mSettings.getBoolean("showAlbumArtist", true); final String albumArtist = currentTrack.getAlbumArtistName(); mArtistName = currentTrack.getArtistName(); if (mArtistName == null || mArtistName.isEmpty()) { mArtistName = albumArtist; } else if (showAlbumArtist && albumArtist != null && !mArtistName.toLowerCase().contains(albumArtist.toLowerCase())) { mArtistName = albumArtist + " / " + mArtistName; } } @Override public String toString() { return "UpdateTrackInfoAsync{" + "mAlbumInfo=" + mAlbumInfo + ", mAlbumName='" + mAlbumName + '\'' + ", mArtistName='" + mArtistName + '\'' + ", mDate='" + mDate + '\'' + ", mHasCoverChanged=" + mHasCoverChanged + ", mTitle='" + mTitle + '\'' + ", mTrackRating=" + mTrackRating + "} " + super.toString(); } private String addDiscAndTrackNumber(String title, final Music track) { final int tracknum = track.getTrack(); final int discnum = track.getDisc(); if (tracknum > -1) { title = tracknum + "] " + title; if (discnum > -1) { title = discnum + ":" + title; } title = "[" + title; } return title; } } }
/** This class contains simple server control methods. */ public final class MPDControl { /** If these are sent to the run() class, the volume will not change. */ public static final int INVALID_INT = -5; public static final long INVALID_LONG = -5L; private static final String TAG = "MPDControl"; private static final String FULLY_QUALIFIED_NAME = "com.namelessdev.mpdroid.helpers" + '.' + TAG + '.'; /** The following are server action commands. */ public static final String ACTION_CONSUME = FULLY_QUALIFIED_NAME + "CONSUME"; public static final String ACTION_MUTE = FULLY_QUALIFIED_NAME + "MUTE"; public static final String ACTION_NEXT = FULLY_QUALIFIED_NAME + "NEXT"; public static final String ACTION_PAUSE = FULLY_QUALIFIED_NAME + "PAUSE"; public static final String ACTION_PLAY = FULLY_QUALIFIED_NAME + "PLAY"; public static final String ACTION_PREVIOUS = FULLY_QUALIFIED_NAME + "PREVIOUS"; public static final String ACTION_SEEK = FULLY_QUALIFIED_NAME + "SEEK"; public static final String ACTION_VOLUME_SET = FULLY_QUALIFIED_NAME + "SET_VOLUME"; public static final String ACTION_SINGLE = FULLY_QUALIFIED_NAME + "SINGLE"; public static final String ACTION_STOP = FULLY_QUALIFIED_NAME + "STOP"; public static final String ACTION_TOGGLE_PLAYBACK = FULLY_QUALIFIED_NAME + "PLAY_PAUSE"; public static final String ACTION_TOGGLE_RANDOM = FULLY_QUALIFIED_NAME + "RANDOM"; public static final String ACTION_TOGGLE_REPEAT = FULLY_QUALIFIED_NAME + "REPEAT"; public static final String ACTION_VOLUME_STEP_DOWN = FULLY_QUALIFIED_NAME + "VOLUME_STEP_DOWN"; public static final String ACTION_VOLUME_STEP_UP = FULLY_QUALIFIED_NAME + "VOLUME_STEP_UP"; private static final int VOLUME_STEP = 5; private static final MPDApplication app = MPDApplication.getInstance(); private static boolean manualConnection = false; static { /* This is strictly required for manual connection */ if (app.oMPDAsyncHelper.getConnectionSettings().sServer == null) { final SettingsHelper sh = new SettingsHelper(app.oMPDAsyncHelper); sh.updateConnectionSettings(); manualConnection = true; } } private MPDControl() { super(); } /** * An overload method to use the standard {@code MPD} object and no {@code userCommand} argument. * * @param userCommand The command to be run. */ public static void run(final String userCommand) { run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, INVALID_LONG); } /** * An overload for the {@code run(mpd, userCommand, long)} method which uses the standard {@code * MPD} object. * * @param userCommand The command to be run. * @param i An integer which will be cast to long for run for userCommand argument. */ public static void run(final String userCommand, final int i) { run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, (long) i); } public static void run(final String userCommand, final long l) { run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, l); } /** * An overload for the {@code run(mpd, userCommand, long)} method which translates resource id * into a native command. * * @param resId A resource id. */ public static void run(final int resId) { switch (resId) { case R.id.next: run(ACTION_NEXT); break; case R.id.prev: run(ACTION_PREVIOUS); break; case R.id.playpause: run(ACTION_TOGGLE_PLAYBACK); break; case R.id.repeat: run(ACTION_TOGGLE_REPEAT); break; case R.id.shuffle: run(ACTION_TOGGLE_RANDOM); break; case R.id.stop: run(ACTION_STOP); break; default: break; } } /** * Overload method for the {@code run(mpd, userCommand, long)} method which allows an integer as * the final parameter. * * @param mpd An {@code MPD} object. * @param userCommand The command to be run. * @param i An integer which will be cast to long for run. */ public static void run(final MPD mpd, final String userCommand, final int i) { run(mpd, userCommand, (long) i); } /** * The parent method which runs the command (above) in a thread. * * @param mpd An {@code MPD} object. * @param userCommand The command to be run. * @param l A long primitive argument for the {@code userCommand}. */ public static void run(final MPD mpd, final String userCommand, final long l) { new Thread( new Runnable() { /** * A simple status retrieval method. * * @return An {@code MPDStatus} state string. */ private String getStatus() { String state = null; try { if (manualConnection) { state = mpd.getStatus(true).getState(); } else { state = mpd.getStatus().getState(); } } catch (final MPDServerException e) { Log.e(TAG, "Failed to receive a current status", e); } return state; } private boolean isPaused() { return MPDStatus.MPD_STATE_PAUSED.equals(getStatus()); } private boolean isPlaying() { return MPDStatus.MPD_STATE_PLAYING.equals(getStatus()); } private void getManualConnection() { try { final MPDAsyncHelper.MPDConnectionInfo conInfo = app.oMPDAsyncHelper.getConnectionSettings(); mpd.connect(conInfo.sServer, conInfo.iPort, conInfo.sPassword); manualConnection = true; } catch (final MPDServerException | UnknownHostException e) { Log.d(TAG, "Failed to get a connection.", e); } } @Override public final void run() { String command = userCommand; if (!mpd.isConnected()) { getManualConnection(); } /** This switch translates for the next switch. */ switch (command) { case ACTION_TOGGLE_PLAYBACK: if (isPlaying()) { command = ACTION_PAUSE; } else { command = ACTION_PLAY; } break; default: break; } /** The main switch for running the command. */ try { switch (command) { case ACTION_CONSUME: mpd.setConsume(!mpd.getStatus().isConsume()); break; case ACTION_MUTE: mpd.setVolume(0); break; case ACTION_NEXT: mpd.next(); break; case ACTION_PAUSE: if (!isPaused()) { mpd.pause(); } break; case ACTION_PLAY: mpd.play(); break; case ACTION_PREVIOUS: mpd.previous(); break; case ACTION_SEEK: long li = l; if (li == INVALID_LONG) { li = 0L; } mpd.seek(li); break; case ACTION_STOP: mpd.stop(); break; case ACTION_SINGLE: mpd.setSingle(!mpd.getStatus().isSingle()); break; case ACTION_TOGGLE_RANDOM: mpd.setRandom(!mpd.getStatus().isRandom()); break; case ACTION_TOGGLE_REPEAT: mpd.setRepeat(!mpd.getStatus().isRepeat()); break; case ACTION_VOLUME_SET: if (l != INVALID_LONG) { mpd.setVolume((int) l); } break; case ACTION_VOLUME_STEP_DOWN: mpd.adjustVolume(-VOLUME_STEP); break; case ACTION_VOLUME_STEP_UP: mpd.adjustVolume(VOLUME_STEP); break; default: break; } } catch (final MPDServerException e) { Log.w(TAG, "Failed to send a simple MPD command.", e); } /** Let MPD disconnect itself. */ } }) .start(); } }
/** * An overload method to use the standard {@code MPD} object and no {@code userCommand} argument. * * @param userCommand The command to be run. */ public static void run(final String userCommand) { run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, INVALID_LONG); }
public static void run(final String userCommand, final long l) { run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, l); }