de.qspool.clementineremote.ui.fragments.PlaylistFragment.java Source code

Java tutorial

Introduction

Here is the source code for de.qspool.clementineremote.ui.fragments.PlaylistFragment.java

Source

/* This file is part of the Android Clementine Remote.
 * Copyright (C) 2013, Andreas Muttscheller <asfa194@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package de.qspool.clementineremote.ui.fragments;

import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;

import android.app.Fragment;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
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.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import de.qspool.clementineremote.App;
import de.qspool.clementineremote.R;
import de.qspool.clementineremote.backend.downloader.DownloadManager;
import de.qspool.clementineremote.backend.listener.OnPlaylistReceivedListener;
import de.qspool.clementineremote.backend.pb.ClementineMessage;
import de.qspool.clementineremote.backend.pb.ClementineMessageFactory;
import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.DownloadItem;
import de.qspool.clementineremote.backend.player.MyPlaylist;
import de.qspool.clementineremote.backend.player.MySong;
import de.qspool.clementineremote.backend.player.PlaylistManager;
import de.qspool.clementineremote.ui.adapter.PlaylistSongAdapter;
import de.qspool.clementineremote.ui.interfaces.BackPressHandleable;
import de.qspool.clementineremote.ui.interfaces.RemoteDataReceiver;

public class PlaylistFragment extends Fragment implements BackPressHandleable, RemoteDataReceiver {

    private PlaylistSongAdapter mAdapter;

    private MaterialDialog mProgressDialog;

    private ActionBar mActionBar;

    private Spinner mPlaylistsSpinner;

    private ListView mList;

    private View mEmptyPlaylist;

    private PlaylistManager mPlaylistManager;

    private OnPlaylistReceivedListener mPlaylistListener;

    private LinkedList<MyPlaylist> mPlaylists = new LinkedList<>();

    private String mFilterText;

    private boolean mUpdateTrackPositionOnNewTrack = false;

    private int mSelectionOffset;

    public PlaylistFragment() {
        mFilterText = "";
        mSelectionOffset = 3;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get the actionbar
        mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        setHasOptionsMenu(true);

        mPlaylistManager = App.Clementine.getPlaylistManager();
        mPlaylistListener = new OnPlaylistReceivedListener() {
            @Override
            public void onPlaylistSongsReceived(final MyPlaylist p) {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (mProgressDialog != null) {
                            mProgressDialog.incrementProgress(1);
                            mProgressDialog.setContent(p.getName());
                        }

                        updateSongList();
                    }
                });
            }

            @Override
            public void onPlaylistReceived(final MyPlaylist p) {
            }

            @Override
            public void onAllRequestedPlaylistSongsReceived() {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (mProgressDialog != null && mProgressDialog.isShowing()) {
                            mPlaylists = mPlaylistManager.getAllPlaylists();

                            mProgressDialog.dismiss();
                            getActivity().invalidateOptionsMenu();

                            mPlaylistsSpinner
                                    .setSelection(mPlaylists.indexOf(mPlaylistManager.getActivePlaylist()));
                        }
                    }
                });
            }

            @Override
            public void onAllPlaylistsReceived() {
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mPlaylists = mPlaylistManager.getAllPlaylists();
                        updatePlaylistSpinner();
                        RequestPlaylistSongs();
                    }
                });
            }
        };
    }

    @Override
    public void onResume() {
        super.onResume();
        // Check if we are still connected
        if (App.ClementineConnection == null || App.Clementine == null || !App.ClementineConnection.isConnected()) {
            return;
        }

        mPlaylistManager.addOnPlaylistReceivedListener(mPlaylistListener);
        mPlaylists = mPlaylistManager.getAllPlaylists();

        mPlaylistsSpinner.setVisibility(View.VISIBLE);

        updatePlaylistSpinner();

        RequestPlaylistSongs();

        // Get the position of the current track if we have one
        if (App.Clementine.getCurrentSong() != null) {
            updateViewPosition();
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        mPlaylistsSpinner.setVisibility(View.GONE);

        mPlaylistManager.removeOnPlaylistReceivedListener(mPlaylistListener);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_playlist, container, false);

        mPlaylists = mPlaylistManager.getAllPlaylists();

        mList = (ListView) view.findViewById(R.id.songs);
        mEmptyPlaylist = view.findViewById(R.id.playlist_empty);

        // Add Spinner to toolbar
        mPlaylistsSpinner = (Spinner) getActivity().findViewById(R.id.toolbar_spinner);

        mPlaylistsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                updateSongList();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });

        updatePlaylistSpinner();

        // Create the adapter
        mAdapter = new PlaylistSongAdapter(getActivity(), R.layout.item_playlist, getSelectedPlaylistSongs());

        mList.setOnItemClickListener(oiclSong);
        mList.setAdapter(mAdapter);

        mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        mList.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
            @Override
            public boolean onActionItemClicked(ActionMode mode, android.view.MenuItem item) {
                SparseBooleanArray checkedPositions = mList.getCheckedItemPositions();
                LinkedList<MySong> selectedSongs = new LinkedList<>();

                for (int i = 0; i < checkedPositions.size(); ++i) {
                    int position = checkedPositions.keyAt(i);
                    if (checkedPositions.valueAt(i)) {
                        selectedSongs.add(getSelectedPlaylistSongs().get(position));
                    }
                }

                if (!selectedSongs.isEmpty()) {
                    switch (item.getItemId()) {
                    case R.id.playlist_context_play:
                        playSong(selectedSongs.get(0));

                        mode.finish();
                        return true;
                    case R.id.playlist_context_download:
                        LinkedList<String> urls = new LinkedList<>();
                        for (MySong s : selectedSongs) {
                            urls.add(s.getUrl());
                        }
                        if (!urls.isEmpty()) {
                            DownloadManager.getInstance().addJob(
                                    ClementineMessageFactory.buildDownloadSongsMessage(DownloadItem.Urls, urls));
                        }
                        mode.finish();
                        return true;
                    case R.id.playlist_context_remove:
                        Message msg = Message.obtain();
                        msg.obj = ClementineMessageFactory.buildRemoveMultipleSongsFromPlaylist(getPlaylistId(),
                                selectedSongs);
                        App.ClementineConnection.mHandler.sendMessage(msg);
                        mode.finish();
                        return true;
                    default:
                        return false;
                    }
                }
                return false;
            }

            @Override
            public boolean onCreateActionMode(ActionMode mode, android.view.Menu menu) {
                android.view.MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.playlist_context_menu, menu);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                    getActivity().getWindow()
                            .setStatusBarColor(ContextCompat.getColor(getActivity(), R.color.grey_cab_status));

                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, android.view.Menu menu) {
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                    getActivity().getWindow()
                            .setStatusBarColor(ContextCompat.getColor(getActivity(), R.color.actionbar_dark));
            }

            @Override
            public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
            }
        });

        // Filter the results
        mAdapter.getFilter().filter(mFilterText);

        mActionBar.setTitle("");
        mActionBar.setSubtitle("");

        return view;
    }

    @Override
    public void onViewCreated(final View view, final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mList.setFastScrollEnabled(true);
        mList.setTextFilterEnabled(true);
        mList.setSelector(new ColorDrawable(ContextCompat.getColor(getActivity(), android.R.color.transparent)));
        mList.setDivider(null);
        mList.setDividerHeight(0);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.download_playlist:
            DownloadManager.getInstance().addJob(
                    ClementineMessageFactory.buildDownloadSongsMessage(DownloadItem.APlaylist, getPlaylistId()));
            return true;
        case R.id.clear_playlist:
            new MaterialDialog.Builder(getActivity()).title(R.string.playlist_clear)
                    .content(R.string.playlist_clear_content).positiveText(R.string.playlist_clear_confirm)
                    .negativeText(R.string.dialog_cancel).onPositive(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(MaterialDialog dialog, DialogAction which) {
                            mPlaylistManager.clearPlaylist(getPlaylistId());
                            updateSongList();
                        }
                    }).show();
            return true;
        case R.id.close_playlist:
            Message msg = Message.obtain();
            msg.obj = ClementineMessageFactory.buildClosePlaylist(getPlaylistId());
            App.ClementineConnection.mHandler.sendMessage(msg);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.clear();
        inflater.inflate(R.menu.playlist_menu, menu);

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        android.view.MenuInflater inflater = getActivity().getMenuInflater();
        inflater.inflate(R.menu.playlist_context_menu, menu);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        // Create a listener for search change
        SearchView searchView = (SearchView) menu.findItem(R.id.playlist_menu_search).getActionView();

        final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextChange(String newText) {
                // Set the filter text as the fragments might not yet
                // created. Only the left and right fragment from the
                // currently active is created (onCreate() called).
                // Therefore the other adapters are not yet created,
                // onCreate filters for this string given in setFilterText()
                setFilterText(newText);
                if (getAdapter() != null) {
                    getAdapter().getFilter().filter(newText);
                }
                return true;
            }

            @Override
            public boolean onQueryTextSubmit(String query) {
                // Do something
                setFilterText(query);
                if (getAdapter() != null) {
                    getAdapter().getFilter().filter(query);
                }

                return true;
            }
        };
        searchView.setOnQueryTextListener(queryTextListener);
        searchView.setQueryHint(getString(R.string.playlist_search_hint));

        EditText searchText = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
        searchText.setHintTextColor(ContextCompat.getColor(getActivity(), R.color.searchview_edittext_hint));

        super.onPrepareOptionsMenu(menu);
    }

    /**
     * Update the underlying data. It reloads the current playlist songs from the Clementine
     * object.
     */
    public void updateSongList() {
        // Check if we should update the current view position
        mAdapter = new PlaylistSongAdapter(getActivity(), R.layout.item_playlist, getSelectedPlaylistSongs());
        mList.setAdapter(mAdapter);

        // We have to post notifyDataSetChanged() here, so fast scroll is set correctly.
        // Without it, the fast scroll cannot get any child views as the adapter is not yet fully
        // attached to the view and getChildCount() returns 0. Therefore, fast scroll won't
        // be enabled.
        // notifyDataSetChanged() forces the listview to recheck the fast scroll preconditions.
        mList.post(new Runnable() {
            @Override
            public void run() {
                mAdapter.notifyDataSetChanged();
            }
        });

        updateViewPosition();

        if (mPlaylists.isEmpty()) {
            mList.setEmptyView(mEmptyPlaylist);
        }

    }

    /**
     * Set the text to filter
     *
     * @param filterText String, which results are filtered by
     */
    public void setFilterText(String filterText) {
        mFilterText = filterText;
    }

    /**
     * Get the song adapter
     *
     * @return The CustomSongAdapter
     */
    public PlaylistSongAdapter getAdapter() {
        return mAdapter;
    }

    private OnItemClickListener oiclSong = new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            playSong(mAdapter.getItem(position));
        }
    };

    private void playSong(MySong song) {
        Message msg = Message.obtain();
        msg.obj = ClementineMessageFactory.buildRequestChangeSong(song.getIndex(), getPlaylistId());
        App.ClementineConnection.mHandler.sendMessage(msg);

        mPlaylistManager.setActivePlaylist(getPlaylistId());
    }

    /**
     * Set the selection to the currently played item
     */
    private void updateViewPosition() {
        if (App.Clementine.getCurrentSong() != null && mPlaylistManager.getActivePlaylistId() == getPlaylistId()) {
            int pos = App.Clementine.getCurrentSong().getIndex();
            mList.setSelection(pos - mSelectionOffset);
        }
    }

    public boolean isUpdateTrackPositionOnNewTrack() {
        return mUpdateTrackPositionOnNewTrack;
    }

    public void setUpdateTrackPositionOnNewTrack(boolean updateTrackPositionOnNewTrack, int offset) {
        this.mUpdateTrackPositionOnNewTrack = updateTrackPositionOnNewTrack;
        mSelectionOffset = offset;
    }

    @Override
    public void MessageFromClementine(ClementineMessage clementineMessage) {
        switch (clementineMessage.getMessageType()) {
        case CURRENT_METAINFO:
            updateSongList();
            break;
        default:
            break;
        }
    }

    /**
     * Sends a request to Clementine to send all songs in all active playlists.
     */
    public void RequestPlaylistSongs() {
        // If a progress is showing, do not show again!
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            return;
        }

        // Open it directly only when we got all playlists
        int requests = mPlaylistManager.requestAllPlaylistSongs();
        if (requests > 0) {
            // Start a Progressbar
            mProgressDialog = new MaterialDialog.Builder(getActivity()).progress(false, requests, true)
                    .title(R.string.player_download_playlists).content(R.string.playlist_loading).show();
        } else {
            mPlaylistsSpinner.setSelection(mPlaylists.indexOf(mPlaylistManager.getActivePlaylist()));
        }
    }

    @Override
    public boolean onBackPressed() {
        return false;
    }

    private void updatePlaylistSpinner() {
        List<CharSequence> arrayList = new ArrayList<>();
        for (int i = 0; i < mPlaylists.size(); i++) {
            arrayList.add(mPlaylists.get(i).getName());
        }

        ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(getActivity(),
                android.R.layout.simple_spinner_item, arrayList);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mPlaylistsSpinner.setAdapter(adapter);
    }

    private int getPlaylistId() {
        return mPlaylists.get(getSelectedPlaylistPosition()).getId();
    }

    private LinkedList<MySong> getSelectedPlaylistSongs() {
        return mPlaylists.get(getSelectedPlaylistPosition()).getPlaylistSongs();
    }

    private int getSelectedPlaylistPosition() {
        int pos = mPlaylistsSpinner.getSelectedItemPosition();
        if (pos == Spinner.INVALID_POSITION || pos >= mPlaylists.size()) {
            pos = mPlaylists.indexOf(mPlaylistManager.getActivePlaylist());
            mPlaylistsSpinner.setSelection(pos);
        }
        return pos;
    }
}