Example #1
0
 @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);
   }
 }
Example #2
0
/**
 * 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;
    }
  }
}
Example #3
0
/** 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();
  }
}
Example #4
0
 /**
  * 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);
 }
Example #5
0
 public static void run(final String userCommand, final long l) {
   run(MPDApplication.getInstance().oMPDAsyncHelper.oMPD, userCommand, l);
 }