/* * Copyright (C) 2014 Fastboot Mobile, LLC. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses>. */ package com.fastbootmobile.encore.app.fragments; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.support.v7.app.ActionBar; import android.text.Spannable; import android.text.SpannableString; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.LayoutAnimationController; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.dd.CircularProgressButton; import com.fastbootmobile.encore.app.AlbumActivity; import com.fastbootmobile.encore.app.AppActivity; import com.fastbootmobile.encore.app.PlaylistActivity; import com.fastbootmobile.encore.app.R; import com.fastbootmobile.encore.app.adapters.PlaylistAdapter; import com.fastbootmobile.encore.app.ui.PlayPauseDrawable; import com.fastbootmobile.encore.app.ui.PlaylistListView; import com.fastbootmobile.encore.app.ui.ScrollStatusBarColorListener; import com.fastbootmobile.encore.art.AlbumArtHelper; import com.fastbootmobile.encore.art.RecyclingBitmapDrawable; import com.fastbootmobile.encore.framework.AutoPlaylistHelper; import com.fastbootmobile.encore.framework.PlaybackProxy; import com.fastbootmobile.encore.framework.PluginsLookup; import com.fastbootmobile.encore.model.Album; import com.fastbootmobile.encore.model.Artist; import com.fastbootmobile.encore.model.BoundEntity; import com.fastbootmobile.encore.model.Playlist; import com.fastbootmobile.encore.model.SearchResult; import com.fastbootmobile.encore.model.Song; import com.fastbootmobile.encore.providers.ILocalCallback; import com.fastbootmobile.encore.providers.IMusicProvider; import com.fastbootmobile.encore.providers.ProviderAggregator; import com.fastbootmobile.encore.providers.ProviderConnection; import com.fastbootmobile.encore.providers.ProviderIdentifier; import com.fastbootmobile.encore.service.BasePlaybackCallback; import com.fastbootmobile.encore.service.PlaybackService; import com.fastbootmobile.encore.utils.Utils; import com.getbase.floatingactionbutton.FloatingActionButton; import com.joshdholtz.sentry.Sentry; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /** * A simple {@link android.support.v4.app.Fragment} subclass. * Use the {@link com.fastbootmobile.encore.app.fragments.PlaylistViewFragment#newInstance} factory method to * create an instance of this fragment. */ public class PlaylistViewFragment extends MaterialReelBaseFragment implements ILocalCallback { private static final String TAG = "PlaylistViewFragment"; public static final String KEY_PLAYLIST = "playlist"; private static final int UPDATE_OFFLINE_STATUS = 1; private static final int UPDATE_DATA_SET = 2; private PlaylistAdapter mAdapter; private Playlist mPlaylist; private PlaylistListView mListViewContents; private FloatingActionButton mPlayFab; private PlayPauseDrawable mFabDrawable; private boolean mFabShouldResume; private Handler mHandler; private CircularProgressButton mOfflineBtn; private RecyclingBitmapDrawable mLogoBitmap; private ImageView mIvHero; private ImageView mIvSource; private TextView mTvPlaylistName; private boolean mIsSpecialPlaylist; private static class PlaylistViewHandler extends Handler { private WeakReference<PlaylistViewFragment> mParent; public PlaylistViewHandler(PlaylistViewFragment parent) { mParent = new WeakReference<>(parent); } @Override public void handleMessage(Message msg) { PlaylistViewFragment parent = mParent.get(); if (parent == null) { return; } if (msg.what == UPDATE_OFFLINE_STATUS) { parent.updateOfflineStatus(); } else if (msg.what == UPDATE_DATA_SET) { parent.mAdapter.notifyDataSetChanged(); parent.mTvPlaylistName.setText(parent.mPlaylist.getName()); } } } private BasePlaybackCallback mPlaybackCallback = new BasePlaybackCallback() { @Override public void onSongStarted(final boolean buffering, final Song s) throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { mAdapter.notifyDataSetChanged(); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(buffering); setReelBarTitle(s.getTitle()); mBarDrawable.setBuffering(buffering); mBarDrawable.setShape(mFabDrawable.getRequestedShape()); } }); } @Override public void onPlaybackResume() throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(false); mBarDrawable.setBuffering(false); mBarDrawable.setShape(mFabDrawable.getRequestedShape()); } }); } @Override public void onPlaybackPause() throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PLAY); mFabDrawable.setBuffering(false); mBarDrawable.setBuffering(false); mBarDrawable.setShape(mFabDrawable.getRequestedShape()); } }); } }; private View.OnClickListener mReelFabClickListener = new View.OnClickListener() { @Override public void onClick(View v) { if (mBarDrawable.getCurrentShape() == PlayPauseDrawable.SHAPE_PLAY) { if (mFabShouldResume) { PlaybackProxy.play(); mBarDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); } else { PlaybackProxy.playPlaylist(mPlaylist); } } else { mFabShouldResume = true; PlaybackProxy.pause(); mBarDrawable.setShape(PlayPauseDrawable.SHAPE_PLAY); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PLAY); hideMaterialReelBar(mPlayFab); } } }; /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @return A new instance of fragment PlaylistViewFragment. */ public static PlaylistViewFragment newInstance(Playlist p) { PlaylistViewFragment fragment = new PlaylistViewFragment(); Bundle bundle = new Bundle(); bundle.putParcelable(KEY_PLAYLIST, p); fragment.setArguments(bundle); return fragment; } public PlaylistViewFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler = new PlaylistViewHandler(this); Bundle args = getArguments(); if (args == null) { throw new IllegalArgumentException("This fragment must have a valid playlist"); } // Get the playlist from the arguments, from the instantiation, and from the cache final String playlistRef = args.getString(KEY_PLAYLIST); if (AutoPlaylistHelper.REF_SPECIAL_FAVORITES.equals(playlistRef)) { mPlaylist = AutoPlaylistHelper.getFavoritesPlaylist(getActivity()); mIsSpecialPlaylist = true; } else if (AutoPlaylistHelper.REF_SPECIAL_MOST_PLAYED.equals(playlistRef)) { mPlaylist = AutoPlaylistHelper.getMostPlayedPlaylist(getActivity()); mIsSpecialPlaylist = true; } else { final ProviderAggregator aggregator = ProviderAggregator.getDefault(); mPlaylist = aggregator.retrievePlaylist(playlistRef, null); mIsSpecialPlaylist = false; } if (mPlaylist == null) { Log.e(TAG, "Playlist is null (not in cache, aborting)"); // TODO: Wait for playlist to be loaded, eventually Activity act = getActivity(); if (act != null) { act.finish(); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_playlist_view, container, false); assert root != null; if (mPlaylist == null) { // If playlist couldn't load, abort early return root; } mListViewContents = (PlaylistListView) root.findViewById(R.id.lvPlaylistContents); // Setup the parallaxed header View headerView = inflater.inflate(R.layout.header_listview_songs, mListViewContents, false); mListViewContents.addParallaxedHeaderView(headerView); mAdapter = new PlaylistAdapter(); mListViewContents.setAdapter(mAdapter); mListViewContents.setOnScrollListener(new ScrollStatusBarColorListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (view.getChildCount() == 0 || getActivity() == null) { return; } final float heroHeight = mIvHero.getMeasuredHeight(); final float scrollY = getScroll(view); final float toolbarBgAlpha = Math.min(1, scrollY / heroHeight); final int toolbarAlphaInteger = (((int) (toolbarBgAlpha * 255)) << 24) | 0xFFFFFF; mColorDrawable.setColor(toolbarAlphaInteger & getResources().getColor(R.color.primary)); SpannableString spannableTitle = new SpannableString(mPlaylist.getName() != null ? mPlaylist.getName() : ""); mAlphaSpan.setAlpha(toolbarBgAlpha); ActionBar actionbar = ((AppActivity) getActivity()).getSupportActionBar(); if (actionbar != null) { actionbar.setBackgroundDrawable(mColorDrawable); spannableTitle.setSpan(mAlphaSpan, 0, spannableTitle.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); actionbar.setTitle(spannableTitle); if (Utils.hasLollipop()) { getActivity().getWindow().setStatusBarColor(toolbarAlphaInteger & getResources().getColor(R.color.primary_dark)); } } } }); headerView.findViewById(R.id.pbAlbumLoading).setVisibility(View.GONE); mIvHero = (ImageView) headerView.findViewById(R.id.ivHero); mTvPlaylistName = (TextView) headerView.findViewById(R.id.tvAlbumName); // Download button mOfflineBtn = (CircularProgressButton) headerView.findViewById(R.id.cpbOffline); mOfflineBtn.setAlpha(0.0f); mOfflineBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ProviderIdentifier pi = mPlaylist.getProvider(); IMusicProvider provider = PluginsLookup.getDefault().getProvider(pi).getBinder(); try { if (mPlaylist.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_NO) { provider.setPlaylistOfflineMode(mPlaylist.getRef(), true); mOfflineBtn.setIndeterminateProgressMode(true); mOfflineBtn.setProgress(1); if (ProviderAggregator.getDefault().isOfflineMode()) { Toast.makeText(getActivity(), R.string.toast_offline_playlist_sync, Toast.LENGTH_SHORT).show(); } } else { provider.setPlaylistOfflineMode(mPlaylist.getRef(), false); mOfflineBtn.setProgress(0); } } catch (RemoteException e) { Log.e(TAG, "Cannot set this playlist to offline mode", e); mOfflineBtn.setProgress(-1); } } }); mHandler.sendEmptyMessageDelayed(UPDATE_OFFLINE_STATUS, 300); mTvPlaylistName.setText(mPlaylist.getName()); Bitmap hero = Utils.dequeueBitmap(PlaylistActivity.BITMAP_PLAYLIST_HERO); if (hero == null) { mIvHero.setImageResource(R.drawable.album_placeholder); } else { mIvHero.setImageBitmap(hero); // Request the higher resolution loadArt(); } mPlayFab = (FloatingActionButton) headerView.findViewById(R.id.fabPlay); // Set source logo mIvSource = (ImageView) headerView.findViewById(R.id.ivSourceLogo); mLogoBitmap = PluginsLookup.getDefault().getCachedLogo(getResources(), mPlaylist); mIvSource.setImageDrawable(mLogoBitmap); // Set the FAB animated drawable mFabDrawable = new PlayPauseDrawable(getResources(), 1); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PLAY); mFabDrawable.setYOffset(6); mPlayFab.setImageDrawable(mFabDrawable); mPlayFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mFabDrawable.getCurrentShape() == PlayPauseDrawable.SHAPE_PLAY) { if (mFabShouldResume) { PlaybackProxy.play(); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(true); } else { playNow(); } if (Utils.hasLollipop()) { showMaterialReelBar(mPlayFab); } } else { mFabShouldResume = true; PlaybackProxy.pause(); mFabDrawable.setBuffering(true); } } }); // Fill the playlist mAdapter.setPlaylist(mPlaylist); // Set the list listener mListViewContents.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Song song = mAdapter.getItem(i - 1); if (Utils.canPlaySong(song)) { PlaybackProxy.clearQueue(); PlaybackProxy.queuePlaylist(mPlaylist, false); PlaybackProxy.playAtIndex(i - 1); // Update FAB mFabShouldResume = true; mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(true); if (Utils.hasLollipop()) { showMaterialReelBar(mPlayFab); } } } }); // Set the display animation AlphaAnimation anim = new AlphaAnimation(0.f, 1.f); anim.setDuration(200); mListViewContents.setLayoutAnimation(new LayoutAnimationController(anim)); setupMaterialReelBar(root, mReelFabClickListener); // Setup the opening animations for non-Lollipop devices if (!Utils.hasLollipop()) { mTvPlaylistName.setVisibility(View.VISIBLE); mTvPlaylistName.setAlpha(0.0f); mTvPlaylistName.animate().alpha(1.0f).setDuration(AlbumActivity.BACK_DELAY).start(); } mIvSource.setAlpha(0.0f); mIvSource.animate().alpha(1).setDuration(200).start(); return root; } @Override public void onResume() { super.onResume(); ProviderAggregator.getDefault().addUpdateCallback(PlaylistViewFragment.this); PlaybackProxy.addCallback(mPlaybackCallback); updateFabStatus(); // Current track might have changed, so update it mAdapter.notifyDataSetChanged(); Song currentSong = PlaybackProxy.getCurrentTrack(); if (currentSong != null) { setReelBarTitle(currentSong.getTitle()); } } @Override public void onPause() { super.onPause(); ProviderAggregator.getDefault().removeUpdateCallback(this); PlaybackProxy.removeCallback(mPlaybackCallback); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.playlist, menu); if (mIsSpecialPlaylist) { // Remove some options not applicable to the special playlist mode menu.removeItem(R.id.menu_remove_duplicates); menu.removeItem(R.id.menu_remove_playlist); menu.removeItem(R.id.menu_rename_playlist); } } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menu_play_now) { playNow(); return true; } else if (item.getItemId() == R.id.menu_play_next) { playNext(); return true; } else if (item.getItemId() == R.id.menu_add_to_queue) { PlaybackProxy.queuePlaylist(mPlaylist, false); return true; } else if (item.getItemId() == R.id.menu_remove_duplicates) { try { removeDuplicates(0); } catch (RemoteException e) { Log.e(TAG, "Cannot remove duplicates", e); } return true; } else if (item.getItemId() == R.id.menu_remove_playlist) { removePlaylistDialog(); return true; } else if (item.getItemId() == R.id.menu_rename_playlist) { renamePlaylistDialog(); return true; } else if (item.getItemId() == R.id.menu_add_to_playlist) { addToPlaylist(); } return false; } public View findViewById(int id) { View root = getView(); if (root != null) { return root.findViewById(id); } else { return null; } } private void addToPlaylist() { PlaylistChooserFragment fragment = PlaylistChooserFragment.newInstance(mPlaylist); fragment.show(getActivity().getSupportFragmentManager(), mPlaylist.getRef()); } private void renamePlaylistDialog() { AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); // Set an EditText view to get user input final EditText input = new EditText(getActivity()); input.setText(mPlaylist.getName()); alert.setView(input); alert.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); ProviderConnection conn = PluginsLookup.getDefault().getProvider(mPlaylist.getProvider()); if (conn != null) { IMusicProvider provider = conn.getBinder(); if (provider != null) { try { provider.renamePlaylist(mPlaylist.getRef(), value); } catch (RemoteException e) { Log.e(TAG, "Cannot rename playlist", e); } catch (Exception e) { Sentry.captureException(new Exception("Rename playlist: Plugin error", e)); } } } } }); alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Canceled. } }); alert.show(); } private void removePlaylistDialog() { new AlertDialog.Builder(getActivity()) .setTitle(R.string.remove_playlist_dialog_title) .setMessage(getString(R.string.remove_playlist_dialog_msg, mPlaylist.getName())) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ProviderConnection conn = PluginsLookup.getDefault().getProvider(mPlaylist.getProvider()); if (conn != null) { IMusicProvider provider = conn.getBinder(); if (provider != null) { try { provider.deletePlaylist(mPlaylist.getRef()); } catch (Exception e) { Log.e(TAG, "Cannot remove playlist", e); } } } getActivity().finish(); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).show(); } private void updateFabStatus() { final int state = PlaybackProxy.getState(); switch (state) { case PlaybackService.STATE_PAUSED: case PlaybackService.STATE_STOPPED: mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PLAY); mFabDrawable.setBuffering(false); break; case PlaybackService.STATE_BUFFERING: case PlaybackService.STATE_PAUSING: mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(true); break; case PlaybackService.STATE_PLAYING: mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(false); break; } } public void notifyReturnTransition() { if (Utils.hasLollipop()) { Utils.animateScale(mPlayFab, true, false); mIvSource.setAlpha(1.0f); mIvSource.animate().alpha(0).setDuration(200).start(); } } public ImageView getHeroImageView() { return mIvHero; } private void loadArt() { new Thread() { public void run() { AlbumArtHelper.retrieveAlbumArt(getResources(), new AlbumArtHelper.AlbumArtListener() { @Override public void onArtLoaded(RecyclingBitmapDrawable output, BoundEntity request) { if (output != null) { Drawable originalDrawable = mIvHero.getDrawable(); if (originalDrawable == null) { originalDrawable = getResources().getDrawable(R.drawable.album_placeholder); } final TransitionDrawable transition = new TransitionDrawable(new Drawable[]{ originalDrawable, output }); // Make sure the transition happens after the activity animation is done, // otherwise weird sliding occurs. mHandler.postDelayed(new Runnable() { @Override public void run() { mIvHero.setImageDrawable(transition); transition.startTransition(500); } }, 600); } } }, mPlaylist, -1, false); } }.start(); } private void playNow() { PlaybackProxy.playPlaylist(mPlaylist); mFabDrawable.setShape(PlayPauseDrawable.SHAPE_PAUSE); mFabDrawable.setBuffering(true); if (Utils.hasLollipop()) { showMaterialReelBar(mPlayFab); } } private void playNext() { // playNext adds elements after the current playing one. If we want to play the playlist // in the proper order, we need to put it backwards. ListIterator<String> it = mPlaylist.songsList().listIterator(); while (it.hasNext()) { it.next(); } final ProviderAggregator aggregator = ProviderAggregator.getDefault(); while (it.hasPrevious()) { PlaybackProxy.playNext(aggregator.retrieveSong(it.previous(), mPlaylist.getProvider())); } } private void removeDuplicates(int count) throws RemoteException { if (count > 1000) { // Watchdog return; } // Process each track and look for the same track List<String> songsIt = new ArrayList<>(mPlaylist.songsList()); List<String> knownTracks = new ArrayList<>(); // Only process if the provider is up ProviderConnection conn = PluginsLookup.getDefault().getProvider(mPlaylist.getProvider()); if (conn != null) { IMusicProvider provider = conn.getBinder(); if (provider != null) { int position = 0; for (String songRef : songsIt) { // If we know the track, remove it (it's the second occurrence of the track). // Else, add it to the known list and move on. if (knownTracks.contains(songRef)) { // Delete the song and restart the process provider.deleteSongFromPlaylist(position, mPlaylist.getRef()); removeDuplicates(count + 1); return; } else { knownTracks.add(songRef); } ++position; } } } } private void updateOfflineStatus() { if (mPlaylist == null) { Log.e(TAG, "Calling updateOfflineStatus when mPlaylist is null!"); return; } // We bias the playlist status based on the current offline mode ProviderAggregator aggregator = ProviderAggregator.getDefault(); final int offlineStatus = mPlaylist.getOfflineStatus(); switch (offlineStatus) { case BoundEntity.OFFLINE_STATUS_NO: mOfflineBtn.setProgress(0); break; case BoundEntity.OFFLINE_STATUS_READY: mOfflineBtn.setProgress(100); break; case BoundEntity.OFFLINE_STATUS_ERROR: mOfflineBtn.setProgress(-1); break; case BoundEntity.OFFLINE_STATUS_PENDING: mOfflineBtn.setProgress(50); mOfflineBtn.setIndeterminateProgressMode(true); break; case BoundEntity.OFFLINE_STATUS_DOWNLOADING: if (aggregator.isOfflineMode()) { mOfflineBtn.setProgress(50); mOfflineBtn.setIndeterminateProgressMode(true); } else { mOfflineBtn.setIndeterminateProgressMode(false); float numSyncTracks = getNumSyncTracks(); float numTracksToSync = 0; // Count the number of tracks to sync (ie. num of tracks available) final Iterator<String> songs = mPlaylist.songs(); while (songs.hasNext()) { String ref = songs.next(); Song s = aggregator.retrieveSong(ref, mPlaylist.getProvider()); if (s != null && s.isAvailable()) { ++numTracksToSync; } } mOfflineBtn.setProgress(Math.min(100, numSyncTracks * 100.0f / numTracksToSync + 0.1f)); } break; } if (mPlaylist.isLoaded() && mPlaylist.isOfflineCapable()) { if (mOfflineBtn.getAlpha() != 1) { mOfflineBtn.animate().alpha(1.0f).setStartDelay(500).setDuration(300).start(); } } else { if (mOfflineBtn.getAlpha() != 0) { mOfflineBtn.animate().alpha(0.0f).setStartDelay(500).setDuration(300).start(); } } } private int getNumSyncTracks() { final Iterator<String> it = mPlaylist.songs(); final ProviderAggregator aggregator = ProviderAggregator.getDefault(); int numSync = 0; while (it.hasNext()) { final String songRef = it.next(); Song song = aggregator.retrieveSong(songRef, mPlaylist.getProvider()); if (song != null && song.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_READY) { numSync++; } } Log.d(TAG, "Num sync tracks: " + numSync); return numSync; } @Override public void onSongUpdate(List<Song> s) { // We check if the song belongs to this playlist boolean hasPlaylist = false; Iterator<String> songsRef = mPlaylist.songs(); while (songsRef.hasNext()) { String ref = songsRef.next(); for (Song song : s) { if (song.getRef().equals(ref)) { hasPlaylist = true; break; } } if (hasPlaylist) { break; } } // It does, update the list then if (hasPlaylist) { if (!mHandler.hasMessages(UPDATE_DATA_SET)) { mHandler.sendEmptyMessage(UPDATE_DATA_SET); } if (!mHandler.hasMessages(UPDATE_OFFLINE_STATUS)) { mHandler.sendEmptyMessage(UPDATE_OFFLINE_STATUS); } } } @Override public void onAlbumUpdate(List<Album> a) { } @Override public void onPlaylistUpdate(final List<Playlist> p) { // If the currently watched playlist is updated, update me if (p.contains(mPlaylist)) { if (!mHandler.hasMessages(UPDATE_DATA_SET)) { mHandler.sendEmptyMessage(UPDATE_DATA_SET); } if (!mHandler.hasMessages(UPDATE_OFFLINE_STATUS)) { mHandler.sendEmptyMessage(UPDATE_OFFLINE_STATUS); } } } @Override public void onPlaylistRemoved(String ref) { } @Override public void onArtistUpdate(List<Artist> a) { // We check if the artists belongs to this playlist boolean hasPlaylist = false; Iterator<String> songsRef = mPlaylist.songs(); final ProviderAggregator aggregator = ProviderAggregator.getDefault(); while (songsRef.hasNext()) { String ref = songsRef.next(); Song song = aggregator.retrieveSong(ref, mPlaylist.getProvider()); for (Artist artist : a) { if (song != null && artist.getRef().equals(song.getArtist())) { hasPlaylist = true; break; } } if (hasPlaylist) { break; } } // It does, update the list then if (hasPlaylist) { if (!mHandler.hasMessages(UPDATE_DATA_SET)) { mHandler.sendEmptyMessage(UPDATE_DATA_SET); } } } @Override public void onProviderConnected(IMusicProvider provider) { } @Override public void onSearchResult(List<SearchResult> searchResult) { } }