blob: 13a2387d586f4699f26185a4d81dd80ea0891cea [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
19import com.android.music.QueryBrowserActivity.QueryListAdapter.QueryHandler;
20
21import android.app.ExpandableListActivity;
22import android.app.SearchManager;
23import android.content.AsyncQueryHandler;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.res.Resources;
30import android.database.Cursor;
31import android.database.CursorWrapper;
32import android.graphics.drawable.BitmapDrawable;
33import android.graphics.drawable.Drawable;
34import android.media.AudioManager;
35import android.media.MediaFile;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Message;
40import android.provider.MediaStore;
41import android.util.Log;
42import android.view.ContextMenu;
43import android.view.Menu;
44import android.view.MenuItem;
45import android.view.SubMenu;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.Window;
49import android.view.ContextMenu.ContextMenuInfo;
50import android.widget.CursorAdapter;
51import android.widget.CursorTreeAdapter;
52import android.widget.ExpandableListAdapter;
53import android.widget.ExpandableListView;
54import android.widget.ImageView;
55import android.widget.SectionIndexer;
56import android.widget.SimpleCursorTreeAdapter;
57import android.widget.TextView;
58import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
59
60import java.text.Collator;
61
62
63public class ArtistAlbumBrowserActivity extends ExpandableListActivity
64 implements View.OnCreateContextMenuListener, MusicUtils.Defs
65{
66 private String mCurrentArtistId;
67 private String mCurrentArtistName;
68 private String mCurrentAlbumId;
69 private String mCurrentAlbumName;
70 private String mCurrentArtistNameForAlbum;
Marco Nelissen9882f542009-08-14 10:06:15 -070071 boolean mIsUnknownArtist;
72 boolean mIsUnknownAlbum;
The Android Open Source Project792a2202009-03-03 19:32:30 -080073 private ArtistAlbumListAdapter mAdapter;
74 private boolean mAdapterSent;
75 private final static int SEARCH = CHILD_MENU_BASE;
76
77 public ArtistAlbumBrowserActivity()
78 {
79 }
80
81 /** Called when the activity is first created. */
82 @Override
83 public void onCreate(Bundle icicle) {
84 super.onCreate(icicle);
85 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
86 setVolumeControlStream(AudioManager.STREAM_MUSIC);
87 if (icicle != null) {
88 mCurrentAlbumId = icicle.getString("selectedalbum");
89 mCurrentAlbumName = icicle.getString("selectedalbumname");
90 mCurrentArtistId = icicle.getString("selectedartist");
91 mCurrentArtistName = icicle.getString("selectedartistname");
92 }
93 MusicUtils.bindToService(this);
94
95 IntentFilter f = new IntentFilter();
96 f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
97 f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
98 f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
99 f.addDataScheme("file");
100 registerReceiver(mScanListener, f);
101
102 setContentView(R.layout.media_picker_activity_expanding);
103 ExpandableListView lv = getExpandableListView();
104 lv.setFastScrollEnabled(true);
105 lv.setOnCreateContextMenuListener(this);
106 lv.setTextFilterEnabled(true);
107
108 mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance();
109 if (mAdapter == null) {
110 //Log.i("@@@", "starting query");
111 mAdapter = new ArtistAlbumListAdapter(
112 getApplication(),
113 this,
114 null, // cursor
115 R.layout.track_list_item_group,
116 new String[] {},
117 new int[] {},
118 R.layout.track_list_item_child,
119 new String[] {},
120 new int[] {});
121 setListAdapter(mAdapter);
122 setTitle(R.string.working_artists);
123 getArtistCursor(mAdapter.getQueryHandler(), null);
124 } else {
125 mAdapter.setActivity(this);
126 setListAdapter(mAdapter);
127 mArtistCursor = mAdapter.getCursor();
128 if (mArtistCursor != null) {
129 init(mArtistCursor);
130 } else {
131 getArtistCursor(mAdapter.getQueryHandler(), null);
132 }
133 }
134 }
135
136 @Override
137 public Object onRetainNonConfigurationInstance() {
138 mAdapterSent = true;
139 return mAdapter;
140 }
141
142 @Override
143 public void onSaveInstanceState(Bundle outcicle) {
144 // need to store the selected item so we don't lose it in case
145 // of an orientation switch. Otherwise we could lose it while
146 // in the middle of specifying a playlist to add the item to.
147 outcicle.putString("selectedalbum", mCurrentAlbumId);
148 outcicle.putString("selectedalbumname", mCurrentAlbumName);
149 outcicle.putString("selectedartist", mCurrentArtistId);
150 outcicle.putString("selectedartistname", mCurrentArtistName);
151 super.onSaveInstanceState(outcicle);
152 }
153
154 @Override
155 public void onDestroy() {
156 MusicUtils.unbindFromService(this);
157 if (!mAdapterSent) {
158 Cursor c = mAdapter.getCursor();
159 if (c != null) {
160 c.close();
161 }
162 }
Marco Nelissen0164ebf2009-08-13 09:58:43 -0700163 // Because we pass the adapter to the next activity, we need to make
164 // sure it doesn't keep a reference to this activity. We can do this
165 // by clearing its DatasetObservers, which setListAdapter(null) does.
166 setListAdapter(null);
167 mAdapter = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800168 unregisterReceiver(mScanListener);
Marco Nelissen9882f542009-08-14 10:06:15 -0700169 setListAdapter(null);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800170 super.onDestroy();
171 }
172
173 @Override
174 public void onResume() {
175 super.onResume();
176 IntentFilter f = new IntentFilter();
177 f.addAction(MediaPlaybackService.META_CHANGED);
178 f.addAction(MediaPlaybackService.QUEUE_CHANGED);
179 registerReceiver(mTrackListListener, f);
180 mTrackListListener.onReceive(null, null);
181
182 MusicUtils.setSpinnerState(this);
183 }
184
185 private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
186 @Override
187 public void onReceive(Context context, Intent intent) {
188 getExpandableListView().invalidateViews();
189 }
190 };
191 private BroadcastReceiver mScanListener = new BroadcastReceiver() {
192 @Override
193 public void onReceive(Context context, Intent intent) {
194 MusicUtils.setSpinnerState(ArtistAlbumBrowserActivity.this);
195 mReScanHandler.sendEmptyMessage(0);
196 if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
197 MusicUtils.clearAlbumArtCache();
198 }
199 }
200 };
201
202 private Handler mReScanHandler = new Handler() {
203 @Override
204 public void handleMessage(Message msg) {
205 getArtistCursor(mAdapter.getQueryHandler(), null);
206 }
207 };
208
209 @Override
210 public void onPause() {
211 unregisterReceiver(mTrackListListener);
212 mReScanHandler.removeCallbacksAndMessages(null);
213 super.onPause();
214 }
215
216 public void init(Cursor c) {
217
218 mAdapter.changeCursor(c); // also sets mArtistCursor
219
220 if (mArtistCursor == null) {
221 MusicUtils.displayDatabaseError(this);
222 closeContextMenu();
223 mReScanHandler.sendEmptyMessageDelayed(0, 1000);
224 return;
225 }
226
227 MusicUtils.hideDatabaseError(this);
228 setTitle();
229 }
230
231 private void setTitle() {
232 setTitle(R.string.artists_title);
233 }
234
235 @Override
236 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
237
238 mCurrentAlbumId = Long.valueOf(id).toString();
239
240 Intent intent = new Intent(Intent.ACTION_PICK);
241 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
242 intent.putExtra("album", mCurrentAlbumId);
243 Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition, childPosition);
244 String album = c.getString(c.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700245 if (album == null || album.equals(MediaFile.UNKNOWN_STRING)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800246 // unknown album, so we should include the artist ID to limit the songs to songs only by that artist
247 mArtistCursor.moveToPosition(groupPosition);
248 mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndex(MediaStore.Audio.Artists._ID));
249 intent.putExtra("artist", mCurrentArtistId);
250 }
251 startActivity(intent);
252 return true;
253 }
254
255 @Override
256 public boolean onCreateOptionsMenu(Menu menu) {
257 super.onCreateOptionsMenu(menu);
258 menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(R.drawable.ic_menu_music_library);
259 menu.add(0, GOTO_PLAYBACK, 0, R.string.goto_playback).setIcon(R.drawable.ic_menu_playback);
260 menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle);
261 return true;
262 }
263
264 @Override
265 public boolean onPrepareOptionsMenu(Menu menu) {
266 menu.findItem(GOTO_PLAYBACK).setVisible(MusicUtils.isMusicLoaded());
267 return super.onPrepareOptionsMenu(menu);
268 }
269
270 @Override
271 public boolean onOptionsItemSelected(MenuItem item) {
272 Intent intent;
273 Cursor cursor;
274 switch (item.getItemId()) {
275 case GOTO_START:
276 intent = new Intent();
277 intent.setClass(this, MusicBrowserActivity.class);
278 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
279 startActivity(intent);
280 return true;
281
282 case GOTO_PLAYBACK:
283 intent = new Intent("com.android.music.PLAYBACK_VIEWER");
284 startActivity(intent);
285 return true;
286
287 case SHUFFLE_ALL:
288 cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
289 new String [] { MediaStore.Audio.Media._ID},
290 MediaStore.Audio.Media.IS_MUSIC + "=1", null,
291 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
292 if (cursor != null) {
293 MusicUtils.shuffleAll(this, cursor);
294 cursor.close();
295 }
296 return true;
297 }
298 return super.onOptionsItemSelected(item);
299 }
300
301 @Override
302 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
303 menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
304 SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist);
305 MusicUtils.makePlaylistMenu(this, sub);
306 menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800307
308 ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn;
309
310 int itemtype = ExpandableListView.getPackedPositionType(mi.packedPosition);
311 int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition);
312 int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition);
313 if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
314 if (gpos == -1) {
315 // this shouldn't happen
316 Log.d("Artist/Album", "no group");
317 return;
318 }
319 gpos = gpos - getExpandableListView().getHeaderViewsCount();
320 mArtistCursor.moveToPosition(gpos);
321 mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
322 mCurrentArtistName = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
323 mCurrentAlbumId = null;
Marco Nelissen9882f542009-08-14 10:06:15 -0700324 mIsUnknownArtist = mCurrentArtistName == null ||
325 mCurrentArtistName.equals(MediaFile.UNKNOWN_STRING);
326 mIsUnknownAlbum = true;
327 if (mIsUnknownArtist) {
Eric Fischerc8aa8372009-06-17 16:34:12 -0700328 menu.setHeaderTitle(getString(R.string.unknown_artist_name));
329 } else {
330 menu.setHeaderTitle(mCurrentArtistName);
Marco Nelissen9882f542009-08-14 10:06:15 -0700331 menu.add(0, SEARCH, 0, R.string.search_title);
Eric Fischerc8aa8372009-06-17 16:34:12 -0700332 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800333 return;
334 } else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
335 if (cpos == -1) {
336 // this shouldn't happen
337 Log.d("Artist/Album", "no child");
338 return;
339 }
340 Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos);
341 c.moveToPosition(cpos);
342 mCurrentArtistId = null;
343 mCurrentAlbumId = Long.valueOf(mi.id).toString();
344 mCurrentAlbumName = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
345 gpos = gpos - getExpandableListView().getHeaderViewsCount();
346 mArtistCursor.moveToPosition(gpos);
347 mCurrentArtistNameForAlbum = mArtistCursor.getString(
348 mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
Marco Nelissen9882f542009-08-14 10:06:15 -0700349 mIsUnknownArtist = mCurrentArtistNameForAlbum == null ||
350 mCurrentArtistNameForAlbum.equals(MediaFile.UNKNOWN_STRING);
351 mIsUnknownAlbum = mCurrentAlbumName == null ||
352 mCurrentAlbumName.equals(MediaFile.UNKNOWN_STRING);
353 if (mIsUnknownAlbum) {
Eric Fischerc8aa8372009-06-17 16:34:12 -0700354 menu.setHeaderTitle(getString(R.string.unknown_album_name));
355 } else {
356 menu.setHeaderTitle(mCurrentAlbumName);
357 }
Marco Nelissen9882f542009-08-14 10:06:15 -0700358 if (!mIsUnknownAlbum || !mIsUnknownArtist) {
359 menu.add(0, SEARCH, 0, R.string.search_title);
360 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800361 }
362 }
363
364 @Override
365 public boolean onContextItemSelected(MenuItem item) {
366 switch (item.getItemId()) {
367 case PLAY_SELECTION: {
368 // play everything by the selected artist
Marco Nelissenbd447b62009-06-29 14:52:05 -0700369 long [] list =
The Android Open Source Project792a2202009-03-03 19:32:30 -0800370 mCurrentArtistId != null ?
Marco Nelissenbd447b62009-06-29 14:52:05 -0700371 MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
372 : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800373
374 MusicUtils.playAll(this, list, 0);
375 return true;
376 }
377
378 case QUEUE: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700379 long [] list =
The Android Open Source Project792a2202009-03-03 19:32:30 -0800380 mCurrentArtistId != null ?
Marco Nelissenbd447b62009-06-29 14:52:05 -0700381 MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
382 : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800383 MusicUtils.addToCurrentPlaylist(this, list);
384 return true;
385 }
386
387 case NEW_PLAYLIST: {
388 Intent intent = new Intent();
389 intent.setClass(this, CreatePlaylist.class);
390 startActivityForResult(intent, NEW_PLAYLIST);
391 return true;
392 }
393
394 case PLAYLIST_SELECTED: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700395 long [] list =
The Android Open Source Project792a2202009-03-03 19:32:30 -0800396 mCurrentArtistId != null ?
Marco Nelissenbd447b62009-06-29 14:52:05 -0700397 MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
398 : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
399 long playlist = item.getIntent().getLongExtra("playlist", 0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800400 MusicUtils.addToPlaylist(this, list, playlist);
401 return true;
402 }
403
404 case DELETE_ITEM: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700405 long [] list;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800406 String desc;
407 if (mCurrentArtistId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700408 list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800409 String f = getString(R.string.delete_artist_desc);
410 desc = String.format(f, mCurrentArtistName);
411 } else {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700412 list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800413 String f = getString(R.string.delete_album_desc);
414 desc = String.format(f, mCurrentAlbumName);
415 }
416 Bundle b = new Bundle();
417 b.putString("description", desc);
Marco Nelissenbd447b62009-06-29 14:52:05 -0700418 b.putLongArray("items", list);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800419 Intent intent = new Intent();
420 intent.setClass(this, DeleteItems.class);
421 intent.putExtras(b);
422 startActivityForResult(intent, -1);
423 return true;
424 }
425
426 case SEARCH:
427 doSearch();
428 return true;
429 }
430 return super.onContextItemSelected(item);
431 }
432
433 void doSearch() {
434 CharSequence title = null;
435 String query = null;
436
437 Intent i = new Intent();
438 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
Marco Nelissen4341b502009-05-06 14:57:37 -0700439 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800440
441 if (mCurrentArtistId != null) {
442 title = mCurrentArtistName;
443 query = mCurrentArtistName;
444 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistName);
445 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE);
446 } else {
Marco Nelissen9882f542009-08-14 10:06:15 -0700447 if (mIsUnknownAlbum) {
448 title = query = mCurrentArtistNameForAlbum;
449 } else {
450 title = query = mCurrentAlbumName;
451 if (!mIsUnknownArtist) {
452 query = query + " " + mCurrentArtistNameForAlbum;
453 }
454 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800455 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum);
456 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
457 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE);
458 }
459 title = getString(R.string.mediasearch, title);
460 i.putExtra(SearchManager.QUERY, query);
461
462 startActivity(Intent.createChooser(i, title));
463 }
464
465 @Override
466 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
467 switch (requestCode) {
468 case SCAN_DONE:
469 if (resultCode == RESULT_CANCELED) {
470 finish();
471 } else {
472 getArtistCursor(mAdapter.getQueryHandler(), null);
473 }
474 break;
475
476 case NEW_PLAYLIST:
477 if (resultCode == RESULT_OK) {
478 Uri uri = intent.getData();
479 if (uri != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700480 long [] list = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800481 if (mCurrentArtistId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700482 list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800483 } else if (mCurrentAlbumId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700484 list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800485 }
Marco Nelissenbd447b62009-06-29 14:52:05 -0700486 MusicUtils.addToPlaylist(this, list, Long.parseLong(uri.getLastPathSegment()));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800487 }
488 }
489 break;
490 }
491 }
492
493 private Cursor getArtistCursor(AsyncQueryHandler async, String filter) {
494
495 StringBuilder where = new StringBuilder();
496 where.append(MediaStore.Audio.Artists.ARTIST + " != ''");
497
498 // Add in the filtering constraints
499 String [] keywords = null;
500 if (filter != null) {
501 String [] searchWords = filter.split(" ");
502 keywords = new String[searchWords.length];
503 Collator col = Collator.getInstance();
504 col.setStrength(Collator.PRIMARY);
505 for (int i = 0; i < searchWords.length; i++) {
506 keywords[i] = '%' + MediaStore.Audio.keyFor(searchWords[i]) + '%';
507 }
508 for (int i = 0; i < searchWords.length; i++) {
509 where.append(" AND ");
510 where.append(MediaStore.Audio.Media.ARTIST_KEY + " LIKE ?");
511 }
512 }
513
514 String whereclause = where.toString();
515 String[] cols = new String[] {
516 MediaStore.Audio.Artists._ID,
517 MediaStore.Audio.Artists.ARTIST,
518 MediaStore.Audio.Artists.NUMBER_OF_ALBUMS,
519 MediaStore.Audio.Artists.NUMBER_OF_TRACKS
520 };
521 Cursor ret = null;
522 if (async != null) {
523 async.startQuery(0, null, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
524 cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
525 } else {
526 ret = MusicUtils.query(this, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
527 cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
528 }
529 return ret;
530 }
531
532 static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter implements SectionIndexer {
533
534 private final Drawable mNowPlayingOverlay;
535 private final BitmapDrawable mDefaultAlbumIcon;
536 private int mGroupArtistIdIdx;
537 private int mGroupArtistIdx;
538 private int mGroupAlbumIdx;
539 private int mGroupSongIdx;
540 private final Context mContext;
541 private final Resources mResources;
542 private final String mAlbumSongSeparator;
543 private final String mUnknownAlbum;
544 private final String mUnknownArtist;
545 private final StringBuilder mBuffer = new StringBuilder();
546 private final Object[] mFormatArgs = new Object[1];
547 private final Object[] mFormatArgs3 = new Object[3];
548 private MusicAlphabetIndexer mIndexer;
549 private ArtistAlbumBrowserActivity mActivity;
550 private AsyncQueryHandler mQueryHandler;
551 private String mConstraint = null;
552 private boolean mConstraintIsValid = false;
553
Marco Nelissen756c3f52009-05-14 10:07:23 -0700554 static class ViewHolder {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800555 TextView line1;
556 TextView line2;
557 ImageView play_indicator;
558 ImageView icon;
559 }
560
561 class QueryHandler extends AsyncQueryHandler {
562 QueryHandler(ContentResolver res) {
563 super(res);
564 }
565
566 @Override
567 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
568 //Log.i("@@@", "query complete");
569 mActivity.init(cursor);
570 }
571 }
572
573 ArtistAlbumListAdapter(Context context, ArtistAlbumBrowserActivity currentactivity,
574 Cursor cursor, int glayout, String[] gfrom, int[] gto,
575 int clayout, String[] cfrom, int[] cto) {
576 super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto);
577 mActivity = currentactivity;
578 mQueryHandler = new QueryHandler(context.getContentResolver());
579
580 Resources r = context.getResources();
581 mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list);
582 mDefaultAlbumIcon = (BitmapDrawable) r.getDrawable(R.drawable.albumart_mp_unknown_list);
583 // no filter or dither, it's a lot faster and we can't tell the difference
584 mDefaultAlbumIcon.setFilterBitmap(false);
585 mDefaultAlbumIcon.setDither(false);
586
587 mContext = context;
588 getColumnIndices(cursor);
589 mResources = context.getResources();
590 mAlbumSongSeparator = context.getString(R.string.albumsongseparator);
591 mUnknownAlbum = context.getString(R.string.unknown_album_name);
592 mUnknownArtist = context.getString(R.string.unknown_artist_name);
593 }
594
595 private void getColumnIndices(Cursor cursor) {
596 if (cursor != null) {
597 mGroupArtistIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
598 mGroupArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
599 mGroupAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
600 mGroupSongIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
601 if (mIndexer != null) {
602 mIndexer.setCursor(cursor);
603 } else {
604 mIndexer = new MusicAlphabetIndexer(cursor, mGroupArtistIdx,
605 mResources.getString(com.android.internal.R.string.fast_scroll_alphabet));
606 }
607 }
608 }
609
610 public void setActivity(ArtistAlbumBrowserActivity newactivity) {
611 mActivity = newactivity;
612 }
613
614 public AsyncQueryHandler getQueryHandler() {
615 return mQueryHandler;
616 }
617
618 @Override
619 public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
620 View v = super.newGroupView(context, cursor, isExpanded, parent);
621 ImageView iv = (ImageView) v.findViewById(R.id.icon);
622 ViewGroup.LayoutParams p = iv.getLayoutParams();
623 p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
624 p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
625 ViewHolder vh = new ViewHolder();
626 vh.line1 = (TextView) v.findViewById(R.id.line1);
627 vh.line2 = (TextView) v.findViewById(R.id.line2);
628 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
629 vh.icon = (ImageView) v.findViewById(R.id.icon);
630 vh.icon.setPadding(0, 0, 1, 0);
631 v.setTag(vh);
632 return v;
633 }
634
635 @Override
636 public View newChildView(Context context, Cursor cursor, boolean isLastChild,
637 ViewGroup parent) {
638 View v = super.newChildView(context, cursor, isLastChild, parent);
639 ViewHolder vh = new ViewHolder();
640 vh.line1 = (TextView) v.findViewById(R.id.line1);
641 vh.line2 = (TextView) v.findViewById(R.id.line2);
642 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
643 vh.icon = (ImageView) v.findViewById(R.id.icon);
644 vh.icon.setBackgroundDrawable(mDefaultAlbumIcon);
645 vh.icon.setPadding(0, 0, 1, 0);
646 v.setTag(vh);
647 return v;
648 }
649
650 @Override
651 public void bindGroupView(View view, Context context, Cursor cursor, boolean isexpanded) {
652
653 ViewHolder vh = (ViewHolder) view.getTag();
654
655 String artist = cursor.getString(mGroupArtistIdx);
656 String displayartist = artist;
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700657 boolean unknown = artist == null || artist.equals(MediaFile.UNKNOWN_STRING);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800658 if (unknown) {
659 displayartist = mUnknownArtist;
660 }
661 vh.line1.setText(displayartist);
662
663 int numalbums = cursor.getInt(mGroupAlbumIdx);
664 int numsongs = cursor.getInt(mGroupSongIdx);
665
666 String songs_albums = MusicUtils.makeAlbumsLabel(context,
667 numalbums, numsongs, unknown);
668
669 vh.line2.setText(songs_albums);
670
Marco Nelissenbd447b62009-06-29 14:52:05 -0700671 long currentartistid = MusicUtils.getCurrentArtistId();
672 long artistid = cursor.getLong(mGroupArtistIdIdx);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800673 if (currentartistid == artistid && !isexpanded) {
674 vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
675 } else {
676 vh.play_indicator.setImageDrawable(null);
677 }
678 }
679
680 @Override
681 public void bindChildView(View view, Context context, Cursor cursor, boolean islast) {
682
683 ViewHolder vh = (ViewHolder) view.getTag();
684
685 String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
686 String displayname = name;
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700687 boolean unknown = name == null || name.equals(MediaFile.UNKNOWN_STRING);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800688 if (unknown) {
689 displayname = mUnknownAlbum;
690 }
691 vh.line1.setText(displayname);
692
693 int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS));
694 int numartistsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800695
696 final StringBuilder builder = mBuffer;
697 builder.delete(0, builder.length());
698 if (unknown) {
699 numsongs = numartistsongs;
700 }
701
702 if (numsongs == 1) {
703 builder.append(context.getString(R.string.onesong));
704 } else {
705 if (numsongs == numartistsongs) {
706 final Object[] args = mFormatArgs;
707 args[0] = numsongs;
708 builder.append(mResources.getQuantityString(R.plurals.Nsongs, numsongs, args));
709 } else {
710 final Object[] args = mFormatArgs3;
711 args[0] = numsongs;
712 args[1] = numartistsongs;
713 args[2] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
714 builder.append(mResources.getQuantityString(R.plurals.Nsongscomp, numsongs, args));
715 }
716 }
717 vh.line2.setText(builder.toString());
718
719 ImageView iv = vh.icon;
720 // We don't actually need the path to the thumbnail file,
721 // we just use it to see if there is album art or not
722 String art = cursor.getString(cursor.getColumnIndexOrThrow(
723 MediaStore.Audio.Albums.ALBUM_ART));
724 if (unknown || art == null || art.length() == 0) {
725 iv.setBackgroundDrawable(mDefaultAlbumIcon);
726 iv.setImageDrawable(null);
727 } else {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700728 long artIndex = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800729 Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon);
730 iv.setImageDrawable(d);
731 }
732
Marco Nelissenbd447b62009-06-29 14:52:05 -0700733 long currentalbumid = MusicUtils.getCurrentAlbumId();
734 long aid = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800735 iv = vh.play_indicator;
736 if (currentalbumid == aid) {
737 iv.setImageDrawable(mNowPlayingOverlay);
738 } else {
739 iv.setImageDrawable(null);
740 }
741 }
742
743
744 @Override
745 protected Cursor getChildrenCursor(Cursor groupCursor) {
746
Marco Nelissenbd447b62009-06-29 14:52:05 -0700747 long id = groupCursor.getLong(groupCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800748
749 String[] cols = new String[] {
750 MediaStore.Audio.Albums._ID,
751 MediaStore.Audio.Albums.ALBUM,
752 MediaStore.Audio.Albums.NUMBER_OF_SONGS,
753 MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
The Android Open Source Project792a2202009-03-03 19:32:30 -0800754 MediaStore.Audio.Albums.ALBUM_ART
755 };
756 Cursor c = MusicUtils.query(mActivity,
757 MediaStore.Audio.Artists.Albums.getContentUri("external", id),
758 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
759
760 class MyCursorWrapper extends CursorWrapper {
761 String mArtistName;
762 int mMagicColumnIdx;
763 MyCursorWrapper(Cursor c, String artist) {
764 super(c);
765 mArtistName = artist;
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700766 if (mArtistName == null || mArtistName.equals(MediaFile.UNKNOWN_STRING)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800767 mArtistName = mUnknownArtist;
768 }
769 mMagicColumnIdx = c.getColumnCount();
770 }
771
772 @Override
773 public String getString(int columnIndex) {
774 if (columnIndex != mMagicColumnIdx) {
775 return super.getString(columnIndex);
776 }
777 return mArtistName;
778 }
779
780 @Override
781 public int getColumnIndexOrThrow(String name) {
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700782 if (MediaStore.Audio.Albums.ARTIST.equals(name)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800783 return mMagicColumnIdx;
784 }
785 return super.getColumnIndexOrThrow(name);
786 }
787
788 @Override
789 public String getColumnName(int idx) {
790 if (idx != mMagicColumnIdx) {
791 return super.getColumnName(idx);
792 }
793 return MediaStore.Audio.Albums.ARTIST;
794 }
795
796 @Override
797 public int getColumnCount() {
798 return super.getColumnCount() + 1;
799 }
800 }
801 return new MyCursorWrapper(c, groupCursor.getString(mGroupArtistIdx));
802 }
803
804 @Override
805 public void changeCursor(Cursor cursor) {
806 if (cursor != mActivity.mArtistCursor) {
807 mActivity.mArtistCursor = cursor;
808 getColumnIndices(cursor);
809 super.changeCursor(cursor);
810 }
811 }
812
813 @Override
814 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
815 String s = constraint.toString();
816 if (mConstraintIsValid && (
817 (s == null && mConstraint == null) ||
818 (s != null && s.equals(mConstraint)))) {
819 return getCursor();
820 }
821 Cursor c = mActivity.getArtistCursor(null, s);
822 mConstraint = s;
823 mConstraintIsValid = true;
824 return c;
825 }
826
827 public Object[] getSections() {
828 return mIndexer.getSections();
829 }
830
831 public int getPositionForSection(int sectionIndex) {
832 return mIndexer.getPositionForSection(sectionIndex);
833 }
834
835 public int getSectionForPosition(int position) {
836 return 0;
837 }
838 }
839
840 private Cursor mArtistCursor;
841}
842