blob: aa686fe636d9aa6d41f32ccf7c826667ef8e65cb [file] [log] [blame]
The Android Open Source Project792a2202009-03-03 19:32:30 -08001/*
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
17package com.android.music;
18
Marco Nelissenf33a5752010-01-27 15:35:43 -080019import com.android.music.MusicUtils.ServiceToken;
20
The Android Open Source Project792a2202009-03-03 19:32:30 -080021import android.app.ListActivity;
22import android.content.AsyncQueryHandler;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.ContentResolver;
26import android.content.ContentUris;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.ServiceConnection;
The Android Open Source Project792a2202009-03-03 19:32:30 -080031import android.database.Cursor;
Marco Nelissen9825b3c2010-01-04 16:55:00 -080032import android.database.MatrixCursor;
The Android Open Source Project792a2202009-03-03 19:32:30 -080033import android.database.MergeCursor;
34import android.database.sqlite.SQLiteException;
35import android.media.AudioManager;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Message;
41import android.provider.MediaStore;
42import android.util.Log;
43import android.view.ContextMenu;
44import android.view.Menu;
45import android.view.MenuItem;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.Window;
49import android.view.ContextMenu.ContextMenuInfo;
50import android.widget.ImageView;
51import android.widget.ListView;
52import android.widget.SimpleCursorAdapter;
53import android.widget.TextView;
54import android.widget.Toast;
55import android.widget.AdapterView.AdapterContextMenuInfo;
56
Marco Nelissen9825b3c2010-01-04 16:55:00 -080057import java.text.Collator;
58import java.util.ArrayList;
59
Jack Heb0fba8b2017-01-26 15:54:38 -080060public class PlaylistBrowserActivity
61 extends ListActivity implements View.OnCreateContextMenuListener, MusicUtils.Defs {
The Android Open Source Project792a2202009-03-03 19:32:30 -080062 private static final String TAG = "PlaylistBrowserActivity";
63 private static final int DELETE_PLAYLIST = CHILD_MENU_BASE + 1;
64 private static final int EDIT_PLAYLIST = CHILD_MENU_BASE + 2;
65 private static final int RENAME_PLAYLIST = CHILD_MENU_BASE + 3;
66 private static final int CHANGE_WEEKS = CHILD_MENU_BASE + 4;
67 private static final long RECENTLY_ADDED_PLAYLIST = -1;
68 private static final long ALL_SONGS_PLAYLIST = -2;
69 private static final long PODCASTS_PLAYLIST = -3;
70 private PlaylistListAdapter mAdapter;
71 boolean mAdapterSent;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080072 private static int mLastListPosCourse = -1;
73 private static int mLastListPosFine = -1;
The Android Open Source Project792a2202009-03-03 19:32:30 -080074
75 private boolean mCreateShortcut;
Marco Nelissenf33a5752010-01-27 15:35:43 -080076 private ServiceToken mToken;
The Android Open Source Project792a2202009-03-03 19:32:30 -080077
Jack Heb0fba8b2017-01-26 15:54:38 -080078 public PlaylistBrowserActivity() {}
The Android Open Source Project792a2202009-03-03 19:32:30 -080079
80 /** Called when the activity is first created. */
81 @Override
Jack Heb0fba8b2017-01-26 15:54:38 -080082 public void onCreate(Bundle icicle) {
The Android Open Source Project792a2202009-03-03 19:32:30 -080083 super.onCreate(icicle);
84
85 final Intent intent = getIntent();
86 final String action = intent.getAction();
87 if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
88 mCreateShortcut = true;
89 }
90
91 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
Marco Nelissenec0c57a2009-12-12 12:27:11 -080092 requestWindowFeature(Window.FEATURE_NO_TITLE);
The Android Open Source Project792a2202009-03-03 19:32:30 -080093 setVolumeControlStream(AudioManager.STREAM_MUSIC);
Marco Nelissenf33a5752010-01-27 15:35:43 -080094 mToken = MusicUtils.bindToService(this, new ServiceConnection() {
The Android Open Source Project792a2202009-03-03 19:32:30 -080095 public void onServiceConnected(ComponentName classname, IBinder obj) {
96 if (Intent.ACTION_VIEW.equals(action)) {
Xiaoyu Ruana6ee6de2010-09-15 13:08:02 +020097 Bundle b = intent.getExtras();
98 if (b == null) {
99 Log.w(TAG, "Unexpected:getExtras() returns null.");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800100 } else {
Xiaoyu Ruana6ee6de2010-09-15 13:08:02 +0200101 try {
102 long id = Long.parseLong(b.getString("playlist"));
103 if (id == RECENTLY_ADDED_PLAYLIST) {
104 playRecentlyAdded();
105 } else if (id == PODCASTS_PLAYLIST) {
106 playPodcasts();
107 } else if (id == ALL_SONGS_PLAYLIST) {
108 long[] list = MusicUtils.getAllSongs(PlaylistBrowserActivity.this);
109 if (list != null) {
110 MusicUtils.playAll(PlaylistBrowserActivity.this, list, 0);
111 }
112 } else {
113 MusicUtils.playPlaylist(PlaylistBrowserActivity.this, id);
114 }
115 } catch (NumberFormatException e) {
116 Log.w(TAG, "Playlist id missing or broken");
117 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800118 }
119 finish();
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800120 return;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800121 }
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800122 MusicUtils.updateNowPlaying(PlaylistBrowserActivity.this);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800123 }
124
Jack Heb0fba8b2017-01-26 15:54:38 -0800125 public void onServiceDisconnected(ComponentName classname) {}
126
The Android Open Source Project792a2202009-03-03 19:32:30 -0800127 });
128 IntentFilter f = new IntentFilter();
129 f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
130 f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
131 f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
132 f.addDataScheme("file");
133 registerReceiver(mScanListener, f);
134
135 setContentView(R.layout.media_picker_activity);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800136 MusicUtils.updateButtonBar(this, R.id.playlisttab);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800137 ListView lv = getListView();
138 lv.setOnCreateContextMenuListener(this);
139 lv.setTextFilterEnabled(true);
140
141 mAdapter = (PlaylistListAdapter) getLastNonConfigurationInstance();
142 if (mAdapter == null) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800143 // Log.i("@@@", "starting query");
144 mAdapter = new PlaylistListAdapter(getApplication(), this, R.layout.track_list_item,
145 mPlaylistCursor, new String[] {MediaStore.Audio.Playlists.NAME},
146 new int[] {android.R.id.text1});
The Android Open Source Project792a2202009-03-03 19:32:30 -0800147 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 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800168
The Android Open Source Project792a2202009-03-03 19:32:30 -0800169 @Override
170 public Object onRetainNonConfigurationInstance() {
171 PlaylistListAdapter a = mAdapter;
172 mAdapterSent = true;
173 return a;
174 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800175
The Android Open Source Project792a2202009-03-03 19:32:30 -0800176 @Override
177 public void onDestroy() {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800178 ListView lv = getListView();
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800179 if (lv != null) {
180 mLastListPosCourse = lv.getFirstVisiblePosition();
Marco Nelissen23b531e2009-12-12 14:07:59 -0800181 View cv = lv.getChildAt(0);
182 if (cv != null) {
183 mLastListPosFine = cv.getTop();
184 }
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800185 }
Marco Nelissenf33a5752010-01-27 15:35:43 -0800186 MusicUtils.unbindFromService(mToken);
Marco Nelissen0e175782009-12-03 14:24:53 -0800187 // 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.
Marco Nelissen8e732ff2009-10-12 12:29:09 -0700191 if (!mAdapterSent && mAdapter != null) {
Marco Nelissen0e175782009-12-03 14:24:53 -0800192 mAdapter.changeCursor(null);
193 }
Marco Nelissen0164ebf2009-08-13 09:58:43 -0700194 // 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;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800199 unregisterReceiver(mScanListener);
200 super.onDestroy();
201 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800202
The Android Open Source Project792a2202009-03-03 19:32:30 -0800203 @Override
204 public void onResume() {
205 super.onResume();
206
207 MusicUtils.setSpinnerState(this);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800208 MusicUtils.updateNowPlaying(PlaylistBrowserActivity.this);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800209 }
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 };
Jack Heb0fba8b2017-01-26 15:54:38 -0800222
The Android Open Source Project792a2202009-03-03 19:32:30 -0800223 private Handler mReScanHandler = new Handler() {
Marco Nelissen42bcc212009-09-01 13:22:19 -0700224 @Override
The Android Open Source Project792a2202009-03-03 19:32:30 -0800225 public void handleMessage(Message msg) {
Marco Nelissen42bcc212009-09-01 13:22:19 -0700226 if (mAdapter != null) {
227 getPlaylistCursor(mAdapter.getQueryHandler(), null);
228 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800229 }
230 };
231 public void init(Cursor cursor) {
Marco Nelissen42bcc212009-09-01 13:22:19 -0700232 if (mAdapter == null) {
233 return;
234 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800235 mAdapter.changeCursor(cursor);
236
237 if (mPlaylistCursor == null) {
238 MusicUtils.displayDatabaseError(this);
239 closeContextMenu();
240 mReScanHandler.sendEmptyMessageDelayed(0, 1000);
241 return;
242 }
243
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800244 // restore previous position
245 if (mLastListPosCourse >= 0) {
246 getListView().setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
247 mLastListPosCourse = -1;
248 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800249 MusicUtils.hideDatabaseError(this);
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800250 MusicUtils.updateButtonBar(this, R.id.playlisttab);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800251 setTitle();
252 }
253
254 private void setTitle() {
255 setTitle(R.string.playlists_title);
256 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800257
The Android Open Source Project792a2202009-03-03 19:32:30 -0800258 @Override
259 public boolean onCreateOptionsMenu(Menu menu) {
260 if (!mCreateShortcut) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800261 menu.add(0, PARTY_SHUFFLE, 0,
262 R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
The Android Open Source Project792a2202009-03-03 19:32:30 -0800263 }
264 return super.onCreateOptionsMenu(menu);
265 }
266
267 @Override
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800268 public boolean onPrepareOptionsMenu(Menu menu) {
269 MusicUtils.setPartyShuffleMenuIcon(menu);
270 return super.onPrepareOptionsMenu(menu);
271 }
272
273 @Override
The Android Open Source Project792a2202009-03-03 19:32:30 -0800274 public boolean onOptionsItemSelected(MenuItem item) {
275 Intent intent;
276 switch (item.getItemId()) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800277 case PARTY_SHUFFLE:
278 MusicUtils.togglePartyShuffle();
279 break;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800280 }
281 return super.onOptionsItemSelected(item);
282 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800283
The Android Open Source Project792a2202009-03-03 19:32:30 -0800284 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);
Jack Heb0fba8b2017-01-26 15:54:38 -0800306 menu.setHeaderTitle(mPlaylistCursor.getString(
307 mPlaylistCursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME)));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800308 }
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();
Marco Nelissen42bcc212009-09-01 13:22:19 -0700358 } else if (mAdapter != null) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800359 getPlaylistCursor(mAdapter.getQueryHandler(), null);
360 }
361 break;
362 }
363 }
364
365 @Override
Jack Heb0fba8b2017-01-26 15:54:38 -0800366 protected void onListItemClick(ListView l, View v, int position, long id) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800367 if (mCreateShortcut) {
368 final Intent shortcut = new Intent();
369 shortcut.setAction(Intent.ACTION_VIEW);
370 shortcut.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/playlist");
371 shortcut.putExtra("playlist", String.valueOf(id));
372
373 final Intent intent = new Intent();
374 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut);
Jack Heb0fba8b2017-01-26 15:54:38 -0800375 intent.putExtra(
376 Intent.EXTRA_SHORTCUT_NAME, ((TextView) v.findViewById(R.id.line1)).getText());
377 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
378 Intent.ShortcutIconResource.fromContext(
379 this, R.drawable.ic_launcher_shortcut_music_playlist));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800380
381 setResult(RESULT_OK, intent);
382 finish();
383 return;
384 }
385 if (id == RECENTLY_ADDED_PLAYLIST) {
386 Intent intent = new Intent(Intent.ACTION_PICK);
387 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
388 intent.putExtra("playlist", "recentlyadded");
389 startActivity(intent);
390 } else if (id == PODCASTS_PLAYLIST) {
391 Intent intent = new Intent(Intent.ACTION_PICK);
392 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
393 intent.putExtra("playlist", "podcasts");
394 startActivity(intent);
395 } else {
396 Intent intent = new Intent(Intent.ACTION_EDIT);
397 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
398 intent.putExtra("playlist", Long.valueOf(id).toString());
399 startActivity(intent);
400 }
401 }
402
403 private void playRecentlyAdded() {
404 // do a query for all songs added in the last X weeks
405 int X = MusicUtils.getIntPref(this, "numweeks", 2) * (3600 * 24 * 7);
Jack Heb0fba8b2017-01-26 15:54:38 -0800406 final String[] ccols = new String[] {MediaStore.Audio.Media._ID};
407 String where =
408 MediaStore.MediaColumns.DATE_ADDED + ">" + (System.currentTimeMillis() / 1000 - X);
409 Cursor cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ccols,
410 where, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
411
The Android Open Source Project792a2202009-03-03 19:32:30 -0800412 if (cursor == null) {
413 // Todo: show a message
414 return;
415 }
416 try {
417 int len = cursor.getCount();
Jack Heb0fba8b2017-01-26 15:54:38 -0800418 long[] list = new long[len];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800419 for (int i = 0; i < len; i++) {
420 cursor.moveToNext();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700421 list[i] = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800422 }
423 MusicUtils.playAll(this, list, 0);
424 } catch (SQLiteException ex) {
425 } finally {
426 cursor.close();
427 }
428 }
429
430 private void playPodcasts() {
431 // do a query for all files that are podcasts
Jack Heb0fba8b2017-01-26 15:54:38 -0800432 final String[] ccols = new String[] {MediaStore.Audio.Media._ID};
433 Cursor cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ccols,
434 MediaStore.Audio.Media.IS_PODCAST + "=1", null,
435 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
436
The Android Open Source Project792a2202009-03-03 19:32:30 -0800437 if (cursor == null) {
438 // Todo: show a message
439 return;
440 }
441 try {
442 int len = cursor.getCount();
Jack Heb0fba8b2017-01-26 15:54:38 -0800443 long[] list = new long[len];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800444 for (int i = 0; i < len; i++) {
445 cursor.moveToNext();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700446 list[i] = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800447 }
448 MusicUtils.playAll(this, list, 0);
449 } catch (SQLiteException ex) {
450 } finally {
451 cursor.close();
452 }
453 }
454
Jack Heb0fba8b2017-01-26 15:54:38 -0800455 String[] mCols = new String[] {MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME};
The Android Open Source Project792a2202009-03-03 19:32:30 -0800456
457 private Cursor getPlaylistCursor(AsyncQueryHandler async, String filterstring) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800458 StringBuilder where = new StringBuilder();
459 where.append(MediaStore.Audio.Playlists.NAME + " != ''");
Jack Heb0fba8b2017-01-26 15:54:38 -0800460
The Android Open Source Project792a2202009-03-03 19:32:30 -0800461 // Add in the filtering constraints
Jack Heb0fba8b2017-01-26 15:54:38 -0800462 String[] keywords = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800463 if (filterstring != null) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800464 String[] searchWords = filterstring.split(" ");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800465 keywords = new String[searchWords.length];
466 Collator col = Collator.getInstance();
467 col.setStrength(Collator.PRIMARY);
468 for (int i = 0; i < searchWords.length; i++) {
469 keywords[i] = '%' + searchWords[i] + '%';
470 }
471 for (int i = 0; i < searchWords.length; i++) {
472 where.append(" AND ");
473 where.append(MediaStore.Audio.Playlists.NAME + " LIKE ?");
474 }
475 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800476
The Android Open Source Project792a2202009-03-03 19:32:30 -0800477 String whereclause = where.toString();
Jack Heb0fba8b2017-01-26 15:54:38 -0800478
The Android Open Source Project792a2202009-03-03 19:32:30 -0800479 if (async != null) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800480 async.startQuery(0, null, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, mCols,
481 whereclause, keywords, MediaStore.Audio.Playlists.NAME);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800482 return null;
483 }
484 Cursor c = null;
Jack Heb0fba8b2017-01-26 15:54:38 -0800485 c = MusicUtils.query(this, MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, mCols,
486 whereclause, keywords, MediaStore.Audio.Playlists.NAME);
487
The Android Open Source Project792a2202009-03-03 19:32:30 -0800488 return mergedCursor(c);
489 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800490
The Android Open Source Project792a2202009-03-03 19:32:30 -0800491 private Cursor mergedCursor(Cursor c) {
492 if (c == null) {
493 return null;
494 }
495 if (c instanceof MergeCursor) {
496 // this shouldn't happen, but fail gracefully
497 Log.d("PlaylistBrowserActivity", "Already wrapped");
498 return c;
499 }
Marco Nelissen9825b3c2010-01-04 16:55:00 -0800500 MatrixCursor autoplaylistscursor = new MatrixCursor(mCols);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800501 if (mCreateShortcut) {
502 ArrayList<Object> all = new ArrayList<Object>(2);
503 all.add(ALL_SONGS_PLAYLIST);
504 all.add(getString(R.string.play_all));
Marco Nelissen9825b3c2010-01-04 16:55:00 -0800505 autoplaylistscursor.addRow(all);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800506 }
507 ArrayList<Object> recent = new ArrayList<Object>(2);
508 recent.add(RECENTLY_ADDED_PLAYLIST);
509 recent.add(getString(R.string.recentlyadded));
Marco Nelissen9825b3c2010-01-04 16:55:00 -0800510 autoplaylistscursor.addRow(recent);
Jack Heb0fba8b2017-01-26 15:54:38 -0800511
The Android Open Source Project792a2202009-03-03 19:32:30 -0800512 // check if there are any podcasts
513 Cursor counter = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
514 new String[] {"count(*)"}, "is_podcast=1", null, null);
515 if (counter != null) {
516 counter.moveToFirst();
517 int numpodcasts = counter.getInt(0);
518 counter.close();
519 if (numpodcasts > 0) {
520 ArrayList<Object> podcasts = new ArrayList<Object>(2);
521 podcasts.add(PODCASTS_PLAYLIST);
522 podcasts.add(getString(R.string.podcasts_listitem));
Marco Nelissen9825b3c2010-01-04 16:55:00 -0800523 autoplaylistscursor.addRow(podcasts);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800524 }
525 }
526
Jack Heb0fba8b2017-01-26 15:54:38 -0800527 Cursor cc = new MergeCursor(new Cursor[] {autoplaylistscursor, c});
The Android Open Source Project792a2202009-03-03 19:32:30 -0800528 return cc;
529 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800530
The Android Open Source Project792a2202009-03-03 19:32:30 -0800531 static class PlaylistListAdapter extends SimpleCursorAdapter {
532 int mTitleIdx;
533 int mIdIdx;
534 private PlaylistBrowserActivity mActivity = null;
535 private AsyncQueryHandler mQueryHandler;
536 private String mConstraint = null;
537 private boolean mConstraintIsValid = false;
538
539 class QueryHandler extends AsyncQueryHandler {
540 QueryHandler(ContentResolver res) {
541 super(res);
542 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800543
The Android Open Source Project792a2202009-03-03 19:32:30 -0800544 @Override
545 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800546 // Log.i("@@@", "query complete: " + cursor.getCount() + " " + mActivity);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800547 if (cursor != null) {
548 cursor = mActivity.mergedCursor(cursor);
549 }
550 mActivity.init(cursor);
551 }
552 }
553
Jack Heb0fba8b2017-01-26 15:54:38 -0800554 PlaylistListAdapter(Context context, PlaylistBrowserActivity currentactivity, int layout,
555 Cursor cursor, String[] from, int[] to) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800556 super(context, layout, cursor, from, to);
557 mActivity = currentactivity;
558 getColumnIndices(cursor);
559 mQueryHandler = new QueryHandler(context.getContentResolver());
560 }
561 private void getColumnIndices(Cursor cursor) {
562 if (cursor != null) {
563 mTitleIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME);
564 mIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID);
565 }
566 }
567
568 public void setActivity(PlaylistBrowserActivity newactivity) {
569 mActivity = newactivity;
570 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800571
The Android Open Source Project792a2202009-03-03 19:32:30 -0800572 public AsyncQueryHandler getQueryHandler() {
573 return mQueryHandler;
574 }
575
576 @Override
577 public void bindView(View view, Context context, Cursor cursor) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800578 TextView tv = (TextView) view.findViewById(R.id.line1);
Jack Heb0fba8b2017-01-26 15:54:38 -0800579
The Android Open Source Project792a2202009-03-03 19:32:30 -0800580 String name = cursor.getString(mTitleIdx);
581 tv.setText(name);
Jack Heb0fba8b2017-01-26 15:54:38 -0800582
The Android Open Source Project792a2202009-03-03 19:32:30 -0800583 long id = cursor.getLong(mIdIdx);
Jack Heb0fba8b2017-01-26 15:54:38 -0800584
The Android Open Source Project792a2202009-03-03 19:32:30 -0800585 ImageView iv = (ImageView) view.findViewById(R.id.icon);
586 if (id == RECENTLY_ADDED_PLAYLIST) {
587 iv.setImageResource(R.drawable.ic_mp_playlist_recently_added_list);
588 } else {
589 iv.setImageResource(R.drawable.ic_mp_playlist_list);
590 }
591 ViewGroup.LayoutParams p = iv.getLayoutParams();
592 p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
593 p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
594
595 iv = (ImageView) view.findViewById(R.id.play_indicator);
596 iv.setVisibility(View.GONE);
597
598 view.findViewById(R.id.line2).setVisibility(View.GONE);
599 }
600
601 @Override
602 public void changeCursor(Cursor cursor) {
Marco Nelissend99bc1e2009-12-04 08:48:06 -0800603 if (mActivity.isFinishing() && cursor != null) {
604 cursor.close();
605 cursor = null;
Marco Nelissen0e175782009-12-03 14:24:53 -0800606 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800607 if (cursor != mActivity.mPlaylistCursor) {
608 mActivity.mPlaylistCursor = cursor;
609 super.changeCursor(cursor);
610 getColumnIndices(cursor);
611 }
612 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800613
The Android Open Source Project792a2202009-03-03 19:32:30 -0800614 @Override
615 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
616 String s = constraint.toString();
Jack Heb0fba8b2017-01-26 15:54:38 -0800617 if (mConstraintIsValid && ((s == null && mConstraint == null)
618 || (s != null && s.equals(mConstraint)))) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800619 return getCursor();
620 }
621 Cursor c = mActivity.getPlaylistCursor(null, s);
622 mConstraint = s;
623 mConstraintIsValid = true;
624 return c;
625 }
626 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800627
The Android Open Source Project792a2202009-03-03 19:32:30 -0800628 private Cursor mPlaylistCursor;
629}