com.audiokernel.euphonyrmt.fragments.QueueFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.audiokernel.euphonyrmt.fragments.QueueFragment.java

Source

/*
 * 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.audiokernel.euphonyrmt.fragments;

import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView;
import com.audiokernel.euphonyrmt.MPDApplication;
import com.audiokernel.euphonyrmt.MainMenuActivity;
import com.audiokernel.euphonyrmt.R;
import com.audiokernel.euphonyrmt.helpers.AlbumCoverDownloadListener;
import com.audiokernel.euphonyrmt.helpers.AlbumInfo;
import com.audiokernel.euphonyrmt.helpers.CoverAsyncHelper;
import com.audiokernel.euphonyrmt.helpers.CoverDownloadListener;
import com.audiokernel.euphonyrmt.helpers.QueueControl;
import com.audiokernel.euphonyrmt.library.SimpleLibraryActivity;
import com.audiokernel.euphonyrmt.models.AbstractPlaylistMusic;
import com.audiokernel.euphonyrmt.models.PlaylistSong;
import com.audiokernel.euphonyrmt.models.PlaylistStream;
import com.audiokernel.euphonyrmt.tools.Tools;
import com.audiokernel.euphonyrmt.views.holders.PlayQueueViewHolder;

import org.a0z.mpd.MPD;
import org.a0z.mpd.MPDPlaylist;
import org.a0z.mpd.MPDStatus;
import org.a0z.mpd.event.StatusChangeListener;
import org.a0z.mpd.exception.MPDException;
import org.a0z.mpd.item.Item;
import org.a0z.mpd.item.Music;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import static android.text.TextUtils.isEmpty;

/**
 * A fragment for showing the media player's current playlist queue.
 */
public class QueueFragment extends ListFragment implements StatusChangeListener, OnMenuItemClickListener {

    protected static final boolean DEBUG = false;

    // Minimum number of songs in the queue before the fastscroll thumb is shown
    protected static final int MIN_SONGS_BEFORE_FASTSCROLL = 50;

    private static final String TAG = "QueueFragment";

    protected final MPDApplication mApp = MPDApplication.getInstance();

    protected final boolean mLightTheme = mApp.isLightThemeSelected();

    protected ActionMode mActionMode;

    protected FragmentActivity mActivity;

    protected DragSortController mController;

    protected String mFilter = null;

    protected final DragSortListView.DropListener mDropListener = new DragSortListView.DropListener() {
        @Override
        public void drop(final int from, final int to) {
            if (from != to && mFilter == null) {
                final AbstractPlaylistMusic itemFrom = mSongList.get(from);
                final int songID = itemFrom.getSongId();

                QueueControl.run(QueueControl.MOVE, songID, to);
            }
        }
    };

    protected int mLastPlayingID = -1;

    protected DragSortListView mList;

    protected String mPlaylistToSave = "";

    protected Integer mPopupSongID;

