Here is the Google sample app. It's set up to pull metadata from a URL with a JSON. I would like to know how to have Firebase be my source.
Here is my attempt in changing the RemoteJSONSource class:
package com.mm.android.uamp.model;
import android.support.v4.media.MediaMetadataCompat;
import android.util.Log;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.mm.android.uamp.utils.LogHelper;
import java.util.ArrayList;
import java.util.Iterator;
public class RemoteJSONSource implements MusicProviderSource {
private static final String TAG = LogHelper.makeLogTag(RemoteJSONSource.class);
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference mMusic = mRootRef.child("music");
ArrayList<MediaMetadataCompat> tracksFromFB = new ArrayList<>();
public void buildFromFirebase(){
mMusic.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot music : dataSnapshot.getChildren()){
String title = music.child("title").getValue(String.class);
String album = music.child("album").getValue(String.class);
String artist = music.child("artist").getValue(String.class);
String genre = music.child("genre").getValue(String.class);
String source = music.child("source").getValue(String.class);
String id = String.valueOf(source.hashCode());
String iconUrl = music.child("image").getValue(String.class);
int trackNumber = music.child("trackNumber").getValue(Integer.class);
int totalTrackCount = music.child("totalTrackCount").getValue(Integer.class);
int duration = music.child("duration").getValue(Integer.class);
MediaMetadataCompat theMetadataFB = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
.putString(MusicProviderSource.CUSTOM_METADATA_TRACK_SOURCE, source)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
.putString(MediaMetadataCompat.METADATA_KEY_GENRE, genre)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, iconUrl)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, trackNumber)
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, totalTrackCount)
.build();
tracksFromFB.add(theMetadataFB);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
}
@Override
public Iterator<MediaMetadataCompat> iterator() {
buildFromFirebase();
ArrayList<MediaMetadataCompat> tracksFB = tracksFromFB;
return tracksFB.iterator();
}
}
The firebase onDataChange is asynchronous so I think it hasn't finished pulling the data yet before the iterator method returns tracksFB.iterator cause tracksFB array is null. Weird thing is when I run in debug mode with a line break on
ArrayList tracksFB = tracksFromFB;
It works. From my research I think I need a callback or some type of pausing task, but I just cant figure it out.
Possible relevant code connected to the iterator method
public interface MusicProviderSource {
String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
Iterator<MediaMetadataCompat> iterator();
}
next
public class MusicProvider {
private static final String TAG = LogHelper.makeLogTag(MusicProvider.class);
private MusicProviderSource mSource;
private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByGenre;
private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
private final Set<String> mFavoriteTracks;
enum State {
NON_INITIALIZED, INITIALIZING, INITIALIZED
}
private volatile State mCurrentState = State.NON_INITIALIZED;
public interface Callback {
void onMusicCatalogReady(boolean success);
}
public MusicProvider() {
this(new RemoteJSONSource());
}
public MusicProvider(MusicProviderSource source) {
mSource = source;
mMusicListByGenre = new ConcurrentHashMap<>();
mMusicListById = new ConcurrentHashMap<>();
mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
public Iterable<String> getGenres() {
if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList();
}
return mMusicListByGenre.keySet();
}
/**
* Get an iterator over a shuffled collection of all songs
*/
public Iterable<MediaMetadataCompat> getShuffledMusic() {
if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList();
}
List<MediaMetadataCompat> shuffled = new ArrayList<>(mMusicListById.size());
for (MutableMediaMetadata mutableMetadata: mMusicListById.values()) {
shuffled.add(mutableMetadata.metadata);
}
Collections.shuffle(shuffled);
return shuffled;
}
/**
* Get music tracks of the given genre
*
*/
public Iterable<MediaMetadataCompat> getMusicsByGenre(String genre) {
if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
return Collections.emptyList();
}
return mMusicListByGenre.get(genre);
}
}
Also the musicService.java in the link above might be relevant. PLEASE help!
There are two ways I can think to do this, but I'm not familiar enough with Firebase to provide working code.
The sample executes iterator()
in an AsyncTask
, expecting it to block until it can provide a response. So the first, and probably easiest, way to fix it would be to cause iterator()
to wait on the data being loaded, or it failing to load. This could be a spinlock or something like wait/notify.
if (!dataloaded) {
try {
wait();
} catch (InterruptedException e) {}
}
ArrayList<MediaMetadataCompat> tracksFB = tracksFromFB;
return tracksFB.iterator();
I'd call buildFromFirebase();
in the constructor though, rather than waiting.
The second option would be to refactor UAMP to have it load the catalog asynchronously. This would be a lot more work, but it may result in a better design in the long run.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.