X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fcom%2Fandroid%2Fmusic%2FTrackBrowserActivity.java;h=f9728920976864d1b44dfd235a1f9efbc0a284d5;hb=3193d7dda28d5c31b318b024db4df40b949071d5;hp=389bb1deff38df530eaa6385d092df36e088dae4;hpb=539844af4c430cf8d1f138d1482520bc4cd3847d;p=android-x86%2Fpackages-apps-Music.git diff --git a/src/com/android/music/TrackBrowserActivity.java b/src/com/android/music/TrackBrowserActivity.java index 389bb1d..f972892 100644 --- a/src/com/android/music/TrackBrowserActivity.java +++ b/src/com/android/music/TrackBrowserActivity.java @@ -31,6 +31,7 @@ import android.content.ServiceConnection; import android.database.AbstractCursor; import android.database.CharArrayBuffer; import android.database.Cursor; +import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaFile; import android.net.Uri; @@ -65,13 +66,13 @@ import java.util.Arrays; public class TrackBrowserActivity extends ListActivity implements View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection { - private final int Q_SELECTED = CHILD_MENU_BASE; - private final int Q_ALL = CHILD_MENU_BASE + 1; - private final int SAVE_AS_PLAYLIST = CHILD_MENU_BASE + 2; - private final int PLAY_ALL = CHILD_MENU_BASE + 3; - private final int CLEAR_PLAYLIST = CHILD_MENU_BASE + 4; - private final int REMOVE = CHILD_MENU_BASE + 5; - private final int SEARCH = CHILD_MENU_BASE + 6; + private static final int Q_SELECTED = CHILD_MENU_BASE; + private static final int Q_ALL = CHILD_MENU_BASE + 1; + private static final int SAVE_AS_PLAYLIST = CHILD_MENU_BASE + 2; + private static final int PLAY_ALL = CHILD_MENU_BASE + 3; + private static final int CLEAR_PLAYLIST = CHILD_MENU_BASE + 4; + private static final int REMOVE = CHILD_MENU_BASE + 5; + private static final int SEARCH = CHILD_MENU_BASE + 6; private static final String LOGTAG = "TrackBrowser"; @@ -94,6 +95,9 @@ public class TrackBrowserActivity extends ListActivity private String mSortOrder; private int mSelectedPosition; private long mSelectedId; + private static int mLastListPosCourse = -1; + private static int mLastListPosFine = -1; + private boolean mUseLastListPos = false; public TrackBrowserActivity() { @@ -105,6 +109,12 @@ public class TrackBrowserActivity extends ListActivity { super.onCreate(icicle); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + Intent intent = getIntent(); + if (intent != null) { + if (intent.getBooleanExtra("withtabs", false)) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + } setVolumeControlStream(AudioManager.STREAM_MUSIC); if (icicle != null) { mSelectedId = icicle.getLong("selectedtrack"); @@ -114,10 +124,9 @@ public class TrackBrowserActivity extends ListActivity mGenre = icicle.getString("genre"); mEditMode = icicle.getBoolean("editmode", false); } else { - mAlbumId = getIntent().getStringExtra("album"); + mAlbumId = intent.getStringExtra("album"); // If we have an album, show everything on the album, not just stuff // by a particular artist. - Intent intent = getIntent(); mArtistId = intent.getStringExtra("artist"); mPlaylist = intent.getStringExtra("playlist"); mGenre = intent.getStringExtra("genre"); @@ -144,10 +153,36 @@ public class TrackBrowserActivity extends ListActivity MediaStore.Audio.Media.ARTIST_ID, MediaStore.Audio.Media.DURATION, MediaStore.Audio.Playlists.Members.PLAY_ORDER, - MediaStore.Audio.Playlists.Members.AUDIO_ID + MediaStore.Audio.Playlists.Members.AUDIO_ID, + MediaStore.Audio.Media.IS_MUSIC }; + setContentView(R.layout.media_picker_activity); + mUseLastListPos = MusicUtils.updateButtonBar(this, R.id.songtab); + mTrackList = getListView(); + mTrackList.setOnCreateContextMenuListener(this); + if (mEditMode) { + ((TouchInterceptor) mTrackList).setDropListener(mDropListener); + ((TouchInterceptor) mTrackList).setRemoveListener(mRemoveListener); + mTrackList.setCacheColorHint(0); + } else { + mTrackList.setTextFilterEnabled(true); + } + mAdapter = (TrackListAdapter) getLastNonConfigurationInstance(); + + if (mAdapter != null) { + mAdapter.setActivity(this); + setListAdapter(mAdapter); + } MusicUtils.bindToService(this, this); + + // don't set the album art until after the view has been layed out + mTrackList.post(new Runnable() { + + public void run() { + setAlbumArtBackground(); + } + }); } public void onServiceConnected(ComponentName name, IBinder service) @@ -159,18 +194,6 @@ public class TrackBrowserActivity extends ListActivity f.addDataScheme("file"); registerReceiver(mScanListener, f); - setContentView(R.layout.media_picker_activity); - mTrackList = getListView(); - mTrackList.setOnCreateContextMenuListener(this); - if (mEditMode) { - //((TouchInterceptor) mTrackList).setDragListener(mDragListener); - ((TouchInterceptor) mTrackList).setDropListener(mDropListener); - ((TouchInterceptor) mTrackList).setRemoveListener(mRemoveListener); - mTrackList.setCacheColorHint(0); - } else { - mTrackList.setTextFilterEnabled(true); - } - mAdapter = (TrackListAdapter) getLastNonConfigurationInstance(); if (mAdapter == null) { //Log.i("@@@", "starting query"); mAdapter = new TrackListAdapter( @@ -185,10 +208,8 @@ public class TrackBrowserActivity extends ListActivity !(mPlaylist.equals("podcasts") || mPlaylist.equals("recentlyadded"))); setListAdapter(mAdapter); setTitle(R.string.working_songs); - getTrackCursor(mAdapter.getQueryHandler(), null); + getTrackCursor(mAdapter.getQueryHandler(), null, true); } else { - mAdapter.setActivity(this); - setListAdapter(mAdapter); 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 @@ -197,12 +218,15 @@ public class TrackBrowserActivity extends ListActivity // 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); + init(mTrackCursor, false); } else { setTitle(R.string.working_songs); - getTrackCursor(mAdapter.getQueryHandler(), null); + getTrackCursor(mAdapter.getQueryHandler(), null, true); } } + if (!mEditMode) { + MusicUtils.updateNowPlaying(this); + } } public void onServiceDisconnected(ComponentName name) { @@ -219,12 +243,20 @@ public class TrackBrowserActivity extends ListActivity @Override public void onDestroy() { + ListView lv = getListView(); + if (lv != null && mUseLastListPos) { + mLastListPosCourse = lv.getFirstVisiblePosition(); + View cv = lv.getChildAt(0); + if (cv != null) { + mLastListPosFine = cv.getTop(); + } + } MusicUtils.unbindFromService(this); try { if ("nowplaying".equals(mPlaylist)) { - unregisterReceiver(mNowPlayingListener); + unregisterReceiverSafe(mNowPlayingListener); } else { - unregisterReceiver(mTrackListListener); + unregisterReceiverSafe(mTrackListListener); } } catch (IllegalArgumentException ex) { // we end up here in case we never registered the listeners @@ -238,9 +270,28 @@ public class TrackBrowserActivity extends ListActivity c.close(); } } - unregisterReceiver(mScanListener); + // 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. + setListAdapter(null); + mAdapter = null; + unregisterReceiverSafe(mScanListener); super.onDestroy(); - } + } + + /** + * Unregister a receiver, but eat the exception that is thrown if the + * receiver was never registered to begin with. This is a little easier + * than keeping track of whether the receivers have actually been + * registered by the time onDestroy() is called. + */ + private void unregisterReceiverSafe(BroadcastReceiver receiver) { + try { + unregisterReceiver(receiver); + } catch (IllegalArgumentException e) { + // ignore + } + } @Override public void onResume() { @@ -275,7 +326,9 @@ public class TrackBrowserActivity extends ListActivity private Handler mReScanHandler = new Handler() { @Override public void handleMessage(Message msg) { - getTrackCursor(mAdapter.getQueryHandler(), null); + if (mAdapter != null) { + getTrackCursor(mAdapter.getQueryHandler(), null, true); + } // if the query results in a null cursor, onQueryComplete() will // call init(), which will post a delayed message to this handler // in order to try again. @@ -295,8 +348,11 @@ public class TrackBrowserActivity extends ListActivity super.onSaveInstanceState(outcicle); } - public void init(Cursor newCursor) { + public void init(Cursor newCursor, boolean isLimited) { + if (mAdapter == null) { + return; + } mAdapter.changeCursor(newCursor); // also sets mTrackCursor if (mTrackCursor == null) { @@ -305,10 +361,23 @@ public class TrackBrowserActivity extends ListActivity mReScanHandler.sendEmptyMessageDelayed(0, 1000); return; } - + MusicUtils.hideDatabaseError(this); + mUseLastListPos = MusicUtils.updateButtonBar(this, R.id.songtab); setTitle(); + // Restore previous position + if (mLastListPosCourse >= 0 && mUseLastListPos) { + ListView lv = getListView(); + // this hack is needed because otherwise the position doesn't change + // for the 2nd (non-limited) cursor + lv.setAdapter(lv.getAdapter()); + lv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine); + if (!isLimited) { + mLastListPosCourse = -1; + } + } + // 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(); @@ -341,6 +410,21 @@ public class TrackBrowserActivity extends ListActivity } } + private void setAlbumArtBackground() { + try { + long albumid = Long.valueOf(mAlbumId); + Bitmap bm = MusicUtils.getArtwork(TrackBrowserActivity.this, -1, albumid, false); + if (bm != null) { + MusicUtils.setBackground(mTrackList, bm); + mTrackList.setCacheColorHint(0); + return; + } + } catch (Exception ex) { + } + mTrackList.setBackgroundResource(0); + mTrackList.setCacheColorHint(0xff000000); + } + private void setTitle() { CharSequence fancyName = null; @@ -369,7 +453,7 @@ public class TrackBrowserActivity extends ListActivity } cursor.deactivate(); } - if (fancyName.equals(MediaFile.UNKNOWN_STRING)) { + if (fancyName == null || fancyName.equals(MediaFile.UNKNOWN_STRING)) { fancyName = getString(R.string.unknown_album_name); } } @@ -422,18 +506,6 @@ public class TrackBrowserActivity extends ListActivity } } - private TouchInterceptor.DragListener mDragListener = - new TouchInterceptor.DragListener() { - public void drag(int from, int to) { - if (mTrackCursor instanceof NowPlayingCursor) { - NowPlayingCursor c = (NowPlayingCursor) mTrackCursor; - c.moveItem(from, to); - ((TrackListAdapter)getListAdapter()).notifyDataSetChanged(); - getListView().invalidateViews(); - mDeletedOneRow = true; - } - } - }; private TouchInterceptor.DropListener mDropListener = new TouchInterceptor.DropListener() { public void drop(int from, int to) { @@ -458,7 +530,7 @@ public class TrackBrowserActivity extends ListActivity if (from < to) { // move the item to somewhere later in the list mTrackCursor.moveToPosition(to); - int toidx = mTrackCursor.getInt(colidx); + long toidx = mTrackCursor.getLong(colidx); mTrackCursor.moveToPosition(from); values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, toidx); wherearg[0] = mTrackCursor.getString(0); @@ -472,7 +544,7 @@ public class TrackBrowserActivity extends ListActivity } else if (from > to) { // move the item to somewhere earlier in the list mTrackCursor.moveToPosition(to); - int toidx = mTrackCursor.getInt(colidx); + long toidx = mTrackCursor.getLong(colidx); mTrackCursor.moveToPosition(from); values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, toidx); wherearg[0] = mTrackCursor.getString(0); @@ -528,6 +600,9 @@ public class TrackBrowserActivity extends ListActivity @Override public void onReceive(Context context, Intent intent) { getListView().invalidateViews(); + if (!mEditMode) { + MusicUtils.updateNowPlaying(TrackBrowserActivity.this); + } } }; @@ -554,6 +629,33 @@ public class TrackBrowserActivity extends ListActivity } }; + // Cursor should be positioned on the entry to be checked + // Returns false if the entry matches the naming pattern used for recordings, + // or if it is marked as not music in the database. + private boolean isMusic(Cursor c) { + int titleidx = c.getColumnIndex(MediaStore.Audio.Media.TITLE); + int albumidx = c.getColumnIndex(MediaStore.Audio.Media.ALBUM); + int artistidx = c.getColumnIndex(MediaStore.Audio.Media.ARTIST); + + String title = c.getString(titleidx); + String album = c.getString(albumidx); + String artist = c.getString(artistidx); + if (MediaFile.UNKNOWN_STRING.equals(album) && + MediaFile.UNKNOWN_STRING.equals(artist) && + title != null && + title.startsWith("recording")) { + // not music + return false; + } + + int ismusic_idx = c.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC); + boolean ismusic = true; + if (ismusic_idx >= 0) { + ismusic = mTrackCursor.getInt(ismusic_idx) != 0; + } + return ismusic; + } + @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) { menu.add(0, PLAY_SELECTION, 0, R.string.play_selection); @@ -564,17 +666,20 @@ public class TrackBrowserActivity extends ListActivity } menu.add(0, USE_AS_RINGTONE, 0, R.string.ringtone_menu); menu.add(0, DELETE_ITEM, 0, R.string.delete_item); - menu.add(0, SEARCH, 0, R.string.search_title); AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn; mSelectedPosition = mi.position; mTrackCursor.moveToPosition(mSelectedPosition); try { int id_idx = mTrackCursor.getColumnIndexOrThrow( MediaStore.Audio.Playlists.Members.AUDIO_ID); - mSelectedId = mTrackCursor.getInt(id_idx); + mSelectedId = mTrackCursor.getLong(id_idx); } catch (IllegalArgumentException ex) { mSelectedId = mi.id; } + // only add the 'search' menu if the selected item is music + if (isMusic(mTrackCursor)) { + menu.add(0, SEARCH, 0, R.string.search_title); + } mCurrentAlbumName = mTrackCursor.getString(mTrackCursor.getColumnIndexOrThrow( MediaStore.Audio.Media.ALBUM)); mCurrentArtistNameForAlbum = mTrackCursor.getString(mTrackCursor.getColumnIndexOrThrow( @@ -595,7 +700,7 @@ public class TrackBrowserActivity extends ListActivity } case QUEUE: { - int [] list = new int[] { (int) mSelectedId }; + long [] list = new long[] { mSelectedId }; MusicUtils.addToCurrentPlaylist(this, list); return true; } @@ -608,8 +713,8 @@ public class TrackBrowserActivity extends ListActivity } case PLAYLIST_SELECTED: { - int [] list = new int[] { (int) mSelectedId }; - int playlist = item.getIntent().getIntExtra("playlist", 0); + long [] list = new long[] { mSelectedId }; + long playlist = item.getIntent().getLongExtra("playlist", 0); MusicUtils.addToPlaylist(this, list, playlist); return true; } @@ -620,13 +725,13 @@ public class TrackBrowserActivity extends ListActivity return true; case DELETE_ITEM: { - int [] list = new int[1]; + long [] list = new long[1]; list[0] = (int) mSelectedId; Bundle b = new Bundle(); String f = getString(R.string.delete_song_desc); String desc = String.format(f, mCurrentTrackName); b.putString("description", desc); - b.putIntArray("items", list); + b.putLongArray("items", list); Intent intent = new Intent(); intent.setClass(this, DeleteItems.class); intent.putExtras(b); @@ -651,11 +756,18 @@ public class TrackBrowserActivity extends ListActivity Intent i = new Intent(); i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - title = mCurrentAlbumName; - query = mCurrentArtistNameForAlbum + " " + mCurrentAlbumName; - i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum); - i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName); + title = mCurrentTrackName; + if (MediaFile.UNKNOWN_STRING.equals(mCurrentArtistNameForAlbum)) { + query = mCurrentTrackName; + } else { + query = mCurrentArtistNameForAlbum + " " + mCurrentTrackName; + i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum); + } + if (MediaFile.UNKNOWN_STRING.equals(mCurrentAlbumName)) { + i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName); + } i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, "audio/*"); title = getString(R.string.mediasearch, title); i.putExtra(SearchManager.QUERY, query); @@ -781,6 +893,18 @@ public class TrackBrowserActivity extends ListActivity 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 (mTrackCursor instanceof NowPlayingCursor) { + if (MusicUtils.sService != null) { + try { + MusicUtils.sService.setQueuePosition(position); + return; + } catch (RemoteException ex) { + } + } + } MusicUtils.playAll(this, mTrackCursor, position); } @@ -795,9 +919,7 @@ public class TrackBrowserActivity extends ListActivity if (mPlaylist == null) { menu.add(0, PLAY_ALL, 0, R.string.play_all).setIcon(com.android.internal.R.drawable.ic_menu_play_clip); } - menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(R.drawable.ic_menu_music_library); - menu.add(0, GOTO_PLAYBACK, 0, R.string.goto_playback).setIcon(R.drawable.ic_menu_playback) - .setVisible(MusicUtils.isMusicLoaded()); + menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu() menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle); if (mPlaylist != null) { menu.add(0, SAVE_AS_PLAYLIST, 0, R.string.save_as_playlist).setIcon(android.R.drawable.ic_menu_save); @@ -809,6 +931,12 @@ public class TrackBrowserActivity extends ListActivity } @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MusicUtils.setPartyShuffleMenuIcon(menu); + return super.onPrepareOptionsMenu(menu); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { Intent intent; Cursor cursor; @@ -818,18 +946,9 @@ public class TrackBrowserActivity extends ListActivity return true; } - case GOTO_START: - intent = new Intent(); - intent.setClass(this, MusicBrowserActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case GOTO_PLAYBACK: - intent = new Intent("com.android.music.PLAYBACK_VIEWER"); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; + case PARTY_SHUFFLE: + MusicUtils.togglePartyShuffle(); + break; case SHUFFLE_ALL: // Should 'shuffle all' shuffle ALL, or only the tracks shown? @@ -864,7 +983,7 @@ public class TrackBrowserActivity extends ListActivity if (resultCode == RESULT_CANCELED) { finish(); } else { - getTrackCursor(mAdapter.getQueryHandler(), null); + getTrackCursor(mAdapter.getQueryHandler(), null, true); } break; @@ -872,7 +991,7 @@ public class TrackBrowserActivity extends ListActivity if (resultCode == RESULT_OK) { Uri uri = intent.getData(); if (uri != null) { - int [] list = new int[] { (int) mSelectedId }; + long [] list = new long[] { mSelectedId }; MusicUtils.addToPlaylist(this, list, Integer.valueOf(uri.getLastPathSegment())); } } @@ -882,7 +1001,7 @@ public class TrackBrowserActivity extends ListActivity if (resultCode == RESULT_OK) { Uri uri = intent.getData(); if (uri != null) { - int [] list = MusicUtils.getSongListForCursor(mTrackCursor); + long [] list = MusicUtils.getSongListForCursor(mTrackCursor); int plid = Integer.parseInt(uri.getLastPathSegment()); MusicUtils.addToPlaylist(this, list, plid); } @@ -891,12 +1010,18 @@ public class TrackBrowserActivity extends ListActivity } } - private Cursor getTrackCursor(AsyncQueryHandler async, String filter) { + private Cursor getTrackCursor(TrackListAdapter.TrackQueryHandler queryhandler, String filter, + boolean async) { + + if (queryhandler == null) { + throw new IllegalArgumentException(); + } + Cursor ret = null; mSortOrder = MediaStore.Audio.Media.TITLE_KEY; StringBuilder where = new StringBuilder(); where.append(MediaStore.Audio.Media.TITLE + " != ''"); - + // Add in the filtering constraints String [] keywords = null; if (filter != null) { @@ -910,24 +1035,15 @@ public class TrackBrowserActivity extends ListActivity for (int i = 0; i < searchWords.length; i++) { where.append(" AND "); where.append(MediaStore.Audio.Media.ARTIST_KEY + "||"); - where.append(MediaStore.Audio.Media.ALBUM_KEY + "||"); where.append(MediaStore.Audio.Media.TITLE_KEY + " LIKE ?"); } } if (mGenre != null) { mSortOrder = MediaStore.Audio.Genres.Members.DEFAULT_SORT_ORDER; - if (async != null) { - async.startQuery(0, null, - MediaStore.Audio.Genres.Members.getContentUri("external", - Integer.valueOf(mGenre)), - mCursorCols, where.toString(), keywords, mSortOrder); - ret = null; - } else { - ret = MusicUtils.query(this, - MediaStore.Audio.Genres.Members.getContentUri("external", Integer.valueOf(mGenre)), - mCursorCols, where.toString(), keywords, mSortOrder); - } + ret = queryhandler.doQuery(MediaStore.Audio.Genres.Members.getContentUri("external", + Integer.valueOf(mGenre)), + mCursorCols, where.toString(), keywords, mSortOrder, async); } else if (mPlaylist != null) { if (mPlaylist.equals("nowplaying")) { if (MusicUtils.sService != null) { @@ -940,43 +1056,22 @@ public class TrackBrowserActivity extends ListActivity } } else if (mPlaylist.equals("podcasts")) { where.append(" AND " + MediaStore.Audio.Media.IS_PODCAST + "=1"); - if (async != null) { - async.startQuery(0, null, - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursorCols, - where.toString(), keywords, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); - ret = null; - } else { - ret = MusicUtils.query(this, - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursorCols, - where.toString(), keywords, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); - } + ret = queryhandler.doQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + mCursorCols, where.toString(), keywords, + MediaStore.Audio.Media.DEFAULT_SORT_ORDER, async); } else if (mPlaylist.equals("recentlyadded")) { // do a query for all songs added in the last X weeks int X = MusicUtils.getIntPref(this, "numweeks", 2) * (3600 * 24 * 7); where.append(" AND " + MediaStore.MediaColumns.DATE_ADDED + ">"); where.append(System.currentTimeMillis() / 1000 - X); - if (async != null) { - async.startQuery(0, null, - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursorCols, - where.toString(), keywords, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); - ret = null; - } else { - ret = MusicUtils.query(this, - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCursorCols, - where.toString(), keywords, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); - } + ret = queryhandler.doQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + mCursorCols, where.toString(), keywords, + MediaStore.Audio.Media.DEFAULT_SORT_ORDER, async); } else { mSortOrder = MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER; - if (async != null) { - async.startQuery(0, null, - MediaStore.Audio.Playlists.Members.getContentUri("external", Long.valueOf(mPlaylist)), - mPlaylistMemberCols, where.toString(), keywords, mSortOrder); - ret = null; - } else { - ret = MusicUtils.query(this, - MediaStore.Audio.Playlists.Members.getContentUri("external", Long.valueOf(mPlaylist)), - mPlaylistMemberCols, where.toString(), keywords, mSortOrder); - } + ret = queryhandler.doQuery(MediaStore.Audio.Playlists.Members.getContentUri("external", + Long.valueOf(mPlaylist)), mPlaylistMemberCols, + where.toString(), keywords, mSortOrder, async); } } else { if (mAlbumId != null) { @@ -987,21 +1082,14 @@ public class TrackBrowserActivity extends ListActivity where.append(" AND " + MediaStore.Audio.Media.ARTIST_ID + "=" + mArtistId); } where.append(" AND " + MediaStore.Audio.Media.IS_MUSIC + "=1"); - if (async != null) { - async.startQuery(0, null, - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - mCursorCols, where.toString() , keywords, mSortOrder); - ret = null; - } else { - ret = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - mCursorCols, where.toString() , keywords, mSortOrder); - } + ret = queryhandler.doQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + mCursorCols, where.toString() , keywords, mSortOrder, async); } // 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 != null) { - init(ret); + if (ret != null && async) { + init(ret, false); setTitle(); } return ret; @@ -1020,7 +1108,7 @@ public class TrackBrowserActivity extends ListActivity try { mNowPlaying = mService.getQueue(); } catch (RemoteException ex) { - mNowPlaying = new int[0]; + mNowPlaying = new long[0]; } mSize = mNowPlaying.length; if (mSize == 0) { @@ -1047,11 +1135,11 @@ public class TrackBrowserActivity extends ListActivity } int size = mCurrentPlaylistCursor.getCount(); - mCursorIdxs = new int[size]; + mCursorIdxs = new long[size]; mCurrentPlaylistCursor.moveToFirst(); int colidx = mCurrentPlaylistCursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID); for (int i = 0; i < size; i++) { - mCursorIdxs[i] = mCurrentPlaylistCursor.getInt(colidx); + mCursorIdxs[i] = mCurrentPlaylistCursor.getLong(colidx); mCurrentPlaylistCursor.moveToNext(); } mCurrentPlaylistCursor.moveToFirst(); @@ -1064,7 +1152,7 @@ public class TrackBrowserActivity extends ListActivity try { int removed = 0; for (int i = mNowPlaying.length - 1; i >= 0; i--) { - int trackid = mNowPlaying[i]; + long trackid = mNowPlaying[i]; int crsridx = Arrays.binarySearch(mCursorIdxs, trackid); if (crsridx < 0) { //Log.i("@@@@@", "item no longer exists in db: " + trackid); @@ -1080,7 +1168,7 @@ public class TrackBrowserActivity extends ListActivity } } } catch (RemoteException ex) { - mNowPlaying = new int[0]; + mNowPlaying = new long[0]; } } @@ -1104,7 +1192,7 @@ public class TrackBrowserActivity extends ListActivity // in queue-order, so we need to figure out where in the cursor we // should be. - int newid = mNowPlaying[newPosition]; + long newid = mNowPlaying[newPosition]; int crsridx = Arrays.binarySearch(mCursorIdxs, newid); mCurrentPlaylistCursor.moveToPosition(crsridx); mCurPos = newPosition; @@ -1231,8 +1319,8 @@ public class TrackBrowserActivity extends ListActivity private String [] mCols; private Cursor mCurrentPlaylistCursor; // updated in onMove private int mSize; // size of the queue - private int[] mNowPlaying; - private int[] mCursorIdxs; + private long[] mNowPlaying; + private long[] mCursorIdxs; private int mCurPos; private IMediaPlaybackService mService; } @@ -1243,7 +1331,6 @@ public class TrackBrowserActivity extends ListActivity int mTitleIdx; int mArtistIdx; - int mAlbumIdx; int mDurationIdx; int mAudioIdIdx; @@ -1254,9 +1341,11 @@ public class TrackBrowserActivity extends ListActivity private AlphabetIndexer mIndexer; private TrackBrowserActivity mActivity = null; - private AsyncQueryHandler mQueryHandler; + private TrackQueryHandler mQueryHandler; + private String mConstraint = null; + private boolean mConstraintIsValid = false; - class ViewHolder { + static class ViewHolder { TextView line1; TextView line2; TextView duration; @@ -1265,15 +1354,50 @@ public class TrackBrowserActivity extends ListActivity char [] buffer2; } - class QueryHandler extends AsyncQueryHandler { - QueryHandler(ContentResolver res) { + 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); + 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); + } } } @@ -1288,14 +1412,14 @@ public class TrackBrowserActivity extends ListActivity mUnknownArtist = context.getString(R.string.unknown_artist_name); mUnknownAlbum = context.getString(R.string.unknown_album_name); - mQueryHandler = new QueryHandler(context.getContentResolver()); + mQueryHandler = new TrackQueryHandler(context.getContentResolver()); } public void setActivity(TrackBrowserActivity newactivity) { mActivity = newactivity; } - public AsyncQueryHandler getQueryHandler() { + public TrackQueryHandler getQueryHandler() { return mQueryHandler; } @@ -1303,7 +1427,6 @@ public class TrackBrowserActivity extends ListActivity if (cursor != null) { mTitleIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE); mArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST); - mAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM); mDurationIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION); try { mAudioIdIdx = cursor.getColumnIndexOrThrow( @@ -1377,7 +1500,7 @@ public class TrackBrowserActivity extends ListActivity vh.line2.setText(vh.buffer2, 0, len); ImageView iv = vh.play_indicator; - int id = -1; + long id = -1; if (MusicUtils.sService != null) { // TODO: IPC call on each bind?? try { @@ -1402,7 +1525,7 @@ public class TrackBrowserActivity extends ListActivity // playlist mode (except when you're viewing the "current playlist", // which is not really a playlist) if ( (mIsNowPlaying && cursor.getPosition() == id) || - (!mIsNowPlaying && !mDisableNowPlayingIndicator && cursor.getInt(mAudioIdIdx) == id)) { + (!mIsNowPlaying && !mDisableNowPlayingIndicator && cursor.getLong(mAudioIdIdx) == id)) { iv.setImageResource(R.drawable.indicator_ic_mp_playing_list); iv.setVisibility(View.VISIBLE); } else { @@ -1421,7 +1544,16 @@ public class TrackBrowserActivity extends ListActivity @Override public Cursor runQueryOnBackgroundThread(CharSequence constraint) { - return mActivity.getTrackCursor(null, constraint.toString()); + 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; } // SectionIndexer methods