1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.music; 18 19 import android.app.ExpandableListActivity; 20 import android.content.ComponentName; 21 import android.content.Intent; 22 import android.graphics.Bitmap; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.graphics.drawable.Drawable; 25 import android.media.AudioManager; 26 import android.media.MediaDescription; 27 import android.media.MediaMetadata; 28 import android.media.browse.MediaBrowser; 29 import android.media.session.MediaController; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.Window; 35 import android.widget.ExpandableListView; 36 import android.widget.ImageView; 37 import android.widget.SimpleExpandableListAdapter; 38 import android.widget.TextView; 39 import android.widget.Toast; 40 41 import com.android.music.utils.LogHelper; 42 import com.android.music.utils.MediaIDHelper; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.concurrent.ConcurrentHashMap; 48 49 public class ArtistAlbumBrowserActivity extends ExpandableListActivity { 50 private static final String TAG = LogHelper.makeLogTag(ArtistAlbumBrowserActivity.class); 51 private static final String KEY_NUM_ALBUMS = "__NUM_ALBUMS__"; 52 private static final MediaBrowser.MediaItem DEFAULT_PARENT_ITEM = 53 new MediaBrowser.MediaItem(new MediaDescription.Builder() 54 .setMediaId(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST) 55 .build(), 56 MediaBrowser.MediaItem.FLAG_BROWSABLE); 57 58 private ArtistAlbumListAdapter mAdapter; 59 private MediaBrowser mMediaBrowser; 60 private MediaBrowser.MediaItem mParentItem; 61 62 /** Called when the activity is first created. */ 63 @Override 64 public void onCreate(Bundle icicle) { 65 super.onCreate(icicle); 66 LogHelper.d(TAG, "onCreate()"); 67 // Handle past states 68 if (icicle != null) { 69 mParentItem = icicle.getParcelable(MusicUtils.TAG_PARENT_ITEM); 70 } else if (getIntent() != null) { 71 mParentItem = getIntent().getExtras().getParcelable(MusicUtils.TAG_PARENT_ITEM); 72 } 73 if (mParentItem == null) { 74 mParentItem = DEFAULT_PARENT_ITEM; 75 } 76 requestWindowFeature(Window.FEATURE_NO_TITLE); 77 setVolumeControlStream(AudioManager.STREAM_MUSIC); 78 79 // Init layout 80 setContentView(R.layout.media_picker_activity_expanding); 81 MusicUtils.updateButtonBar(this, R.id.artisttab); 82 83 // Init expandable list 84 ExpandableListView lv = getExpandableListView(); 85 lv.setTextFilterEnabled(true); 86 mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance(); 87 if (mAdapter == null) { 88 // Log.i("@@@", "starting query"); 89 mAdapter = new ArtistAlbumListAdapter(this, new ArrayList<>(), new ArrayList<>()); 90 setListAdapter(mAdapter); 91 setTitle(R.string.working_artists); 92 } else { 93 mAdapter.setActivity(this); 94 } 95 setListAdapter(mAdapter); 96 LogHelper.d(TAG, "Creating MediaBrowser"); 97 mMediaBrowser = new MediaBrowser(this, new ComponentName(this, MediaPlaybackService.class), 98 mConnectionCallback, null); 99 } 100 101 @Override 102 public Object onRetainNonConfigurationInstance() { 103 return mAdapter; 104 } 105 106 @Override 107 public void onSaveInstanceState(Bundle outcicle) { 108 outcicle.putParcelable(MusicUtils.TAG_PARENT_ITEM, mParentItem); 109 super.onSaveInstanceState(outcicle); 110 } 111 112 @Override 113 public void onDestroy() { 114 setListAdapter(null); 115 mAdapter = null; 116 setListAdapter(null); 117 super.onDestroy(); 118 } 119 120 @Override 121 public void onStart() { 122 LogHelper.d(TAG, "onStart()"); 123 super.onStart(); 124 mMediaBrowser.connect(); 125 } 126 127 @Override 128 public void onStop() { 129 LogHelper.d(TAG, "onStop()"); 130 super.onStop(); 131 mMediaBrowser.disconnect(); 132 } 133 134 private MediaBrowser 135 .SubscriptionCallback mSubscriptionCallback = new MediaBrowser.SubscriptionCallback() { 136 @Override 137 public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) { 138 if (parentId.equals(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)) { 139 mAdapter.getArtistMap().clear(); 140 mAdapter.getGroupData().clear(); 141 mAdapter.notifyDataSetInvalidated(); 142 for (MediaBrowser.MediaItem item : children) { 143 ConcurrentHashMap<String, MediaBrowser.MediaItem> entry = 144 new ConcurrentHashMap<>(); 145 entry.put(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST, item); 146 synchronized (this) { 147 mAdapter.getArtistMap().put(item.getDescription().getTitle().toString(), 148 mAdapter.getGroupData().size()); 149 mAdapter.getGroupData().add(entry); 150 mAdapter.getChildData().add(new ArrayList<>()); 151 } 152 mMediaBrowser.subscribe(item.getMediaId(), this); 153 } 154 mAdapter.notifyDataSetChanged(); 155 } else if (parentId.startsWith(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST)) { 156 String artist = MediaIDHelper.getHierarchy(parentId)[1]; 157 if (!mAdapter.getArtistMap().containsKey(artist)) { 158 return; 159 } 160 int artistIndex = mAdapter.getArtistMap().get(artist); 161 mAdapter.getChildData().get(artistIndex).clear(); 162 mAdapter.notifyDataSetInvalidated(); 163 Bundle extras = new Bundle(); 164 extras.putLong(KEY_NUM_ALBUMS, children.size()); 165 MediaBrowser.MediaItem newArtistItem = 166 new MediaBrowser.MediaItem(new MediaDescription.Builder() 167 .setMediaId("Count") 168 .setExtras(extras) 169 .build(), 170 0); 171 mAdapter.getGroupData().get(artistIndex).put(KEY_NUM_ALBUMS, newArtistItem); 172 for (MediaBrowser.MediaItem item : children) { 173 ConcurrentHashMap<String, MediaBrowser.MediaItem> entry = 174 new ConcurrentHashMap<>(); 175 entry.put(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM, item); 176 mAdapter.getChildData().get(artistIndex).add(entry); 177 } 178 mAdapter.notifyDataSetChanged(); 179 } 180 } 181 182 @Override 183 public void onError(String id) { 184 Toast.makeText(getApplicationContext(), R.string.error_loading_media, Toast.LENGTH_LONG) 185 .show(); 186 } 187 }; 188 189 private MediaBrowser.ConnectionCallback mConnectionCallback = 190 new MediaBrowser.ConnectionCallback() { 191 @Override 192 public void onConnected() { 193 LogHelper.d( 194 TAG, "onConnected: session token ", mMediaBrowser.getSessionToken()); 195 mMediaBrowser.subscribe(mParentItem.getMediaId(), mSubscriptionCallback); 196 if (mMediaBrowser.getSessionToken() == null) { 197 throw new IllegalArgumentException("No Session token"); 198 } 199 MediaController mediaController = new MediaController( 200 ArtistAlbumBrowserActivity.this, mMediaBrowser.getSessionToken()); 201 mediaController.registerCallback(mMediaControllerCallback); 202 ArtistAlbumBrowserActivity.this.setMediaController(mediaController); 203 if (mediaController.getMetadata() != null) { 204 MusicUtils.updateNowPlaying(ArtistAlbumBrowserActivity.this); 205 } 206 } 207 208 @Override 209 public void onConnectionFailed() { 210 LogHelper.d(TAG, "onConnectionFailed"); 211 } 212 213 @Override 214 public void onConnectionSuspended() { 215 LogHelper.d(TAG, "onConnectionSuspended"); 216 ArtistAlbumBrowserActivity.this.setMediaController(null); 217 } 218 }; 219 220 private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { 221 @Override 222 public void onMetadataChanged(MediaMetadata metadata) { 223 super.onMetadataChanged(metadata); 224 MusicUtils.updateNowPlaying(ArtistAlbumBrowserActivity.this); 225 } 226 }; 227 228 private class ArtistAlbumListAdapter extends SimpleExpandableListAdapter { 229 private final Drawable mNowPlayingOverlay; 230 private final BitmapDrawable mDefaultAlbumIcon; 231 private ArtistAlbumBrowserActivity mActivity; 232 private ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> mGroupData; 233 private ArrayList < ArrayList 234 < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> mChildData; 235 private ConcurrentHashMap<String, Integer> mArtistMap; 236 237 private class ViewHolder { 238 TextView line1; 239 TextView line2; 240 ImageView play_indicator; 241 ImageView icon; 242 } 243 244 ArtistAlbumListAdapter(ArtistAlbumBrowserActivity currentActivity, 245 ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> groupData, 246 ArrayList < ArrayList 247 < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> childData) { 248 super(currentActivity, groupData, R.layout.track_list_item_group, new String[] {}, 249 new int[] {}, childData, R.layout.track_list_item_child, new String[] {}, 250 new int[] {}); 251 mGroupData = groupData; 252 mChildData = childData; 253 mActivity = currentActivity; 254 mNowPlayingOverlay = currentActivity.getResources().getDrawable( 255 R.drawable.indicator_ic_mp_playing_list, currentActivity.getTheme()); 256 mDefaultAlbumIcon = (BitmapDrawable) currentActivity.getResources().getDrawable( 257 R.drawable.albumart_mp_unknown_list, currentActivity.getTheme()); 258 // no filter or dither, it's a lot faster and we can't tell the difference 259 mDefaultAlbumIcon.setFilterBitmap(false); 260 mDefaultAlbumIcon.setDither(false); 261 mArtistMap = new ConcurrentHashMap<>(); 262 } 263 264 public ArrayList<ConcurrentHashMap<String, MediaBrowser.MediaItem>> getGroupData() { 265 return mGroupData; 266 } 267 268 public ArrayList < ArrayList 269 < ConcurrentHashMap<String, MediaBrowser.MediaItem>>> getChildData() { 270 return mChildData; 271 } 272 273 public Map<String, Integer> getArtistMap() { 274 return mArtistMap; 275 } 276 277 public void setActivity(ArtistAlbumBrowserActivity newactivity) { 278 mActivity = newactivity; 279 } 280 281 @Override 282 public View newGroupView(boolean isExpanded, ViewGroup parent) { 283 View v = super.newGroupView(isExpanded, parent); 284 ImageView iv = (ImageView) v.findViewById(R.id.icon); 285 ViewGroup.LayoutParams p = iv.getLayoutParams(); 286 p.width = ViewGroup.LayoutParams.WRAP_CONTENT; 287 p.height = ViewGroup.LayoutParams.WRAP_CONTENT; 288 ViewHolder vh = new ViewHolder(); 289 vh.line1 = (TextView) v.findViewById(R.id.line1); 290 vh.line2 = (TextView) v.findViewById(R.id.line2); 291 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator); 292 vh.icon = (ImageView) v.findViewById(R.id.icon); 293 vh.icon.setPadding(0, 0, 1, 0); 294 v.setTag(vh); 295 return v; 296 } 297 298 @Override 299 public View newChildView(boolean isLastChild, ViewGroup parent) { 300 View v = super.newChildView(isLastChild, parent); 301 ViewHolder vh = new ViewHolder(); 302 vh.line1 = (TextView) v.findViewById(R.id.line1); 303 vh.line2 = (TextView) v.findViewById(R.id.line2); 304 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator); 305 vh.icon = (ImageView) v.findViewById(R.id.icon); 306 vh.icon.setBackground(mDefaultAlbumIcon); 307 vh.icon.setPadding(0, 0, 1, 0); 308 v.setTag(vh); 309 return v; 310 } 311 312 @Override 313 public View getGroupView( 314 int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 315 if (convertView == null) { 316 convertView = newGroupView(isExpanded, parent); 317 } 318 Map<String, MediaBrowser.MediaItem> artistEntry = 319 (Map<String, MediaBrowser.MediaItem>) getGroup(groupPosition); 320 MediaBrowser.MediaItem artistItem = 321 artistEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST); 322 MediaBrowser.MediaItem countItem = artistEntry.get(KEY_NUM_ALBUMS); 323 ViewHolder vh = (ViewHolder) convertView.getTag(); 324 vh.line1.setText(artistItem.getDescription().getTitle()); 325 int numalbums = -1; 326 if (countItem != null) { 327 Bundle extras = countItem.getDescription().getExtras(); 328 if (extras != null) { 329 numalbums = (int) extras.getLong(KEY_NUM_ALBUMS); 330 } 331 } 332 String songs_albums = MusicUtils.makeAlbumsLabel(mActivity, numalbums, -1, false); 333 vh.line2.setText(songs_albums); 334 MediaController mediaController = mActivity.getMediaController(); 335 if (mediaController == null) { 336 vh.play_indicator.setImageDrawable(null); 337 return convertView; 338 } 339 MediaMetadata metadata = mediaController.getMetadata(); 340 if (metadata == null) { 341 vh.play_indicator.setImageDrawable(null); 342 return convertView; 343 } 344 if (metadata.getString(MediaMetadata.METADATA_KEY_ARTIST) 345 .equals(artistItem.getDescription().getTitle()) 346 && !isExpanded) { 347 vh.play_indicator.setImageDrawable(mNowPlayingOverlay); 348 } else { 349 vh.play_indicator.setImageDrawable(null); 350 } 351 return convertView; 352 } 353 354 @Override 355 public View getChildView(int groupPosition, int childPosition, boolean isLastChild, 356 View convertView, ViewGroup parent) { 357 if (convertView == null) { 358 convertView = newChildView(isLastChild, parent); 359 } 360 Map<String, MediaBrowser.MediaItem> albumEntry = 361 (Map<String, MediaBrowser.MediaItem>) getChild(groupPosition, childPosition); 362 MediaBrowser.MediaItem albumItem = 363 albumEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM); 364 ViewHolder vh = (ViewHolder) convertView.getTag(); 365 vh.line1.setText(albumItem.getDescription().getTitle()); 366 vh.line2.setText(albumItem.getDescription().getDescription()); 367 Bitmap albumArt = albumItem.getDescription().getIconBitmap(); 368 if (albumArt == null) { 369 vh.icon.setBackground(mDefaultAlbumIcon); 370 } else { 371 vh.icon.setImageDrawable(MusicUtils.getDrawableBitmap(albumArt, mDefaultAlbumIcon)); 372 } 373 MediaController mediaController = mActivity.getMediaController(); 374 if (mediaController == null) { 375 vh.play_indicator.setImageDrawable(null); 376 return convertView; 377 } 378 MediaMetadata metadata = mediaController.getMetadata(); 379 if (metadata == null) { 380 vh.play_indicator.setImageDrawable(null); 381 return convertView; 382 } 383 if (albumItem.getDescription().getTitle().equals( 384 metadata.getString(MediaMetadata.METADATA_KEY_ALBUM))) { 385 vh.play_indicator.setImageDrawable(mNowPlayingOverlay); 386 } else { 387 vh.play_indicator.setImageDrawable(null); 388 } 389 return convertView; 390 } 391 } 392 393 @Override 394 public boolean onChildClick( 395 ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { 396 Map<String, MediaBrowser.MediaItem> albumEntry = 397 (Map<String, MediaBrowser.MediaItem>) mAdapter.getChild( 398 groupPosition, childPosition); 399 Intent intent = new Intent(Intent.ACTION_PICK); 400 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); 401 intent.putExtra( 402 MusicUtils.TAG_PARENT_ITEM, albumEntry.get(MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM)); 403 startActivity(intent); 404 return true; 405 } 406 } 407