/*-
* Copyright (C) 2011 Peter Baldwin
*
* 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 org.peterbaldwin.vlcremote.fragment;
import android.app.Activity;
import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.LinkedList;
import java.util.Queue;
import org.peterbaldwin.client.android.vlcremote.R;
import org.peterbaldwin.vlcremote.app.PlaybackActivity;
import org.peterbaldwin.vlcremote.intent.Intents;
import org.peterbaldwin.vlcremote.listener.ProgressListener;
import org.peterbaldwin.vlcremote.loader.PlaylistLoader;
import org.peterbaldwin.vlcremote.model.Playlist;
import org.peterbaldwin.vlcremote.model.PlaylistItem;
import org.peterbaldwin.vlcremote.model.Reloadable;
import org.peterbaldwin.vlcremote.model.Reloader;
import org.peterbaldwin.vlcremote.model.Remote;
import org.peterbaldwin.vlcremote.model.Status;
import org.peterbaldwin.vlcremote.model.Tags;
import org.peterbaldwin.vlcremote.model.Track;
import org.peterbaldwin.vlcremote.net.MediaServer;
import org.peterbaldwin.vlcremote.widget.PlaylistAdapter;
public class PlaylistFragment extends MediaListFragment implements SearchView.OnQueryTextListener,
LoaderManager.LoaderCallbacks<Remote<Playlist>>, Reloadable, ProgressListener {
private static final int LOADER_PLAYLIST = 1;
private TextView mEmptyView;
private PlaylistAdapter mAdapter;
private BroadcastReceiver mStatusReceiver;
private String mCurrent;
private String mSearchQuery = "";
private Queue<PlaylistLoader> mActiveLoaders;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((Reloader) activity).addReloadable(Tags.FRAGMENT_PLAYLIST, this);
((PlaybackActivity) activity).setSearchViewOnQueryTextListener(this);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActiveLoaders = new LinkedList<PlaylistLoader>();
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.playlist, container, false);
mAdapter = new PlaylistAdapter();
if(savedInstanceState != null && savedInstanceState.containsKey("adapter")) {
if(savedInstanceState.containsKey("adapter")) {
mAdapter = (PlaylistAdapter) savedInstanceState.getSerializable("adapter");
}
mCurrent = savedInstanceState.getString("current");
}
setListAdapter(mAdapter);
mEmptyView = (TextView) view.findViewById(android.R.id.empty);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getListView());
if (getMediaServer() != null && savedInstanceState == null) {
getLoaderManager().initLoader(LOADER_PLAYLIST, null, this);
} else {
onProgress(10000);
}
}
@Override
public void onResume() {
super.onResume();
mStatusReceiver = new StatusReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intents.ACTION_STATUS);
getActivity().registerReceiver(mStatusReceiver, filter);
}
@Override
public void onPause() {
getActivity().unregisterReceiver(mStatusReceiver);
mStatusReceiver = null;
super.onPause();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
reload();
return true;
case R.id.menu_clear_playlist:
getMediaServer().status().command.playback.empty();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if (menuInfo instanceof AdapterContextMenuInfo) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
PlaylistItem item = mAdapter.getItem(info.position);
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.playlist_context, menu);
MenuItem searchItem = menu.findItem(R.id.playlist_context_search);
searchItem.setVisible(isSearchable(item));
}
}
@Override
public boolean onQueryTextSubmit(String query) {
mSearchQuery = query;
reload();
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
if(!newText.equals(mSearchQuery)) {
mSearchQuery = newText;
reload();
}
return true;
}
private boolean isSearchable(PlaylistItem item) {
if (item instanceof Track) {
Track track = (Track) item;
boolean hasTitle = !TextUtils.isEmpty(track.getTitle());
boolean hasArtist = !TextUtils.isEmpty(track.getArtist());
return hasTitle && hasArtist;
} else {
return false;
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ContextMenuInfo menuInfo = menuItem.getMenuInfo();
if (menuInfo instanceof AdapterContextMenuInfo) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
if (info.position < mAdapter.getCount()) {
PlaylistItem item = mAdapter.getItem(info.position);
switch (menuItem.getItemId()) {
case R.id.playlist_context_play:
selectItem(item);
return true;
case R.id.playlist_context_dequeue:
removeItem(item, info.position);
return true;
case R.id.playlist_context_search:
searchForItem(item);
return true;
}
}
}
return super.onContextItemSelected(menuItem);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("adapter", mAdapter);
outState.putString("current", mCurrent);
}
@Override
public void onNewMediaServer(MediaServer server) {
// onActivityCreated will not have been called yet on first use
boolean isFirstUse = getMediaServer() == null;
super.onNewMediaServer(server);
if(!isFirstUse) {
reload();
}
}
private void removeItem(PlaylistItem item, int position) {
int id = item.getId();
// TODO: Register observer and notify observers when playlist item is
// deleted
mAdapter.remove(position);
getMediaServer().status().command.playback.delete(id);
}
private void searchForItem(PlaylistItem item) {
if (item instanceof Track) {
Track track = (Track) item;
String title = track.getTitle();
String artist = track.getArtist();
String query = artist + " " + title;
Intent intent = new Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
intent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
intent.putExtra(MediaStore.EXTRA_MEDIA_TITLE, title);
intent.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, "audio/*");
intent.putExtra(SearchManager.QUERY, query);
String chooserTitle = getString(R.string.mediasearch, title);
startActivity(Intent.createChooser(intent, chooserTitle));
}
}
private void showError(CharSequence message) {
Context context = getActivity();
Toast toast = Toast.makeText(context, message, Toast.LENGTH_LONG);
toast.show();
}
private void selectItem(PlaylistItem item) {
getMediaServer().status().command.playback.play(item.getId());
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
PlaylistItem item = (PlaylistItem) l.getItemAtPosition(position);
selectItem(item);
mAdapter.setCurrentItem(position);
}
public void selectCurrentTrack() {
if(mAdapter.getCurrentItems() != null && !mAdapter.getCurrentItems().isEmpty()) {
int pos = mAdapter.getCurrentItems().iterator().next();
getListView().setSelection(pos);
}
}
@Override
public void setEmptyText(CharSequence text) {
if(mEmptyView != null) {
mEmptyView.setText(text);
}
}
@Override
public void onProgress(final int progress) {
if(getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if(getActivity() != null) {
getActivity().getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress);
}
}
});
}
}
@Override
public Loader<Remote<Playlist>> onCreateLoader(int id, Bundle args) {
setEmptyText(getText(R.string.loading));
String search = mSearchQuery == null ? "" : mSearchQuery;
mActiveLoaders.offer(new PlaylistLoader(getActivity(), getMediaServer(), search, this));
return mActiveLoaders.peek();
}
@Override
public void onLoadFinished(Loader<Remote<Playlist>> loader, Remote<Playlist> remote) {
if(remote == null) {
return;
}
mAdapter.setItems(remote.data);
selectCurrentTrack();
if (remote.error != null) {
setEmptyText(getText(R.string.connection_error));
showError(String.valueOf(remote.error));
} else {
setEmptyText(getText(R.string.emptyplaylist));
}
}
@Override
public void onLoaderReset(Loader<Remote<Playlist>> loader) {
mAdapter.setItems(null);
}
void onStatusChanged(Status status) {
String filePath = status.getTrack().getName() == null ? status.getTrack().getTitle() : status.getTrack().getName();
if (!TextUtils.equals(filePath, mCurrent)) {
// Reload the playlist and scroll to the new current track
mCurrent = filePath;
reload();
}
}
private void reload() {
reload(null);
}
public void reload(Bundle args) {
if (getActivity() != null && getMediaServer() != null) {
PlaylistLoader loader;
while((loader = mActiveLoaders.poll()) != null) {
loader.cancelBackgroundLoad();
}
getLoaderManager().restartLoader(LOADER_PLAYLIST, null, this);
}
}
private class StatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Status status = (Status) intent.getSerializableExtra(Intents.EXTRA_STATUS);
onStatusChanged(status);
}
}
}