Home | History | Annotate | Download | only in music
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.music;
     18 
     19 import com.android.music.MusicUtils.ServiceToken;
     20 
     21 import android.app.ListActivity;
     22 import android.content.AsyncQueryHandler;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ComponentName;
     25 import android.content.ContentResolver;
     26 import android.content.ContentUris;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.ServiceConnection;
     31 import android.database.Cursor;
     32 import android.database.MatrixCursor;
     33 import android.database.MergeCursor;
     34 import android.database.sqlite.SQLiteException;
     35 import android.media.AudioManager;
     36 import android.net.Uri;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.os.IBinder;
     40 import android.os.Message;
     41 import android.provider.MediaStore;
     42 import android.util.Log;
     43 import android.view.ContextMenu;
     44 import android.view.Menu;
     45 import android.view.MenuItem;
     46 import android.view.View;
     47 import android.view.ViewGroup;
     48 import android.view.Window;
     49 import android.view.ContextMenu.ContextMenuInfo;
     50 import android.widget.ImageView;
     51 import android.widget.ListView;
     52 import android.widget.SimpleCursorAdapter;
     53 import android.widget.TextView;
     54 import android.widget.Toast;
     55 import android.widget.AdapterView.AdapterContextMenuInfo;
     56 
     57 import java.text.Collator;
     58 import java.util.ArrayList;
     59 
     60 public class PlaylistBrowserActivity extends ListActivity
     61     implements View.OnCreateContextMenuListener, MusicUtils.Defs
     62 {
     63     private static final String TAG = "PlaylistBrowserActivity";
     64     private static final int DELETE_PLAYLIST = CHILD_MENU_BASE + 1;
     65     private static final int EDIT_PLAYLIST = CHILD_MENU_BASE + 2;
     66     private static final int RENAME_PLAYLIST = CHILD_MENU_BASE + 3;
     67     private static final int CHANGE_WEEKS = CHILD_MENU_BASE + 4;
     68     private static final long RECENTLY_ADDED_PLAYLIST = -1;
     69     private static final long ALL_SONGS_PLAYLIST = -2;
     70     private static final long PODCASTS_PLAYLIST = -3;
     71     private PlaylistListAdapter mAdapter;
     72     boolean mAdapterSent;
     73     private static int mLastListPosCourse = -1;
     74     private static int mLastListPosFine = -1;
     75 
     76     private boolean mCreateShortcut;
     77     private ServiceToken mToken;
     78 
     79     public PlaylistBrowserActivity()
     80     {
     81     }
     82 
     83     /** Called when the activity is first created. */
     84     @Override
     85     public void onCreate(Bundle icicle)
     86     {
     87         super.onCreate(icicle);
     88 
     89         final Intent intent = getIntent();
     90         final String action = intent.getAction();
     91         if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
     92             mCreateShortcut = true;
     93         }
     94 
     95         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
     96         requestWindowFeature(Window.FEATURE_NO_TITLE);
     97         setVolumeControlStream(AudioManager.STREAM_MUSIC);
     98         mToken = MusicUtils.bindToService(this, new ServiceConnection() {
     99             public void onServiceConnected(ComponentName classname, IBinder obj) {
    100                 if (Intent.ACTION_VIEW.equals(action)) {
    101                     long id = Long.parseLong(intent.getExtras().getString("playlist"));
    102                     if (id == RECENTLY_ADDED_PLAYLIST) {
    103                         playRecentlyAdded();
    104                     } else if (id == PODCASTS_PLAYLIST) {
    105                         playPodcasts();
    106                     } else if (id == ALL_SONGS_PLAYLIST) {
    107                         long [] list = MusicUtils.getAllSongs(PlaylistBrowserActivity.this);
    108                         if (list != null) {
    109                             MusicUtils.playAll(PlaylistBrowserActivity.this, list, 0);
    110                         }
    111                     } else {
    112                         MusicUtils.playPlaylist(PlaylistBrowserActivity.this, id);
    113                     }
    114                     finish();
    115                     return;
    116                 }
    117                 MusicUtils.updateNowPlaying(PlaylistBrowserActivity.this);
    118             }
    119 
    120             public void onServiceDisconnected(ComponentName classname) {
    121             }
    122 
    123         });
    124         IntentFilter f = new IntentFilter();
    125         f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
    126         f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    127         f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
    128         f.addDataScheme("file");
    129         registerReceiver(mScanListener, f);
    130 
    131         setContentView(R.layout.media_picker_activity);
    132         MusicUtils.updateButtonBar(this, R.id.playlisttab);
    133         ListView lv = getListView();
    134         lv.setOnCreateContextMenuListener(this);
    135         lv.setTextFilterEnabled(true);
    136 
    137         mAdapter = (PlaylistListAdapter) getLastNonConfigurationInstance();
    138         if (mAdapter == null) {
    139             //Log.i("@@@", "starting query");
    140             mAdapter = new PlaylistListAdapter(
    141                     getApplication(),
    142                     this,
    143                     R.layout.track_list_item,
    144                     mPlaylistCursor,
    145                     new String[] { MediaStore.Audio.Playlists.NAME},
    146                     new int[] { android.R.id.text1 });
    147             setListAdapter(mAdapter);
    148             setTitle(R.string.working_playlists);
    149             getPlaylistCursor(mAdapter.getQueryHandler(), null);
    150         } else {
    151             mAdapter.setActivity(this);
    152             setListAdapter(mAdapter);
    153             mPlaylistCursor = mAdapter.getCursor();
    154             // If mPlaylistCursor is null, this can be because it doesn't have
    155             // a cursor yet (because the initial query that sets its cursor
    156             // is still in progress), or because the query failed.
    157             // In order to not flash the error dialog at the user for the
    158             // first case, simply retry the query when the cursor is null.
    159             // Worst case, we end up doing the same query twice.
    160             if (mPlaylistCursor != null) {
    161                 init(mPlaylistCursor);
    162             } else {
    163                 setTitle(R.string.working_playlists);
    164                 getPlaylistCursor(mAdapter.getQueryHandler(), null);
    165             }
    166         }
    167     }
    168 
    169     @Override
    170     public Object onRetainNonConfigurationInstance() {
    171         PlaylistListAdapter a = mAdapter;
    172         mAdapterSent = true;
    173         return a;
    174     }
    175 
    176     @Override
    177     public void onDestroy() {
    178         ListView lv = getListView();
    179         if (lv != null) {
    180             mLastListPosCourse = lv.getFirstVisiblePosition();
    181             View cv = lv.getChildAt(0);
    182             if (cv != null) {
    183                 mLastListPosFine = cv.getTop();
    184             }
    185         }
    186         MusicUtils.unbindFromService(mToken);
    187         // If we have an adapter and didn't send it off to another activity yet, we should
    188         // close its cursor, which we do by assigning a null cursor to it. Doing this
    189         // instead of closing the cursor directly keeps the framework from accessing
    190         // the closed cursor later.
    191         if (!mAdapterSent && mAdapter != null) {
    192             mAdapter.changeCursor(null);
    193         }
    194         // Because we pass the adapter to the next activity, we need to make
    195         // sure it doesn't keep a reference to this activity. We can do this
    196         // by clearing its DatasetObservers, which setListAdapter(null) does.
    197         setListAdapter(null);
    198         mAdapter = null;
    199         unregisterReceiver(mScanListener);
    200         super.onDestroy();
    201     }
    202 
    203     @Override
    204     public void onResume() {
    205         super.onResume();
    206 
    207         MusicUtils.setSpinnerState(this);
    208         MusicUtils.updateNowPlaying(PlaylistBrowserActivity.this);
    209     }
    210     @Override
    211     public void onPause() {
    212         mReScanHandler.removeCallbacksAndMessages(null);
    213         super.onPause();
    214     }
    215     private BroadcastReceiver mScanListener = new BroadcastReceiver() {
    216         @Override
    217         public void onReceive(Context context, Intent intent) {
    218             MusicUtils.setSpinnerState(PlaylistBrowserActivity.this);
    219             mReScanHandler.sendEmptyMessage(0);
    220         }
    221     };
    222 
    223     private Handler mReScanHandler = new Handler() {
    224         @Override
    225         public void handleMessage(Message msg) {
    226             if (mAdapter != null) {
    227                 getPlaylistCursor(mAdapter.getQueryHandler(), null);
    228             }
    229         }
    230     };
    231     public void init(Cursor cursor) {
    232 
    233         if (mAdapter == null) {
    234             return;
    235         }
    236         mAdapter.changeCursor(cursor);
    237 
    238         if (mPlaylistCursor == null) {
    239             MusicUtils.displayDatabaseError(this);
    240             closeContextMenu();
    241             mReScanHandler.sendEmptyMessageDelayed(0, 1000);
    242             return;
    243         }
    244 
    245         // restore previous position
    246         if (mLastListPosCourse >= 0) {
    247             getListView().setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
    248             mLastListPosCourse = -1;
    249         }
    250         MusicUtils.hideDatabaseError(this);
    251         MusicUtils.updateButtonBar(this, R.id.playlisttab);
    252         setTitle();
    253     }
    254 
    255     private void setTitle() {
    256         setTitle(R.string.playlists_title);
    257     }
    258 
    259     @Override
    260     public boolean onCreateOptionsMenu(Menu menu) {
    261         if (!mCreateShortcut) {
    262             menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
    263         }
    264         return super.onCreateOptionsMenu(menu);
    265     }
    266 
    267     @Override
    268     public boolean onPrepareOptionsMenu(Menu menu) {
    269         MusicUtils.setPartyShuffleMenuIcon(menu);
    270         return super.onPrepareOptionsMenu(menu);
    271     }
    272 
    273     @Override
    274     public boolean onOptionsItemSelected(MenuItem item) {
    275         Intent intent;
    276         switch (item.getItemId()) {
    277             case PARTY_SHUFFLE:
    278                 MusicUtils.togglePartyShuffle();
    279                 break;
    280         }
    281         return super.onOptionsItemSelected(item);
    282     }
    283 
    284     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
    285         if (mCreateShortcut) {
    286             return;
    287         }
    288 
    289         AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn;
    290 
    291         menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
    292 
    293         if (mi.id >= 0 /*|| mi.id == PODCASTS_PLAYLIST*/) {
    294             menu.add(0, DELETE_PLAYLIST, 0, R.string.delete_playlist_menu);
    295         }
    296 
    297         if (mi.id == RECENTLY_ADDED_PLAYLIST) {
    298             menu.add(0, EDIT_PLAYLIST, 0, R.string.edit_playlist_menu);
    299         }
    300 
    301         if (mi.id >= 0) {
    302             menu.add(0, RENAME_PLAYLIST, 0, R.string.rename_playlist_menu);
    303         }
    304 
    305         mPlaylistCursor.moveToPosition(mi.position);
    306         menu.setHeaderTitle(mPlaylistCursor.getString(mPlaylistCursor.getColumnIndexOrThrow(
    307                 MediaStore.Audio.Playlists.NAME)));
    308     }
    309 
    310     @Override
    311     public boolean onContextItemSelected(MenuItem item) {
    312         AdapterContextMenuInfo mi = (AdapterContextMenuInfo) item.getMenuInfo();
    313         switch (item.getItemId()) {
    314             case PLAY_SELECTION:
    315                 if (mi.id == RECENTLY_ADDED_PLAYLIST) {
    316                     playRecentlyAdded();
    317                 } else if (mi.id == PODCASTS_PLAYLIST) {
    318                     playPodcasts();
    319                 } else {
    320                     MusicUtils.playPlaylist(this, mi.id);
    321                 }
    322                 break;
    323             case DELETE_PLAYLIST:
    324                 Uri uri = ContentUris.withAppendedId(
    325                         MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, mi.id);
    326                 getContentResolver().delete(uri, null, null);
    327                 Toast.makeText(this, R.string.playlist_deleted_message, Toast.LENGTH_SHORT).show();
    328                 if (mPlaylistCursor.getCount() == 0) {
    329                     setTitle(R.string.no_playlists_title);
    330                 }
    331                 break;
    332             case EDIT_PLAYLIST:
    333                 if (mi.id == RECENTLY_ADDED_PLAYLIST) {
    334                     Intent intent = new Intent();
    335                     intent.setClass(this, WeekSelector.class);
    336                     startActivityForResult(intent, CHANGE_WEEKS);
    337                     return true;
    338                 } else {
    339                     Log.e(TAG, "should not be here");
    340                 }
    341                 break;
    342             case RENAME_PLAYLIST:
    343                 Intent intent = new Intent();
    344                 intent.setClass(this, RenamePlaylist.class);
    345                 intent.putExtra("rename", mi.id);
    346                 startActivityForResult(intent, RENAME_PLAYLIST);
    347                 break;
    348         }
    349         return true;
    350     }
    351 
    352     @Override
    353     protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    354         switch (requestCode) {
    355             case SCAN_DONE:
    356                 if (resultCode == RESULT_CANCELED) {
    357                     finish();
    358                 } else if (mAdapter != null) {
    359                     getPlaylistCursor(mAdapter.getQueryHandler(), null);
    360                 }
    361                 break;
    362         }
    363     }
    364 
    365     @Override
    366     protected void onListItemClick(ListView l, View v, int position, long id)
    367     {
    368         if (mCreateShortcut) {
    369             final Intent shortcut = new Intent();
    370             shortcut.setAction(Intent.ACTION_VIEW);
    371             shortcut.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/playlist");
    372             shortcut.putExtra("playlist", String.valueOf(id));
    373 
    374             final Intent intent = new Intent();
    375             intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut);
    376             intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, ((TextView) v.findViewById(R.id.line1)).getText());
    377             intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(
    378                     this, R.drawable.ic_launcher_shortcut_music_playlist));
    379 
    380             setResult(RESULT_OK, intent);
    381             finish();
    382             return;
    383         }
    384         if (id == RECENTLY_ADDED_PLAYLIST) {
    385             Intent intent = new Intent(Intent.ACTION_PICK);
    386             intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
    387             intent.putExtra("playlist", "recentlyadded");
    388             startActivity(intent);
    389         } else if (id == PODCASTS_PLAYLIST) {
    390             Intent intent = new Intent(Intent.ACTION_PICK);
    391             intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
    392             intent.putExtra("playlist", "podcasts");
    393             startActivity(intent);
    394         } else {
    395             Intent intent = new Intent(Intent.ACTION_EDIT);
    396             intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
    397             intent.putExtra("playlist", Long.valueOf(id).toString());
    398             startActivity(intent);
    399         }
    400     }
    401 
    402     private void playRecentlyAdded() {
    403         // do a query for all songs added in the last X weeks
    404         int X = MusicUtils.getIntPref(this, "numweeks", 2) * (3600 * 24 * 7);
    405         final String[] ccols = new String[] { MediaStore.Audio.Media._ID};
    406         String where = MediaStore.MediaColumns.DATE_ADDED + ">" + (System.currentTimeMillis() / 1000 - X);
    407         Cursor cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
    408                 ccols, where, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    409 
    410         if (cursor == null) {
    411             // Todo: show a message
    412             return;
    413         }
    414         try {
    415             int len = cursor.getCount();
    416             long [] list = new long[len];
    417             for (int i = 0; i < len; i++) {
    418                 cursor.moveToNext();
    419                 list[i] = cursor.getLong(0);
    420             }
    421             MusicUtils.playAll(this, list, 0);
    422         } catch (SQLiteException ex) {
    423         } finally {
    424             cursor.close();
    425         }
    426     }
    427 
    428     private void playPodcasts() {
    429         // do a query for all files that are podcasts
    430         final String[] ccols = new String[] { MediaStore.Audio.Media._ID};
    431         Cursor cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
    432                 ccols, MediaStore.Audio.Media.IS_PODCAST + "=1",
    433                 null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    434 
    435         if (cursor == null) {
    436             // Todo: show a message
    437             return;
    438         }
    439         try {
    440             int len = cursor.getCount();
    441             long [] list = new long[len];
    442             for (int i = 0; i < len; i++) {
    443                 cursor.moveToNext();
    444                 list[i] = cursor.getLong(0);
    445             }
    446             MusicUtils.playAll(this, list, 0);
    447         } catch (SQLiteException ex) {
    448         } finally {
    449             cursor.close();
    450         }
    451     }
    452 
    453 
    454     String[] mCols = new String[] {
    455             MediaStore.Audio.Playlists._ID,
    456             MediaStore.Audio.Playlists.NAME
    457     };
    458 
    459     private Cursor getPlaylistCursor(AsyncQueryHandler async, String filterstring) {
    460 
    461         StringBuilder where = new StringBuilder();
    462         where.append(MediaStore.Audio.Playlists.NAME + " != ''");
    463 
    464         // Add in the filtering constraints
    465         String [] keywords = null;
    466         if (filterstring != null) {
    467             String [] searchWords = filterstring.split(" ");
    468             keywords = new String[searchWords.length];
    469             Collator col = Collator.getInstance();
    470             col.setStrength(Collator.PRIMARY);
    471             for (int i = 0; i < searchWords.length; i++) {
    472                 keywords[i] = '%' + searchWords[i] + '%';
    473             }
    474             for (int i = 0; i < searchWords.length; i++) {
    475                 where.append(" AND ");
    476                 where.append(MediaStore.Audio.Playlists.NAME + " LIKE ?");
    477             }
    478         }
    479 
    480         String whereclause = where.toString();
    481 
    482 
    483         if (async != null) {
    484             async.startQuery(0, null, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
    485                     mCols, whereclause, keywords, MediaStore.Audio.Playlists.NAME);
    486             return null;
    487         }
    488         Cursor c = null;
    489         c = MusicUtils.query(this, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
    490                 mCols, whereclause, keywords, MediaStore.Audio.Playlists.NAME);
    491 
    492         return mergedCursor(c);
    493     }
    494 
    495     private Cursor mergedCursor(Cursor c) {
    496         if (c == null) {
    497             return null;
    498         }
    499         if (c instanceof MergeCursor) {
    500             // this shouldn't happen, but fail gracefully
    501             Log.d("PlaylistBrowserActivity", "Already wrapped");
    502             return c;
    503         }
    504         MatrixCursor autoplaylistscursor = new MatrixCursor(mCols);
    505         if (mCreateShortcut) {
    506             ArrayList<Object> all = new ArrayList<Object>(2);
    507             all.add(ALL_SONGS_PLAYLIST);
    508             all.add(getString(R.string.play_all));
    509             autoplaylistscursor.addRow(all);
    510         }
    511         ArrayList<Object> recent = new ArrayList<Object>(2);
    512         recent.add(RECENTLY_ADDED_PLAYLIST);
    513         recent.add(getString(R.string.recentlyadded));
    514         autoplaylistscursor.addRow(recent);
    515 
    516         // check if there are any podcasts
    517         Cursor counter = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
    518                 new String[] {"count(*)"}, "is_podcast=1", null, null);
    519         if (counter != null) {
    520             counter.moveToFirst();
    521             int numpodcasts = counter.getInt(0);
    522             counter.close();
    523             if (numpodcasts > 0) {
    524                 ArrayList<Object> podcasts = new ArrayList<Object>(2);
    525                 podcasts.add(PODCASTS_PLAYLIST);
    526                 podcasts.add(getString(R.string.podcasts_listitem));
    527                 autoplaylistscursor.addRow(podcasts);
    528             }
    529         }
    530 
    531         Cursor cc = new MergeCursor(new Cursor [] {autoplaylistscursor, c});
    532         return cc;
    533     }
    534 
    535     static class PlaylistListAdapter extends SimpleCursorAdapter {
    536         int mTitleIdx;
    537         int mIdIdx;
    538         private PlaylistBrowserActivity mActivity = null;
    539         private AsyncQueryHandler mQueryHandler;
    540         private String mConstraint = null;
    541         private boolean mConstraintIsValid = false;
    542 
    543         class QueryHandler extends AsyncQueryHandler {
    544             QueryHandler(ContentResolver res) {
    545                 super(res);
    546             }
    547 
    548             @Override
    549             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    550                 //Log.i("@@@", "query complete: " + cursor.getCount() + "   " + mActivity);
    551                 if (cursor != null) {
    552                     cursor = mActivity.mergedCursor(cursor);
    553                 }
    554                 mActivity.init(cursor);
    555             }
    556         }
    557 
    558         PlaylistListAdapter(Context context, PlaylistBrowserActivity currentactivity,
    559                 int layout, Cursor cursor, String[] from, int[] to) {
    560             super(context, layout, cursor, from, to);
    561             mActivity = currentactivity;
    562             getColumnIndices(cursor);
    563             mQueryHandler = new QueryHandler(context.getContentResolver());
    564         }
    565         private void getColumnIndices(Cursor cursor) {
    566             if (cursor != null) {
    567                 mTitleIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME);
    568                 mIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID);
    569             }
    570         }
    571 
    572         public void setActivity(PlaylistBrowserActivity newactivity) {
    573             mActivity = newactivity;
    574         }
    575 
    576         public AsyncQueryHandler getQueryHandler() {
    577             return mQueryHandler;
    578         }
    579 
    580         @Override
    581         public void bindView(View view, Context context, Cursor cursor) {
    582 
    583             TextView tv = (TextView) view.findViewById(R.id.line1);
    584 
    585             String name = cursor.getString(mTitleIdx);
    586             tv.setText(name);
    587 
    588             long id = cursor.getLong(mIdIdx);
    589 
    590             ImageView iv = (ImageView) view.findViewById(R.id.icon);
    591             if (id == RECENTLY_ADDED_PLAYLIST) {
    592                 iv.setImageResource(R.drawable.ic_mp_playlist_recently_added_list);
    593             } else {
    594                 iv.setImageResource(R.drawable.ic_mp_playlist_list);
    595             }
    596             ViewGroup.LayoutParams p = iv.getLayoutParams();
    597             p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
    598             p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
    599 
    600             iv = (ImageView) view.findViewById(R.id.play_indicator);
    601             iv.setVisibility(View.GONE);
    602 
    603             view.findViewById(R.id.line2).setVisibility(View.GONE);
    604         }
    605 
    606         @Override
    607         public void changeCursor(Cursor cursor) {
    608             if (mActivity.isFinishing() && cursor != null) {
    609                 cursor.close();
    610                 cursor = null;
    611             }
    612             if (cursor != mActivity.mPlaylistCursor) {
    613                 mActivity.mPlaylistCursor = cursor;
    614                 super.changeCursor(cursor);
    615                 getColumnIndices(cursor);
    616             }
    617         }
    618 
    619         @Override
    620         public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
    621             String s = constraint.toString();
    622             if (mConstraintIsValid && (
    623                     (s == null && mConstraint == null) ||
    624                     (s != null && s.equals(mConstraint)))) {
    625                 return getCursor();
    626             }
    627             Cursor c = mActivity.getPlaylistCursor(null, s);
    628             mConstraint = s;
    629             mConstraintIsValid = true;
    630             return c;
    631         }
    632     }
    633 
    634     private Cursor mPlaylistCursor;
    635 }
    636 
    637