Java tutorial
/*- * 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.TextView; import android.widget.Toast; import java.util.LinkedList; import java.util.Queue; import org.peterbaldwin.client.android.vlcremote.R; import org.peterbaldwin.vlcremote.intent.Intents; import org.peterbaldwin.vlcremote.loader.PlaylistLoader; import org.peterbaldwin.vlcremote.listener.ProgressListener; 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 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 Queue<PlaylistLoader> mActiveLoaders; @Override public void onAttach(Activity activity) { super.onAttach(activity); ((Reloader) activity).addReloadable(Tags.FRAGMENT_PLAYLIST, 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)); } } 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() { 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 = ""; 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); } } @Override public boolean onBackPressed() { // TODO Auto-generated method stub return false; } }