    protected final View.OnClickListener mItemMenuButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            mPopupSongID = (Integer) v.getTag();
            final PopupMenu popupMenu = new PopupMenu(mActivity, v);
            popupMenu.getMenuInflater().inflate(R.menu.mpd_playlistcnxmenu, popupMenu.getMenu());
            if (getPlaylistItemSong(mPopupSongID).isStream()) {
                popupMenu.getMenu().findItem(R.id.PLCX_goto).setVisible(false);
            }
            popupMenu.setOnMenuItemClickListener(QueueFragment.this);
            popupMenu.show();
        }
    };

    protected ViewGroup mRootView;

    protected SearchView mSearchView;

    protected ArrayList<AbstractPlaylistMusic> mSongList;

    @Override
    public void connectionStateChanged(final boolean connected, final boolean connectionLost) {
    }

    protected AbstractPlaylistMusic getPlaylistItemSong(final int songID) {
        AbstractPlaylistMusic song = null;
        for (final AbstractPlaylistMusic music : mSongList) {
            if (music.getSongId() == songID) {
                song = music;
                break;
            }
        }
        return song;
    }

    protected boolean isFiltered(final String item) {
        final String processedItem;

        if (item == null) {
            processedItem = "";
        } else {
            processedItem = item.toLowerCase(Locale.getDefault());
        }

        return processedItem.contains(mFilter);
    }

    @Override
    public void libraryStateChanged(final boolean updating, final boolean dbChanged) {
    }

    @Override
    public void onActivityCreated(final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mActivity = getActivity();
        refreshListColorCacheHint();
    }

    /*
     * Create Menu for Playlist View
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.mpd_playlistmenu, menu);
        menu.removeItem(R.id.PLM_EditPL);
    }

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
            final Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        mRootView = container;
        final View view = inflater.inflate(R.layout.playlist_activity, container, false);
        mSearchView = (SearchView) view.findViewById(R.id.search);
        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextChange(final String newText) {
                mFilter = newText;
                if (newText != null && newText.isEmpty()) {
                    mFilter = null;
                }
                if (mFilter != null) {
                    mFilter = mFilter.toLowerCase();
                }
                mList.setDragEnabled(mFilter == null);
                update(false);
                return false;
            }

            @Override
            public boolean onQueryTextSubmit(final String query) {
                // Hide the keyboard and give focus to the list
                final InputMethodManager imm = (InputMethodManager) mActivity
                        .getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
                mList.requestFocus();
                return true;
            }
        });
        mList = (DragSortListView) view.findViewById(android.R.id.list);
        mList.requestFocus();
        mList.setDropListener(mDropListener);
        mController = new DragSortController(mList);
        mController.setDragHandleId(R.id.cover);
        mController.setRemoveEnabled(false);
        mController.setSortEnabled(true);
        mController.setDragInitMode(1);

        mList.setFloatViewManager(mController);
        mList.setOnTouchListener(mController);
        mList.setDragEnabled(true);

        refreshListColorCacheHint();
        mList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
        mList.setMultiChoiceModeListener(new MultiChoiceModeListener() {

            @Override
            public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {

                final SparseBooleanArray checkedItems = mList.getCheckedItemPositions();
                final int count = mList.getCount();
                final ListAdapter adapter = mList.getAdapter();
                final int itemId = item.getItemId();
                int j = 0;
                int[] positions = null;
                boolean result = true;

                if (itemId == R.id.menu_delete) {
                    positions = new int[mList.getCheckedItemCount()];
                    for (int i = 0; i < count && j < positions.length; i++) {
                        if (checkedItems.get(i)) {
                            positions[j] = ((Music) adapter.getItem(i)).getSongId();
                            j++;
                        }
                    }
                } else if (itemId == R.id.menu_crop) {
                    positions = new int[mList.getCount() - mList.getCheckedItemCount()];
                    for (int i = 0; i < count && j < positions.length; i++) {
                        if (!checkedItems.get(i)) {
                            positions[j] = ((Music) adapter.getItem(i)).getSongId();
                            j++;
                        }
                    }
                } else {
                    result = false;
                }

                if (j > 0) {
                    QueueControl.run(QueueControl.REMOVE_BY_ID, positions);
                    mode.finish();
                }

                return result;
            }

            @Override
            public boolean onCreateActionMode(final ActionMode mode, final Menu menu) {
                final MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.mpd_queuemenu, menu);
                return true;
            }

            @Override
            public void onDestroyActionMode(final ActionMode mode) {
                mActionMode = null;
                mController.setSortEnabled(true);
            }

            @Override
            public void onItemCheckedStateChanged(final ActionMode mode, final int position, final long id,
                    final boolean checked) {
                final int selectCount = mList.getCheckedItemCount();
                if (selectCount == 0) {
                    mode.finish();
                }
                if (selectCount == 1) {
                    mode.setTitle(R.string.actionSongSelected);
                } else {
                    mode.setTitle(getString(R.string.actionSongsSelected, selectCount));
                }
            }

            @Override
            public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {
                mActionMode = mode;
                mController.setSortEnabled(false);
                return false;
            }
        });

        return view;
    }

    @Override
    public void onListItemClick(final ListView l, final View v, final int position, final long id) {
        super.onListItemClick(l, v, position, id);

        final int song = ((Music) l.getAdapter().getItem(position)).getSongId();

        QueueControl.run(QueueControl.SKIP_TO_ID, song);
    }

    @Override
    public boolean onMenuItemClick(final MenuItem item) {
        final Intent intent;
        final AbstractPlaylistMusic music;

        switch (item.getItemId()) {
        case R.id.PLCX_playNext:
            QueueControl.run(QueueControl.MOVE_TO_NEXT, mPopupSongID);
            Tools.notifyUser("Song moved to next in list");
            break;
        case R.id.PLCX_moveFirst:
            // Move song to first in playlist
            QueueControl.run(QueueControl.MOVE, mPopupSongID, 0);
            Tools.notifyUser("Song moved to first in list");
            break;
        case R.id.PLCX_moveLast:
            QueueControl.run(QueueControl.MOVE_TO_LAST, mPopupSongID);
            Tools.notifyUser("Song moved to last in list");
            break;
        case R.id.PLCX_removeFromPlaylist:
            QueueControl.run(QueueControl.REMOVE_BY_ID, mPopupSongID);

            if (isAdded()) {
                Tools.notifyUser(R.string.deletedSongFromPlaylist);
            }
            break;
        case R.id.PLCX_removeAlbumFromPlaylist:
            if (DEBUG) {
                Log.d(TAG, "Remove Album " + mPopupSongID);
            }
            QueueControl.run(QueueControl.REMOVE_ALBUM_BY_ID, mPopupSongID);
            if (isAdded()) {
                Tools.notifyUser(R.string.deletedSongFromPlaylist);
            }
            break;
        case R.id.PLCX_goToArtist:
            music = getPlaylistItemSong(mPopupSongID);
            if (music == null || isEmpty(music.getArtist())) {
                break;
            }

            intent = new Intent(mActivity, SimpleLibraryActivity.class);
            intent.putExtra("artist", music.getArtistAsArtist());
            startActivityForResult(intent, -1);
            break;
        case R.id.PLCX_goToAlbum:
            music = getPlaylistItemSong(mPopupSongID);

            if (music == null || isEmpty(music.getArtist()) || isEmpty(music.getAlbum())) {
                break;
            }

            intent = new Intent(mActivity, SimpleLibraryActivity.class);
            intent.putExtra("album", music.getAlbumAsAlbum());
            startActivityForResult(intent, -1);
            break;
        case R.id.PLCX_goToFolder:
            music = getPlaylistItemSong(mPopupSongID);
            if (music == null || isEmpty(music.getFullPath())) {
                break;
            }
            intent = new Intent(mActivity, SimpleLibraryActivity.class);
            intent.putExtra("folder", music.getParent());
            startActivityForResult(intent, -1);
            break;
        default:
            break;
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        super.onOptionsItemSelected(item);

        // Menu actions...
        boolean result = true;

        if (item.getItemId() == R.id.PLM_Clear) {
            QueueControl.run(QueueControl.CLEAR);
            mSongList.clear();
            if (isAdded()) {
                Tools.notifyUser(R.string.playlistCleared);
            }
            ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
        } else if (item.getItemId() == R.id.PLM_Save) {
            List<Item> playLists;
            try {
                playLists = mApp.oMPDAsyncHelper.oMPD.getPlaylists();
            } catch (final IOException | MPDException e) {
                Log.e(TAG, "Failed to receive list of playlists.", e);
                playLists = new ArrayList<>(0);
            }
            Collections.sort(playLists);
            final String[] playlistsArray = new String[playLists.size() + 1];
            for (int p = 0; p < playLists.size(); p++) {
                playlistsArray[p] = playLists.get(p).getName(); // old playlists
            }
            playlistsArray[playlistsArray.length - 1] = getResources().getString(R.string.newPlaylist); // "new playlist"
            mPlaylistToSave = playlistsArray[playlistsArray.length - 1];
            new AlertDialog.Builder(mActivity) // dialog with list of playlists
                    .setTitle(R.string.playlistName).setSingleChoiceItems(playlistsArray, playlistsArray.length - 1,
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(final DialogInterface dialog, final int which) {
                                    mPlaylistToSave = playlistsArray[which];
                                }
                            })
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            savePlaylist(mPlaylistToSave);
                        }
                    }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            // Do nothing.
                        }
                    }).create().show();
        } else {
            result = false;
        }

        return result;
    }

    @Override
    public void onPause() {
        mApp.oMPDAsyncHelper.removeStatusChangeListener(this);
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        mApp.oMPDAsyncHelper.addStatusChangeListener(this);
        new Thread(new Runnable() {
            @Override
            public void run() {
                update();
            }
        }).start();
    }

    @Override
    public void playlistChanged(final MPDStatus mpdStatus, final int oldPlaylistVersion) {
        update();

    }

    @Override
    public void randomChanged(final boolean random) {
    }

    protected void refreshListColorCacheHint() {
        if (mList != null) {
            if (mLightTheme) {
                mList.setCacheColorHint(getResources().getColor(android.R.color.background_light));
            } else {
                mList.setCacheColorHint(getResources().getColor(R.color.nowplaying_background));
            }
        }
    }

    protected void refreshPlaylistItemView(final AbstractPlaylistMusic... playlistSongs) {
        final int start = mList.getFirstVisiblePosition();

        for (int i = start; i <= mList.getLastVisiblePosition(); i++) {
            final AbstractPlaylistMusic playlistMusic = (AbstractPlaylistMusic) mList.getAdapter().getItem(i);
            for (final AbstractPlaylistMusic song : playlistSongs) {
                if (playlistMusic.getSongId() == song.getSongId()) {
                    final View view = mList.getChildAt(i - start);
                    mList.getAdapter().getView(i, view, mList);
                }
            }
        }
    }

    @Override
    public void repeatChanged(final boolean repeating) {
    }

    void savePlaylist(final String name) {
        if (name.equals(getResources().getString(R.string.newPlaylist))) {
            // if "new playlist", show dialog with EditText for new playlist:
            final EditText input = new EditText(mActivity);
            new AlertDialog.Builder(mActivity).setTitle(R.string.newPlaylistPrompt).setView(input)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            final String name = input.getText().toString().trim();
                            if (!name.isEmpty() && !name.equals(MPD.STREAMS_PLAYLIST)) {
                                // TODO: Need to warn user if they attempt to save to MPD.STREAMS_PLAYLIST
                                savePlaylist(name);
                            }
                        }
                    }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            // Do nothing.
                        }
                    }).create().show();
        } else if (!name.isEmpty()) {
            // actually save:
            QueueControl.run(QueueControl.SAVE_PLAYLIST, name);
        }
    }

    public void scrollToNowPlaying() {
        final int songPos = mApp.oMPDAsyncHelper.oMPD.getStatus().getSongPos();

        if (songPos == -1) {
            Log.d(TAG, "Missing list item.");
        } else {

            if (mActivity instanceof MainMenuActivity) {
                ((MainMenuActivity) mActivity).showQueue();
            }

            final ListView listView = getListView();
            listView.requestFocusFromTouch();
            listView.setSelection(songPos);
            listView.clearFocus();
        }
    }

    @Override
    public void stateChanged(final MPDStatus mpdStatus, final int oldState) {
    }

    @Override
    public void stickerChanged(final MPDStatus mpdStatus) {
    }

    @Override
    public void trackChanged(final MPDStatus mpdStatus, final int oldTrack) {
        if (mSongList != null) {
            // Mark running track...
            for (final AbstractPlaylistMusic song : mSongList) {
                final int newPlay;
                if (song.getSongId() == mpdStatus.getSongId()) {
                    if (mLightTheme) {
                        newPlay = R.drawable.ic_media_play_light;
                    } else {
                        newPlay = R.drawable.ic_media_play;
                    }
                } else {
                    newPlay = 0;
                }
                if (song.getCurrentSongIconRefID() != newPlay) {
                    song.setCurrentSongIconRefID(newPlay);
                    refreshPlaylistItemView(song);
                }
            }
        }
    }

    void update() {
        update(true);
    }

    /**
     * Update the current playlist fragment.
     *
     * @param forcePlayingIDRefresh Force the current track to refresh.
     */
    void update(final boolean forcePlayingIDRefresh) {
        // Save the scroll bar position to restore it after update
        final MPDPlaylist playlist = mApp.oMPDAsyncHelper.oMPD.getPlaylist();
        final List<Music> musics = playlist.getMusicList();
        final ArrayList<AbstractPlaylistMusic> newSongList = new ArrayList<>(musics.size());

        if (mLastPlayingID == -1 || forcePlayingIDRefresh) {
            mLastPlayingID = mApp.oMPDAsyncHelper.oMPD.getStatus().getSongId();
        }

        // The position in the song list of the currently played song
        int listPlayingID = -1;

        // Copy list to avoid concurrent exception
        for (final Music music : new ArrayList<>(musics)) {
            if (music == null) {
                continue;
            }

            final AbstractPlaylistMusic item;
            if (music.isStream()) {
                item = new PlaylistStream(music);
            } else {
                item = new PlaylistSong(music);
            }

            if (mFilter != null) {
                if (!(isFiltered(item.getAlbumArtist()) || isFiltered(item.getAlbum())
                        || isFiltered(item.getTitle()))) {
                    continue;
                }
            }

            if (item.getSongId() == mLastPlayingID) {
                if (mLightTheme) {
                    item.setCurrentSongIconRefID(R.drawable.ic_media_play_light);
                } else {
                    item.setCurrentSongIconRefID(R.drawable.ic_media_play);
                }

                /**
                 * Lie a little. Scroll to the previous song than the one playing.
                 * That way it shows that there are other songs before it.
                 */
                listPlayingID = newSongList.size() - 1;
            } else {
                item.setCurrentSongIconRefID(0);
            }
            newSongList.add(item);
        }

        updateScrollbar(newSongList, listPlayingID);
    }

    public void updateCover(final AlbumInfo albumInfo) {

        final List<AbstractPlaylistMusic> musicsToBeUpdated = new ArrayList<>(mSongList.size());

        for (final AbstractPlaylistMusic playlistMusic : mSongList) {
            final AlbumInfo abstractAlbumInfo = new AlbumInfo(playlistMusic);

            if (abstractAlbumInfo.equals(albumInfo)) {
                playlistMusic.setForceCoverRefresh(true);
                musicsToBeUpdated.add(playlistMusic);
            }
        }
        refreshPlaylistItemView(musicsToBeUpdated.toArray(new AbstractPlaylistMusic[musicsToBeUpdated.size()]));
    }

    /**
     * Updates the scrollbar.
     *
     * @param newSongList   The updated list of songs for the playlist.
     * @param listPlayingID The current playing playlist id.
     */
    protected void updateScrollbar(final ArrayList newSongList, final int listPlayingID) {
        mActivity.runOnUiThread(new Runnable() {
            /**
             * This is a helper method to workaround shortcomings of the fast scroll API.
             *
             * @param scrollbarStyle The {@code View} scrollbar style.
             * @param isAlwaysVisible The visibility of the scrollbar.
             */
            private void refreshFastScrollStyle(final int scrollbarStyle, final boolean isAlwaysVisible) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    mList.setFastScrollAlwaysVisible(isAlwaysVisible);
                    mList.setScrollBarStyle(scrollbarStyle);
                } else {
                    mList.setScrollBarStyle(scrollbarStyle);
                    mList.setFastScrollAlwaysVisible(isAlwaysVisible);
                }
            }

            @Override
            public void run() {
                final int firstVisibleElementIndex = mList.getFirstVisiblePosition();
                final View firstVisibleItem = mList.getChildAt(0);
                final int firstVisiblePosition;
                final ArrayAdapter songs = new QueueAdapter(mActivity, R.layout.playlist_queue_item, newSongList);

                if (firstVisibleItem != null) {
                    firstVisiblePosition = firstVisibleItem.getTop();
                } else {
                    firstVisiblePosition = 0;
                }

                setListAdapter(songs);
                mSongList = newSongList;
                songs.notifyDataSetChanged();

                /**
                 * Note : Setting the scrollbar style before setting the fast scroll state is very
                 * important pre-KitKat, because of a bug. It is also very important post-KitKat
                 * because it needs the opposite order or it won't show the FastScroll.
                 *
                 * This is so stupid I don't even .... argh.
                 */
                if (newSongList.size() >= MIN_SONGS_BEFORE_FASTSCROLL) {
                    refreshFastScrollStyle(View.SCROLLBARS_INSIDE_INSET, true);
                } else {
                    refreshFastScrollStyle(View.SCROLLBARS_INSIDE_OVERLAY, false);
                }

                if (mActionMode != null) {
                    mActionMode.finish();
                }

                // Restore the scroll bar position
                if (firstVisibleElementIndex == 0 || firstVisiblePosition == 0) {
                    /**
                     * Only scroll if there is a valid song to scroll to. 0 is a valid song but
                     * does not require scroll anyway. Also, only scroll if it's the first update.
                     * You don't want your playlist to scroll itself while you are looking at other
                     * stuff.
                     */
                    if (listPlayingID > 0 && getView() != null) {
                        setSelection(listPlayingID);
                    }
                } else {
                    mList.setSelectionFromTop(firstVisibleElementIndex, firstVisiblePosition);
                }
            }
        });
    }

    @Override
    public void volumeChanged(final MPDStatus mpdStatus, final int oldVolume) {
    }

    private class QueueAdapter extends ArrayAdapter<AbstractPlaylistMusic> {

        QueueAdapter(final Context context, @LayoutRes final int resource, final List<AbstractPlaylistMusic> data) {
            super(context, resource, data);
        }

        @Override
        public View getView(final int position, final View convertView, final ViewGroup parent) {
            final PlayQueueViewHolder viewHolder;
            final View view;

            if (convertView == null) {
                view = LayoutInflater.from(getContext()).inflate(R.layout.playlist_queue_item, mRootView);
                viewHolder = new PlayQueueViewHolder();
                viewHolder.mArtist = (TextView) view.findViewById(android.R.id.text2);
                viewHolder.mTitle = (TextView) view.findViewById(android.R.id.text1);
                viewHolder.mPlay = (ImageView) view.findViewById(R.id.picture);
                viewHolder.mAlbumCover = (ImageView) view.findViewById(R.id.cover);
                viewHolder.mCoverHelper = new CoverAsyncHelper();
                int height = viewHolder.mAlbumCover.getHeight();
                // If the list is not displayed yet, the height is 0.
                // This is a problem, so set a fallback one.
                final int fallbackHeight = 128;
                if (height == 0) {
                    height = fallbackHeight;
                }
                viewHolder.mCoverHelper.setCoverMaxSize(height);
                final CoverDownloadListener acd = new AlbumCoverDownloadListener(viewHolder.mAlbumCover);
                final AlbumCoverDownloadListener oldAcd = (AlbumCoverDownloadListener) viewHolder.mAlbumCover
                        .getTag(R.id.AlbumCoverDownloadListener);
                if (oldAcd != null) {
                    oldAcd.detach();
                }
                viewHolder.mAlbumCover.setTag(R.id.AlbumCoverDownloadListener, acd);
                viewHolder.mAlbumCover.setTag(R.id.CoverAsyncHelper, viewHolder.mCoverHelper);
                viewHolder.mCoverHelper.addCoverDownloadListener(acd);
                viewHolder.mMenuButton = view.findViewById(R.id.menu);
                viewHolder.mMenuButton.setOnClickListener(mItemMenuButtonListener);
                view.setTag(viewHolder);
            } else {
                viewHolder = (PlayQueueViewHolder) convertView.getTag();
                view = convertView;
            }

            final AbstractPlaylistMusic music = getItem(position);

            viewHolder.mArtist.setText(music.getPlaylistSubLine());
            viewHolder.mTitle.setText(music.getPlayListMainLine());
            viewHolder.mMenuButton.setTag(music.getSongId());
            viewHolder.mPlay.setImageResource(music.getCurrentSongIconRefID());

            final AlbumInfo albumInfo = new AlbumInfo(music);

            if (music.isForceCoverRefresh() || viewHolder.mAlbumCover.getTag() == null
                    || !viewHolder.mAlbumCover.getTag().equals(albumInfo.getKey())) {
                if (!music.isForceCoverRefresh()) {
                    final int noCoverResource = AlbumCoverDownloadListener.getNoCoverResource();
                    viewHolder.mAlbumCover.setImageResource(noCoverResource);
                }
                music.setForceCoverRefresh(false);
                viewHolder.mCoverHelper.downloadCover(albumInfo, false);
            }
            return view;
        }
    }
}