/** Called by the providers when the details of a song have been updated. */ @Override public void onSongUpdate(ProviderIdentifier provider, final Song s) throws RemoteException { if (s == null) { Log.w(TAG, "Provider " + provider.mName + " sent in a null songUpdate"); return; } try { Song cached = mCache.getSong(s.getRef()); boolean wasLoaded = false; boolean changed = false; if (cached == null) { mCache.putSong(provider, s); changed = true; cached = s; } else { wasLoaded = cached.isLoaded(); if (s.isLoaded() && !cached.isIdentical(s)) { cached.setAlbum(s.getAlbum()); cached.setArtist(s.getArtist()); cached.setSourceLogo(s.getLogo()); cached.setDuration(s.getDuration()); cached.setTitle(s.getTitle()); cached.setYear(s.getYear()); cached.setOfflineStatus(s.getOfflineStatus()); cached.setAvailable(s.isAvailable()); cached.setIsLoaded(s.isLoaded()); changed = true; } } if (!wasLoaded && cached.isLoaded()) { // Match the album with the artist Artist artist = mCache.getArtist(s.getArtist()); if (artist == null && s.getArtist() != null) { artist = retrieveArtist(s.getArtist(), provider); } if (artist != null) { Album album = mCache.getAlbum(s.getAlbum()); if (album == null && s.getAlbum() != null) { album = retrieveAlbum(s.getAlbum(), provider); } if (album != null) { artist.addAlbum(album.getRef()); } } } if (changed) { postSongForUpdate(cached); } } catch (Exception e) { Log.e(TAG, "Exception while updating song data", e); } }
/** * Retrieves an album from the provider, and put it in the cache * * @param ref The reference to the album * @param provider The provider from which retrieve the album * @return The album, or null if the provider says so */ public Playlist retrievePlaylist(final String ref, final ProviderIdentifier provider) { if (ref == null) { // Force get stack trace try { throw new RuntimeException(); } catch (RuntimeException e) { Log.e(TAG, "retrievePlaylist called with a null reference", e); } return null; } // Try from cache Playlist output = mCache.getPlaylist(ref); if (output == null && provider != null) { ProviderConnection pc = PluginsLookup.getDefault().getProvider(provider); if (pc != null) { IMusicProvider binder = pc.getBinder(); if (binder != null) { try { output = binder.getPlaylist(ref); onPlaylistAddedOrUpdated(provider, output); } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve the playlist", e); } } } } return output; }
/** * Retrieves an album from the provider, and put it in the cache * * @param ref The reference to the album * @param provider The provider from which retrieve the album * @return The album, or null if the provider says so */ public Album retrieveAlbum(final String ref, final ProviderIdentifier provider) { if (ref == null) { // Force get stack trace try { throw new RuntimeException(); } catch (RuntimeException e) { Log.e(TAG, "retrieveAlbum called with a null reference", e); } return null; } // Try from cache Album output = mCache.getAlbum(ref); if (output == null && provider != null) { ProviderConnection pc = PluginsLookup.getDefault().getProvider(provider); if (pc != null) { IMusicProvider binder = pc.getBinder(); if (binder != null) { try { output = binder.getAlbum(ref); onAlbumUpdate(provider, output); } catch (DeadObjectException e) { Log.e(TAG, "Provider died while retrieving album"); } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve the album", e); } } } } return output; }
/** * Called by the provider if a playlist has been removed from the user playlists container. * * @param provider The provider * @param ref The reference of the playlist that has been removed * @throws RemoteException */ @Override public void onPlaylistRemoved(ProviderIdentifier provider, String ref) throws RemoteException { if (ref != null) { mCache.removePlaylist(ref); } synchronized (mUpdateCallbacks) { for (ILocalCallback cb : mUpdateCallbacks) { cb.onPlaylistRemoved(ref); } } }
/** Called by the providers when the details of an artist have been updated. */ @Override public void onArtistUpdate(ProviderIdentifier provider, Artist a) throws RemoteException { if (a == null) { Log.w(TAG, "Provider returned a null artist"); return; } Artist cached = mCache.getArtist(a.getRef()); if (cached == null) { mCache.putArtist(provider, a); postArtistForUpdate(a); } else if (!cached.isIdentical(a)) { cached.setName(a.getName()); Iterator<String> it = a.albums(); while (it.hasNext()) { cached.addAlbum(it.next()); } cached.setIsLoaded(a.isLoaded()); postArtistForUpdate(a); } }
/** * Retrieves a song from the provider, and put it in the cache * * @param ref The reference to the song * @param provider The provider from which retrieve the song (may be null to query cache only) * @return The song, or null if the provider says so */ public Song retrieveSong(final String ref, final ProviderIdentifier provider) { if (ref == null) { // Force get stack trace try { throw new RuntimeException(); } catch (RuntimeException e) { Log.e(TAG, "retrieveSong called with a null reference", e); } return null; } // Try from cache Song output = mCache.getSong(ref); if (output == null && provider != null) { // Get from provider then ProviderConnection pc = PluginsLookup.getDefault().getProvider(provider); if (pc != null) { IMusicProvider binder = pc.getBinder(); if (binder != null) { try { output = binder.getSong(ref); if (output != null) { onSongUpdate(provider, output); } } catch (DeadObjectException e) { Log.e(TAG, "Provider died while retrieving song"); return null; } catch (RemoteException e) { Log.e(TAG, "Unable to retrieve the song", e); return null; } } else { if (DEBUG) Log.e(TAG, "Binder null: provider not yet connected?"); } } else { Log.e(TAG, "Unknown provider identifier: " + provider); } } if (output == null && provider != null) { Log.d(TAG, "Unable to get song " + ref + " from " + provider.mName); } return output; }
/** Called by the providers when the details of an album have been updated. */ @Override public void onAlbumUpdate(ProviderIdentifier provider, final Album a) throws RemoteException { if (a == null) { Log.w(TAG, "Provider returned a null album"); return; } Album cached = mCache.getAlbum(a.getRef()); boolean modified = false; // See IProviderCallback.aidl in providerlib for more info about the logic of updating // the Album objects if (cached == null) { mCache.putAlbum(provider, a); cached = a; modified = true; } else if (!cached.isLoaded() || !cached.isIdentical(a)) { cached.setName(a.getName()); cached.setYear(a.getYear()); cached.setIsLoaded(a.isLoaded()); cached.setProvider(a.getProvider()); if (cached.getSongsCount() != a.getSongsCount()) { Iterator<String> songsIt = a.songs(); while (songsIt.hasNext()) { String songRef = songsIt.next(); cached.addSong(songRef); } } modified = true; } if (cached.getProvider() == null) { Log.e(TAG, "Provider for " + cached.getRef() + " is null!"); } if (modified) { // Add the album to each artist of the song (once) Iterator<String> songs = a.songs(); while (songs.hasNext()) { String songRef = songs.next(); Song song = retrieveSong(songRef, a.getProvider()); if (song != null && song.isLoaded()) { String artistRef = song.getArtist(); if (artistRef != null) { Artist artist = retrieveArtist(artistRef, song.getProvider()); if (artist != null) { artist.addAlbum(a.getRef()); } else { if (DEBUG) Log.e(TAG, "Artist is null!"); } } } else { if (DEBUG) Log.e(TAG, "Song is null!"); } } postAlbumForUpdate(cached); } }
/** * Called by the providers when a Playlist has been added or updated. The app's providers * syndicator will automatically update the local cache of playlists based on the playlist name. */ @Override public void onPlaylistAddedOrUpdated(final ProviderIdentifier provider, final Playlist p) throws RemoteException { if (p == null || p.getRef() == null) { Log.w(TAG, "Provider returned a null playlist or a null-ref playlist"); return; } try { // We compare the provided copy with the one we have in cache. We only notify the callbacks // if it indeed changed. Playlist cached = mCache.getPlaylist(p.getRef()); boolean notify; if (cached == null) { mCache.putPlaylist(provider, p); cached = p; notify = true; } else { notify = !cached.isIdentical(p); // If the playlist isn't identical, update it if (notify) { // Update the name cached.setName(p.getName()); if (p.getName() == null) { Log.w(TAG, "Playlist " + p.getRef() + " updated, but name is null!"); } cached.setIsLoaded(p.isLoaded()); // Empty the playlist while (cached.getSongsCount() > 0) { cached.removeSong(0); } // Re-add the songs to it Iterator<String> songIt = p.songs(); while (songIt.hasNext()) { cached.addSong(songIt.next()); } // Set offline information cached.setOfflineCapable(p.isOfflineCapable()); cached.setOfflineStatus(p.getOfflineStatus()); } } final Playlist finalCachedPlaylist = cached; // If something has actually changed if (notify) { mExecutor.execute( new Runnable() { @Override public void run() { // First, we try to check if we need information for some of the songs // TODO(xplodwild): Is this really needed in a properly designed provider? Iterator<String> it = finalCachedPlaylist.songs(); while (it.hasNext()) { String ref = it.next(); retrieveSong(ref, provider); } // Then we notify the callbacks postPlaylistForUpdate(finalCachedPlaylist); } }); } } catch (Exception e) { Log.e(TAG, "FUUUU", e); } }
public List<Playlist> getAllMultiProviderPlaylists() { return mCache.getAllMultiProviderPlaylists(); }
/** * Returns the list of all cached playlists. At the same time, providers will be called for * updates and/or fetching playlists, and LocalCallbacks will be called when providers notify this * class of eventual new entries. * * @return A list of playlists */ public List<Playlist> getAllPlaylists() { mBackHandler.removeCallbacks(mUpdatePlaylistsRunnable); mBackHandler.post(mUpdatePlaylistsRunnable); return mCache.getAllPlaylists(); }