/*
* ServeStream: A HTTP stream browser/player for Android
* Copyright 2013 William Seemann
*
* 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 net.sourceforge.servestream.activity;
import net.sourceforge.servestream.R;
import net.sourceforge.servestream.dslv.DragSortController;
import net.sourceforge.servestream.dslv.DragSortListView;
import net.sourceforge.servestream.dslv.SimpleDragSortCursorAdapter;
import net.sourceforge.servestream.provider.Media;
import net.sourceforge.servestream.service.IMediaPlaybackService;
import net.sourceforge.servestream.service.MediaPlaybackService;
import net.sourceforge.servestream.utils.MusicUtils;
import net.sourceforge.servestream.utils.PreferenceConstants;
import net.sourceforge.servestream.utils.MusicUtils.ServiceToken;
import android.content.AsyncQueryHandler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.database.AbstractCursor;
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import java.util.Arrays;
public class NowPlayingActivity extends ActionBarActivity implements
View.OnCreateContextMenuListener,
MusicUtils.Defs, ServiceConnection {
private static final String TAG = NowPlayingActivity.class.getName();
private DragSortListView mList;
private boolean mIsDragging = false;
private boolean mShouldRefresh = false;
private static boolean mDeletedOneRow = false;
private String mCurrentTrackName;
private static Cursor mTrackCursor;
private TrackListAdapter mAdapter;
private boolean mAdapterSent = false;
private int mSelectedPosition;
private ServiceToken mToken;
private SharedPreferences mPreferences;
String[] mCursorCols = new String[] {
Media.MediaColumns._ID, // index must match IDCOLIDX below
Media.MediaColumns.URI,
Media.MediaColumns.TITLE,
Media.MediaColumns.ALBUM,
Media.MediaColumns.ARTIST,
Media.MediaColumns.DURATION
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_now_playing);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mList = (DragSortListView) findViewById(android.R.id.list);
mList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
if (mTrackCursor.getCount() == 0) {
return;
}
// When selecting a track from the queue, just jump there instead of
// reloading the queue. This is both faster, and prevents accidentally
// dropping out of party shuffle.
if (MusicUtils.sService != null) {
try {
int queuePosition = MusicUtils.sService.getQueuePosition();
if (position == queuePosition) {
if (MusicUtils.sService.isPlaying()) {
MusicUtils.sService.pause();
} else {
MusicUtils.sService.play();
}
} else {
MusicUtils.sService.setQueuePosition(position);
}
} catch (RemoteException ex) {
}
}
}
});
DragSortController controller = new DragSortController(mList);
controller.setDragInitMode(DragSortController.ON_DRAG);
controller.setRemoveMode(DragSortController.FLING_REMOVE);
controller.setRemoveEnabled(true);
controller.setDragHandleId(R.id.drag);
mList.setOnTouchListener(controller);
mList.setOnCreateContextMenuListener(this);
mAdapter = (TrackListAdapter) getLastCustomNonConfigurationInstance();
if (mAdapter != null) {
mAdapter.setActivity(this);
mList.setAdapter(mAdapter);
}
mToken = MusicUtils.bindToService(this, this);
}
public void onServiceConnected(ComponentName name, IBinder service) {
if (mAdapter == null) {
//Log.i("@@@", "starting query");
mAdapter = new TrackListAdapter(
getApplication(), // need to use application context to avoid leaks
this,
R.layout.now_playing_item,
null, // cursor
new String[] {},
new int[] {});
mList.setAdapter(mAdapter);
getTrackCursor(mAdapter.getQueryHandler(), null, true);
} else {
mTrackCursor = mAdapter.getCursor();
// If mTrackCursor is null, this can be because it doesn't have
// a cursor yet (because the initial query that sets its cursor
// is still in progress), or because the query failed.
// In order to not flash the error dialog at the user for the
// first case, simply retry the query when the cursor is null.
// Worst case, we end up doing the same query twice.
if (mTrackCursor != null) {
init(mTrackCursor, false);
} else {
getTrackCursor(mAdapter.getQueryHandler(), null, true);
}
}
}
public void onServiceDisconnected(ComponentName name) {
finish();
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
TrackListAdapter a = mAdapter;
mAdapterSent = true;
return a;
}
@Override
public void onDestroy() {
ListView lv = mList;
if (lv != null) {
// clear the listeners so we won't get any more callbacks
((DragSortListView) lv).setDropListener(null);
((DragSortListView) lv).setRemoveListener(null);
}
MusicUtils.unbindFromService(mToken);
try {
unregisterReceiver(mNowPlayingListener);
} catch (IllegalArgumentException ex) {
// we end up here in case we never registered the listeners
}
// If we have an adapter and didn't send it off to another activity yet, we should
// close its cursor, which we do by assigning a null cursor to it. Doing this
// instead of closing the cursor directly keeps the framework from accessing
// the closed cursor later.
if (!mAdapterSent && mAdapter != null) {
mAdapter.changeCursor(null);
}
// Because we pass the adapter to the next activity, we need to make
// sure it doesn't keep a reference to this activity. We can do this
// by clearing its DatasetObservers, which setListAdapter(null) does.
mList.setAdapter(null);
mAdapter = null;
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
if (mTrackCursor != null) {
mList.invalidateViews();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void init(Cursor newCursor, boolean isLimited) {
if (mAdapter == null) {
return;
}
mAdapter.changeCursor(newCursor); // also sets mTrackCursor
if (mTrackCursor == null) {
closeContextMenu();
return;
}
// When showing the queue, position the selection on the currently playing track
// Otherwise, position the selection on the first matching artist, if any
IntentFilter f = new IntentFilter();
f.addAction(MediaPlaybackService.META_CHANGED);
f.addAction(MediaPlaybackService.META_RETRIEVED);
f.addAction(MediaPlaybackService.QUEUE_CHANGED);
f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
try {
int cur = MusicUtils.sService.getQueuePosition();
mList.setSelection(cur);
registerReceiver(mNowPlayingListener, new IntentFilter(f));
mNowPlayingListener.onReceive(this, new Intent(MediaPlaybackService.META_CHANGED));
} catch (RemoteException ex) {
}
}
private void removePlaylistItem(int which) {
View v = mList.getChildAt(which - mList.getFirstVisiblePosition());
if (v == null) {
Log.d(TAG, "No view when removing playlist item " + which);
return;
}
try {
if (MusicUtils.sService != null
&& which != MusicUtils.sService.getQueuePosition()) {
mDeletedOneRow = true;
}
} catch (RemoteException e) {
// Service died, so nothing playing.
mDeletedOneRow = true;
}
v.setVisibility(View.GONE);
mList.invalidateViews();
((NowPlayingCursor)mTrackCursor).removeItem(which);
v.setVisibility(View.VISIBLE);
mList.invalidateViews();
}
private BroadcastReceiver mNowPlayingListener = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MediaPlaybackService.META_CHANGED)) {
mList.invalidateViews();
} else if (intent.getAction().equals(MediaPlaybackService.META_RETRIEVED)) {
if (isDragging()) {
mShouldRefresh = true;
System.out.println("dsdsdsssssssssssss=================>");
} else {
if (mAdapter != null) {
Cursor c = new NowPlayingCursor(MusicUtils.sService, mCursorCols);
if (c.getCount() == 0) {
c.close();
finish();
return;
}
mAdapter.changeCursor(c);
}
}
} else if (intent.getAction().equals(MediaPlaybackService.QUEUE_CHANGED)) {
if (mDeletedOneRow) {
// This is the notification for a single row that was
// deleted previously, which is already reflected in
// the UI.
mDeletedOneRow = false;
return;
}
// The service could disappear while the broadcast was in flight,
// so check to see if it's still valid
if (MusicUtils.sService == null) {
finish();
return;
}
if (mAdapter != null) {
Cursor c = new NowPlayingCursor(MusicUtils.sService, mCursorCols);
if (c.getCount() == 0) {
finish();
c.close();
return;
}
mAdapter.changeCursor(c);
}
} else if (intent.getAction().equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
mList.invalidateViews();
}
}
};
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
mSelectedPosition = info.position;
mTrackCursor.moveToPosition(mSelectedPosition);
mCurrentTrackName = mTrackCursor.getString(mTrackCursor.getColumnIndexOrThrow(
Media.MediaColumns.TITLE));
menu.setHeaderTitle(mCurrentTrackName);
android.view.MenuItem remove = menu.add(R.string.remove_from_playlist);
remove.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(android.view.MenuItem arg0) {
removePlaylistItem(mSelectedPosition);
return true;
}
});
}
// In order to use alt-up/down as a shortcut for moving the selected item
// in the list, we need to override dispatchKeyEvent, not onKeyDown.
// (onKeyDown never sees these events, since they are handled by the list)
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getMetaState() != 0 &&
event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_UP:
moveItem(true);
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
moveItem(false);
return true;
case KeyEvent.KEYCODE_DEL:
removeItem();
return true;
}
}
return super.dispatchKeyEvent(event);
}
private void removeItem() {
int curcount = mTrackCursor.getCount();
int curpos = mList.getSelectedItemPosition();
if (curcount == 0 || curpos < 0) {
return;
}
// remove track from queue
// Work around bug 902971. To get quick visual feedback
// of the deletion of the item, hide the selected view.
try {
if (curpos != MusicUtils.sService.getQueuePosition()) {
mDeletedOneRow = true;
}
} catch (RemoteException ex) {
}
View v = mList.getSelectedView();
v.setVisibility(View.GONE);
mList.invalidateViews();
((NowPlayingCursor)mTrackCursor).removeItem(curpos);
v.setVisibility(View.VISIBLE);
mList.invalidateViews();
}
private void moveItem(boolean up) {
int curcount = mTrackCursor.getCount();
int curpos = mList.getSelectedItemPosition();
if ( (up && curpos < 1) || (!up && curpos >= curcount - 1)) {
return;
}
NowPlayingCursor c = (NowPlayingCursor) mTrackCursor;
c.moveItem(curpos, up ? curpos - 1 : curpos + 1);
((TrackListAdapter)mList.getAdapter()).notifyDataSetChanged();
mList.invalidateViews();
mDeletedOneRow = true;
if (up) {
mList.setSelection(curpos - 1);
} else {
mList.setSelection(curpos + 1);
}
}
private Cursor getTrackCursor(TrackListAdapter.TrackQueryHandler queryhandler, String filter,
boolean async) {
if (queryhandler == null) {
throw new IllegalArgumentException();
}
Cursor ret = null;
if (MusicUtils.sService != null) {
ret = new NowPlayingCursor(MusicUtils.sService, mCursorCols);
if (ret.getCount() == 0) {
finish();
}
} else {
// Nothing is playing.
}
// This special case is for the "nowplaying" cursor, which cannot be handled
// asynchronously using AsyncQueryHandler, so we do some extra initialization here.
if (ret != null && async) {
init(ret, false);
}
return ret;
}
private class NowPlayingCursor extends AbstractCursor
{
public NowPlayingCursor(IMediaPlaybackService service, String [] cols)
{
mCols = cols;
mService = service;
makeNowPlayingCursor();
}
private void makeNowPlayingCursor() {
mCurrentPlaylistCursor = null;
try {
mNowPlaying = mService.getQueue();
} catch (RemoteException ex) {
mNowPlaying = new long[0];
}
mSize = mNowPlaying.length;
if (mSize == 0) {
return;
}
StringBuilder where = new StringBuilder();
where.append(Media.MediaColumns._ID + " IN (");
for (int i = 0; i < mSize; i++) {
where.append(mNowPlaying[i]);
if (i < mSize - 1) {
where.append(",");
}
}
where.append(")");
mCurrentPlaylistCursor = MusicUtils.query(NowPlayingActivity.this,
Media.MediaColumns.CONTENT_URI,
mCols, where.toString(), null, Media.MediaColumns._ID);
if (mCurrentPlaylistCursor == null) {
mSize = 0;
return;
}
int size = mCurrentPlaylistCursor.getCount();
mCursorIdxs = new long[size];
mCurrentPlaylistCursor.moveToFirst();
int colidx = mCurrentPlaylistCursor.getColumnIndexOrThrow(Media.MediaColumns._ID);
for (int i = 0; i < size; i++) {
mCursorIdxs[i] = mCurrentPlaylistCursor.getLong(colidx);
mCurrentPlaylistCursor.moveToNext();
}
mCurrentPlaylistCursor.moveToFirst();
mCurPos = -1;
// At this point we can verify the 'now playing' list we got
// earlier to make sure that all the items in there still exist
// in the database, and remove those that aren't. This way we
// don't get any blank items in the list.
try {
int removed = 0;
for (int i = mNowPlaying.length - 1; i >= 0; i--) {
long trackid = mNowPlaying[i];
int crsridx = Arrays.binarySearch(mCursorIdxs, trackid);
if (crsridx < 0) {
//Log.i("@@@@@", "item no longer exists in db: " + trackid);
removed += mService.removeTrack(trackid);
}
}
if (removed > 0) {
mNowPlaying = mService.getQueue();
mSize = mNowPlaying.length;
if (mSize == 0) {
mCursorIdxs = null;
return;
}
}
} catch (RemoteException ex) {
mNowPlaying = new long[0];
}
}
@Override
public int getCount()
{
return mSize;
}
@Override
public boolean onMove(int oldPosition, int newPosition)
{
if (oldPosition == newPosition)
return true;
if (mNowPlaying == null || mCursorIdxs == null || newPosition >= mNowPlaying.length) {
return false;
}
// The cursor doesn't have any duplicates in it, and is not ordered
// in queue-order, so we need to figure out where in the cursor we
// should be.
long newid = mNowPlaying[newPosition];
int crsridx = Arrays.binarySearch(mCursorIdxs, newid);
mCurrentPlaylistCursor.moveToPosition(crsridx);
mCurPos = newPosition;
return true;
}
public boolean removeItem(int which)
{
try {
if (mService.removeTracks(which, which) == 0) {
return false; // delete failed
}
int i = (int) which;
mSize--;
while (i < mSize) {
mNowPlaying[i] = mNowPlaying[i+1];
i++;
}
onMove(-1, (int) mCurPos);
} catch (RemoteException ex) {
}
return true;
}
public void moveItem(int from, int to) {
try {
mService.moveQueueItem(from, to);
mNowPlaying = mService.getQueue();
onMove(-1, mCurPos); // update the underlying cursor
} catch (RemoteException ex) {
}
}
@Override
public String getString(int column)
{
try {
return mCurrentPlaylistCursor.getString(column);
} catch (Exception ex) {
onChange(true);
return "";
}
}
@Override
public short getShort(int column)
{
return mCurrentPlaylistCursor.getShort(column);
}
@Override
public int getInt(int column)
{
try {
return mCurrentPlaylistCursor.getInt(column);
} catch (Exception ex) {
onChange(true);
return 0;
}
}
@Override
public long getLong(int column)
{
try {
return mCurrentPlaylistCursor.getLong(column);
} catch (Exception ex) {
onChange(true);
return 0;
}
}
@Override
public float getFloat(int column)
{
return mCurrentPlaylistCursor.getFloat(column);
}
@Override
public double getDouble(int column)
{
return mCurrentPlaylistCursor.getDouble(column);
}
@Override
public boolean isNull(int column)
{
return mCurrentPlaylistCursor.isNull(column);
}
@Override
public String[] getColumnNames()
{
return mCols;
}
@Override
public boolean requery()
{
makeNowPlayingCursor();
return true;
}
@Override
public void close() {
if (mCurrentPlaylistCursor != null) {
mCurrentPlaylistCursor.close();
mCurrentPlaylistCursor = null;
}
}
private String [] mCols;
private Cursor mCurrentPlaylistCursor; // updated in onMove
private int mSize; // size of the queue
private long[] mNowPlaying;
private long[] mCursorIdxs;
private int mCurPos;
private IMediaPlaybackService mService;
}
class TrackListAdapter extends SimpleDragSortCursorAdapter {
int mUriIdx;
int mTitleIdx;
int mArtistIdx;
int mDurationIdx;
int mAudioIdIdx;
private final StringBuilder mBuilder = new StringBuilder();
private NowPlayingActivity mActivity = null;
private TrackQueryHandler mQueryHandler;
private String mConstraint = null;
private boolean mConstraintIsValid = false;
class ViewHolder {
TextView line1;
TextView line2;
TextView duration;
ImageView play_indicator;
CharArrayBuffer buffer1;
char [] buffer2;
ImageView icon;
}
class TrackQueryHandler extends AsyncQueryHandler {
class QueryArgs {
public Uri uri;
public String [] projection;
public String selection;
public String [] selectionArgs;
public String orderBy;
}
TrackQueryHandler(ContentResolver res) {
super(res);
}
public Cursor doQuery(Uri uri, String[] projection,
String selection, String[] selectionArgs,
String orderBy, boolean async) {
if (async) {
// Get 100 results first, which is enough to allow the user to start scrolling,
// while still being very fast.
Uri limituri = uri.buildUpon().appendQueryParameter("limit", "100").build();
QueryArgs args = new QueryArgs();
args.uri = uri;
args.projection = projection;
args.selection = selection;
args.selectionArgs = selectionArgs;
args.orderBy = orderBy;
startQuery(0, args, limituri, projection, selection, selectionArgs, orderBy);
return null;
}
return MusicUtils.query(mActivity,
uri, projection, selection, selectionArgs, orderBy);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
//Log.i("@@@", "query complete: " + cursor.getCount() + " " + mActivity);
mActivity.init(cursor, cookie != null);
if (token == 0 && cookie != null && cursor != null && cursor.getCount() >= 100) {
QueryArgs args = (QueryArgs) cookie;
startQuery(1, null, args.uri, args.projection, args.selection,
args.selectionArgs, args.orderBy);
}
}
}
TrackListAdapter(Context context, NowPlayingActivity currentactivity,
int layout, Cursor cursor, String[] from, int[] to) {
super(context, layout, cursor, from, to);
mActivity = currentactivity;
getColumnIndices(cursor);
mQueryHandler = new TrackQueryHandler(context.getContentResolver());
}
public void setActivity(NowPlayingActivity newactivity) {
mActivity = newactivity;
}
public TrackQueryHandler getQueryHandler() {
return mQueryHandler;
}
private void getColumnIndices(Cursor cursor) {
if (cursor != null) {
mUriIdx = cursor.getColumnIndexOrThrow(Media.MediaColumns.URI);
mTitleIdx = cursor.getColumnIndexOrThrow(Media.MediaColumns.TITLE);
mArtistIdx = cursor.getColumnIndexOrThrow(Media.MediaColumns.ARTIST);
mDurationIdx = cursor.getColumnIndexOrThrow(Media.MediaColumns.DURATION);
try {
mAudioIdIdx = cursor.getColumnIndexOrThrow(
MediaStore.Audio.Playlists.Members.AUDIO_ID);
} catch (IllegalArgumentException ex) {
mAudioIdIdx = cursor.getColumnIndexOrThrow(Media.MediaColumns._ID);
}
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = super.newView(context, cursor, parent);
ViewHolder vh = new ViewHolder();
vh.line1 = (TextView) v.findViewById(R.id.line1);
vh.line2 = (TextView) v.findViewById(R.id.line2);
vh.duration = (TextView) v.findViewById(R.id.duration);
vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
vh.buffer1 = new CharArrayBuffer(100);
vh.buffer2 = new char[200];
vh.icon = (ImageView) v.findViewById(R.id.icon);
v.setTag(vh);
return v;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder vh = (ViewHolder) view.getTag();
String trackName = cursor.getString(mTitleIdx);
if (trackName == null || trackName.equals(Media.UNKNOWN_STRING)) {
vh.line1.setText(R.string.widget_one_track_info_unavailable);
} else {
cursor.copyStringToBuffer(mTitleIdx, vh.buffer1);
vh.line1.setText(vh.buffer1.data, 0, vh.buffer1.sizeCopied);
}
int secs = cursor.getInt(mDurationIdx) / 1000;
if (secs == 0) {
vh.duration.setText(" ");
} else {
vh.duration.setText(MusicUtils.makeTimeString(context, secs));
}
final StringBuilder builder = mBuilder;
builder.delete(0, builder.length());
String artistName = cursor.getString(mArtistIdx);
if (artistName == null || artistName.equals(Media.UNKNOWN_STRING)) {
builder.append(cursor.getString(mUriIdx));
} else {
builder.append(artistName);
}
int len = builder.length();
if (vh.buffer2.length < len) {
vh.buffer2 = new char[len];
}
builder.getChars(0, len, vh.buffer2, 0);
vh.line2.setText(vh.buffer2, 0, len);
ImageView iv = vh.icon;
iv.setVisibility(View.GONE);
if (mPreferences.getBoolean(PreferenceConstants.RETRIEVE_ALBUM_ART, false)) {
long id = cursor.getInt(mAudioIdIdx);
Drawable d = MusicUtils.getCachedMediumArtwork(NowPlayingActivity.this, id);
if (d != null) {
iv.setImageDrawable(d);
iv.setVisibility(View.VISIBLE);
}
}
iv = vh.play_indicator;
long id = -1;
boolean isPlaying = false;
if (MusicUtils.sService != null) {
// TODO: IPC call on each bind??
try {
id = MusicUtils.sService.getQueuePosition();
isPlaying = MusicUtils.sService.isPlaying();
} catch (RemoteException ex) {
}
}
// Determining whether and where to show the "now playing indicator
// is tricky, because we don't actually keep track of where the songs
// in the current playlist came from after they've started playing.
//
// If the "current playlists" is shown, then we can simply match by position,
// otherwise, we need to match by id. Match-by-id gets a little weird if
// a song appears in a playlist more than once, and you're in edit-playlist
// mode. In that case, both items will have the "now playing" indicator.
// For this reason, we don't show the play indicator at all when in edit
// playlist mode (except when you're viewing the "current playlist",
// which is not really a playlist)
if ( (cursor.getPosition() == id)) {
if (isPlaying) {
iv.setImageResource(R.drawable.indicator_ic_mp_playing_list);
} else {
iv.setImageResource(R.drawable.indicator_ic_mp_paused_list);
}
iv.setVisibility(View.VISIBLE);
} else {
iv.setVisibility(View.GONE);
}
}
@Override
public void changeCursor(Cursor cursor) {
if (mActivity.isFinishing() && cursor != null) {
cursor.close();
cursor = null;
}
if (cursor != mTrackCursor) {
mTrackCursor = cursor;
super.changeCursor(cursor);
getColumnIndices(cursor);
}
}
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
String s = constraint.toString();
if (mConstraintIsValid && (
(s == null && mConstraint == null) ||
(s != null && s.equals(mConstraint)))) {
return getCursor();
}
Cursor c = mActivity.getTrackCursor(mQueryHandler, s, false);
mConstraint = s;
mConstraintIsValid = true;
return c;
}
@Override
public void drop(int from, int to) {
setDragging(false);
if (from == to) {
return;
}
if (mShouldRefresh) {
if (mAdapter != null) {
Cursor c = new NowPlayingCursor(MusicUtils.sService, mCursorCols);
if (c.getCount() == 0) {
c.close();
finish();
return;
}
mAdapter.changeCursor(c);
}
}
mShouldRefresh = false;
// update the currently playing list
NowPlayingCursor c = (NowPlayingCursor) mTrackCursor;
c.moveItem(from, to);
//((DragSortListView.AdapterWrapper)mList.getAdapter()).notifyDataSetChanged();
NowPlayingActivity.this.mList.invalidateViews();
mDeletedOneRow = true;
}
@Override
public void remove(int which) {
removePlaylistItem(which);
}
/**
* Does nothing. Just completes DragSortListener interface.
*/
@Override
public void drag(int from, int to) {
setDragging(true);
}
}
private synchronized void setDragging(boolean isDragging) {
mIsDragging = isDragging;
}
private synchronized boolean isDragging() {
return mIsDragging;
}
}