blob: 139fcc71604d8babb264e854646704f1468694d2 [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;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080025import android.content.ComponentName;
The Android Open Source Project792a2202009-03-03 19:32:30 -080026import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080030import android.content.ServiceConnection;
The Android Open Source Project792a2202009-03-03 19:32:30 -080031import android.content.res.Resources;
32import android.database.Cursor;
33import android.database.CursorWrapper;
34import android.graphics.drawable.BitmapDrawable;
35import android.graphics.drawable.Drawable;
36import android.media.AudioManager;
The Android Open Source Project792a2202009-03-03 19:32:30 -080037import android.net.Uri;
38import android.os.Bundle;
39import android.os.Handler;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080040import android.os.IBinder;
The Android Open Source Project792a2202009-03-03 19:32:30 -080041import android.os.Message;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080042import android.os.Parcel;
43import android.os.Parcelable;
The Android Open Source Project792a2202009-03-03 19:32:30 -080044import android.provider.MediaStore;
45import android.util.Log;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080046import android.util.SparseArray;
The Android Open Source Project792a2202009-03-03 19:32:30 -080047import android.view.ContextMenu;
48import android.view.Menu;
49import android.view.MenuItem;
50import android.view.SubMenu;
51import android.view.View;
52import android.view.ViewGroup;
53import android.view.Window;
54import android.view.ContextMenu.ContextMenuInfo;
The Android Open Source Project792a2202009-03-03 19:32:30 -080055import android.widget.ExpandableListView;
56import android.widget.ImageView;
57import android.widget.SectionIndexer;
58import android.widget.SimpleCursorTreeAdapter;
59import android.widget.TextView;
60import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
61
62import java.text.Collator;
63
64
65public class ArtistAlbumBrowserActivity extends ExpandableListActivity
Marco Nelissenec0c57a2009-12-12 12:27:11 -080066 implements View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection
The Android Open Source Project792a2202009-03-03 19:32:30 -080067{
68 private String mCurrentArtistId;
69 private String mCurrentArtistName;
70 private String mCurrentAlbumId;
71 private String mCurrentAlbumName;
72 private String mCurrentArtistNameForAlbum;
Marco Nelissen9882f542009-08-14 10:06:15 -070073 boolean mIsUnknownArtist;
74 boolean mIsUnknownAlbum;
The Android Open Source Project792a2202009-03-03 19:32:30 -080075 private ArtistAlbumListAdapter mAdapter;
76 private boolean mAdapterSent;
77 private final static int SEARCH = CHILD_MENU_BASE;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080078 private static int mLastListPosCourse = -1;
79 private static int mLastListPosFine = -1;
The Android Open Source Project792a2202009-03-03 19:32:30 -080080
The Android Open Source Project792a2202009-03-03 19:32:30 -080081 /** 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);
Marco Nelissenec0c57a2009-12-12 12:27:11 -080086 requestWindowFeature(Window.FEATURE_NO_TITLE);
The Android Open Source Project792a2202009-03-03 19:32:30 -080087 setVolumeControlStream(AudioManager.STREAM_MUSIC);
88 if (icicle != null) {
89 mCurrentAlbumId = icicle.getString("selectedalbum");
90 mCurrentAlbumName = icicle.getString("selectedalbumname");
91 mCurrentArtistId = icicle.getString("selectedartist");
92 mCurrentArtistName = icicle.getString("selectedartistname");
93 }
Marco Nelissenec0c57a2009-12-12 12:27:11 -080094 MusicUtils.bindToService(this, this);
The Android Open Source Project792a2202009-03-03 19:32:30 -080095
96 IntentFilter f = new IntentFilter();
97 f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
98 f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
99 f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
100 f.addDataScheme("file");
101 registerReceiver(mScanListener, f);
102
103 setContentView(R.layout.media_picker_activity_expanding);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800104 MusicUtils.updateButtonBar(this, R.id.artisttab);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800105 ExpandableListView lv = getExpandableListView();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800106 lv.setOnCreateContextMenuListener(this);
107 lv.setTextFilterEnabled(true);
108
109 mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance();
110 if (mAdapter == null) {
111 //Log.i("@@@", "starting query");
112 mAdapter = new ArtistAlbumListAdapter(
113 getApplication(),
114 this,
115 null, // cursor
116 R.layout.track_list_item_group,
117 new String[] {},
118 new int[] {},
119 R.layout.track_list_item_child,
120 new String[] {},
121 new int[] {});
122 setListAdapter(mAdapter);
123 setTitle(R.string.working_artists);
124 getArtistCursor(mAdapter.getQueryHandler(), null);
125 } else {
126 mAdapter.setActivity(this);
127 setListAdapter(mAdapter);
128 mArtistCursor = mAdapter.getCursor();
129 if (mArtistCursor != null) {
130 init(mArtistCursor);
131 } else {
132 getArtistCursor(mAdapter.getQueryHandler(), null);
133 }
134 }
135 }
136
137 @Override
138 public Object onRetainNonConfigurationInstance() {
139 mAdapterSent = true;
140 return mAdapter;
141 }
142
143 @Override
144 public void onSaveInstanceState(Bundle outcicle) {
145 // need to store the selected item so we don't lose it in case
146 // of an orientation switch. Otherwise we could lose it while
147 // in the middle of specifying a playlist to add the item to.
148 outcicle.putString("selectedalbum", mCurrentAlbumId);
149 outcicle.putString("selectedalbumname", mCurrentAlbumName);
150 outcicle.putString("selectedartist", mCurrentArtistId);
151 outcicle.putString("selectedartistname", mCurrentArtistName);
152 super.onSaveInstanceState(outcicle);
153 }
154
155 @Override
156 public void onDestroy() {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800157 ExpandableListView lv = getExpandableListView();
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800158 if (lv != null) {
159 mLastListPosCourse = lv.getFirstVisiblePosition();
Marco Nelissen23b531e2009-12-12 14:07:59 -0800160 View cv = lv.getChildAt(0);
161 if (cv != null) {
162 mLastListPosFine = cv.getTop();
163 }
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800164 }
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800165
The Android Open Source Project792a2202009-03-03 19:32:30 -0800166 MusicUtils.unbindFromService(this);
Marco Nelissen0e175782009-12-03 14:24:53 -0800167 // If we have an adapter and didn't send it off to another activity yet, we should
168 // close its cursor, which we do by assigning a null cursor to it. Doing this
169 // instead of closing the cursor directly keeps the framework from accessing
170 // the closed cursor later.
Marco Nelissen8e732ff2009-10-12 12:29:09 -0700171 if (!mAdapterSent && mAdapter != null) {
Marco Nelissen0e175782009-12-03 14:24:53 -0800172 mAdapter.changeCursor(null);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800173 }
Marco Nelissen0164ebf2009-08-13 09:58:43 -0700174 // Because we pass the adapter to the next activity, we need to make
175 // sure it doesn't keep a reference to this activity. We can do this
176 // by clearing its DatasetObservers, which setListAdapter(null) does.
177 setListAdapter(null);
178 mAdapter = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800179 unregisterReceiver(mScanListener);
Marco Nelissen9882f542009-08-14 10:06:15 -0700180 setListAdapter(null);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800181 super.onDestroy();
182 }
183
184 @Override
185 public void onResume() {
186 super.onResume();
187 IntentFilter f = new IntentFilter();
188 f.addAction(MediaPlaybackService.META_CHANGED);
189 f.addAction(MediaPlaybackService.QUEUE_CHANGED);
190 registerReceiver(mTrackListListener, f);
191 mTrackListListener.onReceive(null, null);
192
193 MusicUtils.setSpinnerState(this);
194 }
195
196 private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
197 @Override
198 public void onReceive(Context context, Intent intent) {
199 getExpandableListView().invalidateViews();
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800200 MusicUtils.updateNowPlaying(ArtistAlbumBrowserActivity.this);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800201 }
202 };
203 private BroadcastReceiver mScanListener = new BroadcastReceiver() {
204 @Override
205 public void onReceive(Context context, Intent intent) {
206 MusicUtils.setSpinnerState(ArtistAlbumBrowserActivity.this);
207 mReScanHandler.sendEmptyMessage(0);
208 if (intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
209 MusicUtils.clearAlbumArtCache();
210 }
211 }
212 };
213
214 private Handler mReScanHandler = new Handler() {
215 @Override
216 public void handleMessage(Message msg) {
Marco Nelissen42bcc212009-09-01 13:22:19 -0700217 if (mAdapter != null) {
218 getArtistCursor(mAdapter.getQueryHandler(), null);
219 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800220 }
221 };
222
223 @Override
224 public void onPause() {
225 unregisterReceiver(mTrackListListener);
226 mReScanHandler.removeCallbacksAndMessages(null);
227 super.onPause();
228 }
229
230 public void init(Cursor c) {
231
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(c); // also sets mArtistCursor
236
237 if (mArtistCursor == 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 ExpandableListView elv = getExpandableListView();
247 elv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
248 mLastListPosCourse = -1;
249 }
250
The Android Open Source Project792a2202009-03-03 19:32:30 -0800251 MusicUtils.hideDatabaseError(this);
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800252 MusicUtils.updateButtonBar(this, R.id.artisttab);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800253 setTitle();
254 }
255
256 private void setTitle() {
257 setTitle(R.string.artists_title);
258 }
259
260 @Override
261 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
262
263 mCurrentAlbumId = Long.valueOf(id).toString();
264
265 Intent intent = new Intent(Intent.ACTION_PICK);
266 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
267 intent.putExtra("album", mCurrentAlbumId);
268 Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition, childPosition);
269 String album = c.getString(c.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800270 if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800271 // unknown album, so we should include the artist ID to limit the songs to songs only by that artist
272 mArtistCursor.moveToPosition(groupPosition);
273 mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndex(MediaStore.Audio.Artists._ID));
274 intent.putExtra("artist", mCurrentArtistId);
275 }
276 startActivity(intent);
277 return true;
278 }
279
280 @Override
281 public boolean onCreateOptionsMenu(Menu menu) {
282 super.onCreateOptionsMenu(menu);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800283 menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
The Android Open Source Project792a2202009-03-03 19:32:30 -0800284 menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all).setIcon(R.drawable.ic_menu_shuffle);
285 return true;
286 }
287
288 @Override
289 public boolean onPrepareOptionsMenu(Menu menu) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800290 MusicUtils.setPartyShuffleMenuIcon(menu);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800291 return super.onPrepareOptionsMenu(menu);
292 }
293
294 @Override
295 public boolean onOptionsItemSelected(MenuItem item) {
296 Intent intent;
297 Cursor cursor;
298 switch (item.getItemId()) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800299 case PARTY_SHUFFLE:
300 MusicUtils.togglePartyShuffle();
301 break;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800302
303 case SHUFFLE_ALL:
304 cursor = MusicUtils.query(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
305 new String [] { MediaStore.Audio.Media._ID},
306 MediaStore.Audio.Media.IS_MUSIC + "=1", null,
307 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
308 if (cursor != null) {
309 MusicUtils.shuffleAll(this, cursor);
310 cursor.close();
311 }
312 return true;
313 }
314 return super.onOptionsItemSelected(item);
315 }
316
317 @Override
318 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
319 menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
320 SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist);
321 MusicUtils.makePlaylistMenu(this, sub);
322 menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800323
324 ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn;
325
326 int itemtype = ExpandableListView.getPackedPositionType(mi.packedPosition);
327 int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition);
328 int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition);
329 if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
330 if (gpos == -1) {
331 // this shouldn't happen
332 Log.d("Artist/Album", "no group");
333 return;
334 }
335 gpos = gpos - getExpandableListView().getHeaderViewsCount();
336 mArtistCursor.moveToPosition(gpos);
337 mCurrentArtistId = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
338 mCurrentArtistName = mArtistCursor.getString(mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
339 mCurrentAlbumId = null;
Marco Nelissen9882f542009-08-14 10:06:15 -0700340 mIsUnknownArtist = mCurrentArtistName == null ||
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800341 mCurrentArtistName.equals(MediaStore.UNKNOWN_STRING);
Marco Nelissen9882f542009-08-14 10:06:15 -0700342 mIsUnknownAlbum = true;
343 if (mIsUnknownArtist) {
Eric Fischerc8aa8372009-06-17 16:34:12 -0700344 menu.setHeaderTitle(getString(R.string.unknown_artist_name));
345 } else {
346 menu.setHeaderTitle(mCurrentArtistName);
Marco Nelissen9882f542009-08-14 10:06:15 -0700347 menu.add(0, SEARCH, 0, R.string.search_title);
Eric Fischerc8aa8372009-06-17 16:34:12 -0700348 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800349 return;
350 } else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
351 if (cpos == -1) {
352 // this shouldn't happen
353 Log.d("Artist/Album", "no child");
354 return;
355 }
356 Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos);
357 c.moveToPosition(cpos);
358 mCurrentArtistId = null;
359 mCurrentAlbumId = Long.valueOf(mi.id).toString();
360 mCurrentAlbumName = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
361 gpos = gpos - getExpandableListView().getHeaderViewsCount();
362 mArtistCursor.moveToPosition(gpos);
363 mCurrentArtistNameForAlbum = mArtistCursor.getString(
364 mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
Marco Nelissen9882f542009-08-14 10:06:15 -0700365 mIsUnknownArtist = mCurrentArtistNameForAlbum == null ||
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800366 mCurrentArtistNameForAlbum.equals(MediaStore.UNKNOWN_STRING);
Marco Nelissen9882f542009-08-14 10:06:15 -0700367 mIsUnknownAlbum = mCurrentAlbumName == null ||
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800368 mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING);
Marco Nelissen9882f542009-08-14 10:06:15 -0700369 if (mIsUnknownAlbum) {
Eric Fischerc8aa8372009-06-17 16:34:12 -0700370 menu.setHeaderTitle(getString(R.string.unknown_album_name));
371 } else {
372 menu.setHeaderTitle(mCurrentAlbumName);
373 }
Marco Nelissen9882f542009-08-14 10:06:15 -0700374 if (!mIsUnknownAlbum || !mIsUnknownArtist) {
375 menu.add(0, SEARCH, 0, R.string.search_title);
376 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800377 }
378 }
379
380 @Override
381 public boolean onContextItemSelected(MenuItem item) {
382 switch (item.getItemId()) {
383 case PLAY_SELECTION: {
384 // play everything by the selected artist
Marco Nelissenbd447b62009-06-29 14:52:05 -0700385 long [] list =
The Android Open Source Project792a2202009-03-03 19:32:30 -0800386 mCurrentArtistId != null ?
Marco Nelissenbd447b62009-06-29 14:52:05 -0700387 MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
388 : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800389
390 MusicUtils.playAll(this, list, 0);
391 return true;
392 }
393
394 case QUEUE: {
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));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800399 MusicUtils.addToCurrentPlaylist(this, list);
400 return true;
401 }
402
403 case NEW_PLAYLIST: {
404 Intent intent = new Intent();
405 intent.setClass(this, CreatePlaylist.class);
406 startActivityForResult(intent, NEW_PLAYLIST);
407 return true;
408 }
409
410 case PLAYLIST_SELECTED: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700411 long [] list =
The Android Open Source Project792a2202009-03-03 19:32:30 -0800412 mCurrentArtistId != null ?
Marco Nelissenbd447b62009-06-29 14:52:05 -0700413 MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
414 : MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
415 long playlist = item.getIntent().getLongExtra("playlist", 0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800416 MusicUtils.addToPlaylist(this, list, playlist);
417 return true;
418 }
419
420 case DELETE_ITEM: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700421 long [] list;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800422 String desc;
423 if (mCurrentArtistId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700424 list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800425 String f = getString(R.string.delete_artist_desc);
426 desc = String.format(f, mCurrentArtistName);
427 } else {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700428 list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800429 String f = getString(R.string.delete_album_desc);
430 desc = String.format(f, mCurrentAlbumName);
431 }
432 Bundle b = new Bundle();
433 b.putString("description", desc);
Marco Nelissenbd447b62009-06-29 14:52:05 -0700434 b.putLongArray("items", list);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800435 Intent intent = new Intent();
436 intent.setClass(this, DeleteItems.class);
437 intent.putExtras(b);
438 startActivityForResult(intent, -1);
439 return true;
440 }
441
442 case SEARCH:
443 doSearch();
444 return true;
445 }
446 return super.onContextItemSelected(item);
447 }
448
449 void doSearch() {
450 CharSequence title = null;
451 String query = null;
452
453 Intent i = new Intent();
454 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
Marco Nelissen4341b502009-05-06 14:57:37 -0700455 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800456
457 if (mCurrentArtistId != null) {
458 title = mCurrentArtistName;
459 query = mCurrentArtistName;
460 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistName);
461 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE);
462 } else {
Marco Nelissen9882f542009-08-14 10:06:15 -0700463 if (mIsUnknownAlbum) {
464 title = query = mCurrentArtistNameForAlbum;
465 } else {
466 title = query = mCurrentAlbumName;
467 if (!mIsUnknownArtist) {
468 query = query + " " + mCurrentArtistNameForAlbum;
469 }
470 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800471 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, mCurrentArtistNameForAlbum);
472 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
473 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE);
474 }
475 title = getString(R.string.mediasearch, title);
476 i.putExtra(SearchManager.QUERY, query);
477
478 startActivity(Intent.createChooser(i, title));
479 }
480
481 @Override
482 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
483 switch (requestCode) {
484 case SCAN_DONE:
485 if (resultCode == RESULT_CANCELED) {
486 finish();
487 } else {
488 getArtistCursor(mAdapter.getQueryHandler(), null);
489 }
490 break;
491
492 case NEW_PLAYLIST:
493 if (resultCode == RESULT_OK) {
494 Uri uri = intent.getData();
495 if (uri != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700496 long [] list = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800497 if (mCurrentArtistId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700498 list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800499 } else if (mCurrentAlbumId != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700500 list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800501 }
Marco Nelissenbd447b62009-06-29 14:52:05 -0700502 MusicUtils.addToPlaylist(this, list, Long.parseLong(uri.getLastPathSegment()));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800503 }
504 }
505 break;
506 }
507 }
508
509 private Cursor getArtistCursor(AsyncQueryHandler async, String filter) {
510
511 StringBuilder where = new StringBuilder();
512 where.append(MediaStore.Audio.Artists.ARTIST + " != ''");
513
514 // Add in the filtering constraints
515 String [] keywords = null;
516 if (filter != null) {
517 String [] searchWords = filter.split(" ");
518 keywords = new String[searchWords.length];
519 Collator col = Collator.getInstance();
520 col.setStrength(Collator.PRIMARY);
521 for (int i = 0; i < searchWords.length; i++) {
522 keywords[i] = '%' + MediaStore.Audio.keyFor(searchWords[i]) + '%';
523 }
524 for (int i = 0; i < searchWords.length; i++) {
525 where.append(" AND ");
526 where.append(MediaStore.Audio.Media.ARTIST_KEY + " LIKE ?");
527 }
528 }
529
530 String whereclause = where.toString();
531 String[] cols = new String[] {
532 MediaStore.Audio.Artists._ID,
533 MediaStore.Audio.Artists.ARTIST,
534 MediaStore.Audio.Artists.NUMBER_OF_ALBUMS,
535 MediaStore.Audio.Artists.NUMBER_OF_TRACKS
536 };
537 Cursor ret = null;
538 if (async != null) {
539 async.startQuery(0, null, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
540 cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
541 } else {
542 ret = MusicUtils.query(this, MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
543 cols, whereclause , keywords, MediaStore.Audio.Artists.ARTIST_KEY);
544 }
545 return ret;
546 }
547
548 static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter implements SectionIndexer {
549
550 private final Drawable mNowPlayingOverlay;
551 private final BitmapDrawable mDefaultAlbumIcon;
552 private int mGroupArtistIdIdx;
553 private int mGroupArtistIdx;
554 private int mGroupAlbumIdx;
555 private int mGroupSongIdx;
556 private final Context mContext;
557 private final Resources mResources;
558 private final String mAlbumSongSeparator;
559 private final String mUnknownAlbum;
560 private final String mUnknownArtist;
561 private final StringBuilder mBuffer = new StringBuilder();
562 private final Object[] mFormatArgs = new Object[1];
563 private final Object[] mFormatArgs3 = new Object[3];
564 private MusicAlphabetIndexer mIndexer;
565 private ArtistAlbumBrowserActivity mActivity;
566 private AsyncQueryHandler mQueryHandler;
567 private String mConstraint = null;
568 private boolean mConstraintIsValid = false;
569
Marco Nelissen756c3f52009-05-14 10:07:23 -0700570 static class ViewHolder {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800571 TextView line1;
572 TextView line2;
573 ImageView play_indicator;
574 ImageView icon;
575 }
576
577 class QueryHandler extends AsyncQueryHandler {
578 QueryHandler(ContentResolver res) {
579 super(res);
580 }
581
582 @Override
583 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
584 //Log.i("@@@", "query complete");
585 mActivity.init(cursor);
586 }
587 }
588
589 ArtistAlbumListAdapter(Context context, ArtistAlbumBrowserActivity currentactivity,
590 Cursor cursor, int glayout, String[] gfrom, int[] gto,
591 int clayout, String[] cfrom, int[] cto) {
592 super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto);
593 mActivity = currentactivity;
594 mQueryHandler = new QueryHandler(context.getContentResolver());
595
596 Resources r = context.getResources();
597 mNowPlayingOverlay = r.getDrawable(R.drawable.indicator_ic_mp_playing_list);
598 mDefaultAlbumIcon = (BitmapDrawable) r.getDrawable(R.drawable.albumart_mp_unknown_list);
599 // no filter or dither, it's a lot faster and we can't tell the difference
600 mDefaultAlbumIcon.setFilterBitmap(false);
601 mDefaultAlbumIcon.setDither(false);
602
603 mContext = context;
604 getColumnIndices(cursor);
605 mResources = context.getResources();
606 mAlbumSongSeparator = context.getString(R.string.albumsongseparator);
607 mUnknownAlbum = context.getString(R.string.unknown_album_name);
608 mUnknownArtist = context.getString(R.string.unknown_artist_name);
609 }
610
611 private void getColumnIndices(Cursor cursor) {
612 if (cursor != null) {
613 mGroupArtistIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
614 mGroupArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
615 mGroupAlbumIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
616 mGroupSongIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
617 if (mIndexer != null) {
618 mIndexer.setCursor(cursor);
619 } else {
620 mIndexer = new MusicAlphabetIndexer(cursor, mGroupArtistIdx,
621 mResources.getString(com.android.internal.R.string.fast_scroll_alphabet));
622 }
623 }
624 }
625
626 public void setActivity(ArtistAlbumBrowserActivity newactivity) {
627 mActivity = newactivity;
628 }
629
630 public AsyncQueryHandler getQueryHandler() {
631 return mQueryHandler;
632 }
633
634 @Override
635 public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
636 View v = super.newGroupView(context, cursor, isExpanded, parent);
637 ImageView iv = (ImageView) v.findViewById(R.id.icon);
638 ViewGroup.LayoutParams p = iv.getLayoutParams();
639 p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
640 p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
641 ViewHolder vh = new ViewHolder();
642 vh.line1 = (TextView) v.findViewById(R.id.line1);
643 vh.line2 = (TextView) v.findViewById(R.id.line2);
644 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
645 vh.icon = (ImageView) v.findViewById(R.id.icon);
646 vh.icon.setPadding(0, 0, 1, 0);
647 v.setTag(vh);
648 return v;
649 }
650
651 @Override
652 public View newChildView(Context context, Cursor cursor, boolean isLastChild,
653 ViewGroup parent) {
654 View v = super.newChildView(context, cursor, isLastChild, parent);
655 ViewHolder vh = new ViewHolder();
656 vh.line1 = (TextView) v.findViewById(R.id.line1);
657 vh.line2 = (TextView) v.findViewById(R.id.line2);
658 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
659 vh.icon = (ImageView) v.findViewById(R.id.icon);
660 vh.icon.setBackgroundDrawable(mDefaultAlbumIcon);
661 vh.icon.setPadding(0, 0, 1, 0);
662 v.setTag(vh);
663 return v;
664 }
665
666 @Override
667 public void bindGroupView(View view, Context context, Cursor cursor, boolean isexpanded) {
668
669 ViewHolder vh = (ViewHolder) view.getTag();
670
671 String artist = cursor.getString(mGroupArtistIdx);
672 String displayartist = artist;
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800673 boolean unknown = artist == null || artist.equals(MediaStore.UNKNOWN_STRING);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800674 if (unknown) {
675 displayartist = mUnknownArtist;
676 }
677 vh.line1.setText(displayartist);
678
679 int numalbums = cursor.getInt(mGroupAlbumIdx);
680 int numsongs = cursor.getInt(mGroupSongIdx);
681
682 String songs_albums = MusicUtils.makeAlbumsLabel(context,
683 numalbums, numsongs, unknown);
684
685 vh.line2.setText(songs_albums);
686
Marco Nelissenbd447b62009-06-29 14:52:05 -0700687 long currentartistid = MusicUtils.getCurrentArtistId();
688 long artistid = cursor.getLong(mGroupArtistIdIdx);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800689 if (currentartistid == artistid && !isexpanded) {
690 vh.play_indicator.setImageDrawable(mNowPlayingOverlay);
691 } else {
692 vh.play_indicator.setImageDrawable(null);
693 }
694 }
695
696 @Override
697 public void bindChildView(View view, Context context, Cursor cursor, boolean islast) {
698
699 ViewHolder vh = (ViewHolder) view.getTag();
700
701 String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
702 String displayname = name;
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800703 boolean unknown = name == null || name.equals(MediaStore.UNKNOWN_STRING);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800704 if (unknown) {
705 displayname = mUnknownAlbum;
706 }
707 vh.line1.setText(displayname);
708
709 int numsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS));
710 int numartistsongs = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800711
712 final StringBuilder builder = mBuffer;
713 builder.delete(0, builder.length());
714 if (unknown) {
715 numsongs = numartistsongs;
716 }
717
718 if (numsongs == 1) {
719 builder.append(context.getString(R.string.onesong));
720 } else {
721 if (numsongs == numartistsongs) {
722 final Object[] args = mFormatArgs;
723 args[0] = numsongs;
724 builder.append(mResources.getQuantityString(R.plurals.Nsongs, numsongs, args));
725 } else {
726 final Object[] args = mFormatArgs3;
727 args[0] = numsongs;
728 args[1] = numartistsongs;
729 args[2] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
730 builder.append(mResources.getQuantityString(R.plurals.Nsongscomp, numsongs, args));
731 }
732 }
733 vh.line2.setText(builder.toString());
734
735 ImageView iv = vh.icon;
736 // We don't actually need the path to the thumbnail file,
737 // we just use it to see if there is album art or not
738 String art = cursor.getString(cursor.getColumnIndexOrThrow(
739 MediaStore.Audio.Albums.ALBUM_ART));
740 if (unknown || art == null || art.length() == 0) {
741 iv.setBackgroundDrawable(mDefaultAlbumIcon);
742 iv.setImageDrawable(null);
743 } else {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700744 long artIndex = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800745 Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon);
746 iv.setImageDrawable(d);
747 }
748
Marco Nelissenbd447b62009-06-29 14:52:05 -0700749 long currentalbumid = MusicUtils.getCurrentAlbumId();
750 long aid = cursor.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800751 iv = vh.play_indicator;
752 if (currentalbumid == aid) {
753 iv.setImageDrawable(mNowPlayingOverlay);
754 } else {
755 iv.setImageDrawable(null);
756 }
757 }
758
759
760 @Override
761 protected Cursor getChildrenCursor(Cursor groupCursor) {
762
Marco Nelissenbd447b62009-06-29 14:52:05 -0700763 long id = groupCursor.getLong(groupCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800764
765 String[] cols = new String[] {
766 MediaStore.Audio.Albums._ID,
767 MediaStore.Audio.Albums.ALBUM,
768 MediaStore.Audio.Albums.NUMBER_OF_SONGS,
769 MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
The Android Open Source Project792a2202009-03-03 19:32:30 -0800770 MediaStore.Audio.Albums.ALBUM_ART
771 };
772 Cursor c = MusicUtils.query(mActivity,
773 MediaStore.Audio.Artists.Albums.getContentUri("external", id),
774 cols, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);
775
776 class MyCursorWrapper extends CursorWrapper {
777 String mArtistName;
778 int mMagicColumnIdx;
779 MyCursorWrapper(Cursor c, String artist) {
780 super(c);
781 mArtistName = artist;
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800782 if (mArtistName == null || mArtistName.equals(MediaStore.UNKNOWN_STRING)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800783 mArtistName = mUnknownArtist;
784 }
785 mMagicColumnIdx = c.getColumnCount();
786 }
787
788 @Override
789 public String getString(int columnIndex) {
790 if (columnIndex != mMagicColumnIdx) {
791 return super.getString(columnIndex);
792 }
793 return mArtistName;
794 }
795
796 @Override
797 public int getColumnIndexOrThrow(String name) {
The Android Open Source Project6a9c41c2009-03-09 11:52:14 -0700798 if (MediaStore.Audio.Albums.ARTIST.equals(name)) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800799 return mMagicColumnIdx;
800 }
801 return super.getColumnIndexOrThrow(name);
802 }
803
804 @Override
805 public String getColumnName(int idx) {
806 if (idx != mMagicColumnIdx) {
807 return super.getColumnName(idx);
808 }
809 return MediaStore.Audio.Albums.ARTIST;
810 }
811
812 @Override
813 public int getColumnCount() {
814 return super.getColumnCount() + 1;
815 }
816 }
817 return new MyCursorWrapper(c, groupCursor.getString(mGroupArtistIdx));
818 }
819
820 @Override
821 public void changeCursor(Cursor cursor) {
Marco Nelissend99bc1e2009-12-04 08:48:06 -0800822 if (mActivity.isFinishing() && cursor != null) {
823 cursor.close();
824 cursor = null;
Marco Nelissen0e175782009-12-03 14:24:53 -0800825 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800826 if (cursor != mActivity.mArtistCursor) {
827 mActivity.mArtistCursor = cursor;
828 getColumnIndices(cursor);
829 super.changeCursor(cursor);
830 }
831 }
832
833 @Override
834 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
835 String s = constraint.toString();
836 if (mConstraintIsValid && (
837 (s == null && mConstraint == null) ||
838 (s != null && s.equals(mConstraint)))) {
839 return getCursor();
840 }
841 Cursor c = mActivity.getArtistCursor(null, s);
842 mConstraint = s;
843 mConstraintIsValid = true;
844 return c;
845 }
846
847 public Object[] getSections() {
848 return mIndexer.getSections();
849 }
850
851 public int getPositionForSection(int sectionIndex) {
852 return mIndexer.getPositionForSection(sectionIndex);
853 }
854
855 public int getSectionForPosition(int position) {
856 return 0;
857 }
858 }
859
860 private Cursor mArtistCursor;
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800861
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800862 public void onServiceConnected(ComponentName name, IBinder service) {
863 MusicUtils.updateNowPlaying(this);
864 }
865
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800866 public void onServiceDisconnected(ComponentName name) {
867 finish();
868 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800869}
870