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