package com.markzhai.lyrichere.utils;
import android.os.Bundle;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import com.markzhai.lyrichere.VoiceSearchParams;
import com.markzhai.lyrichere.provider.MusicProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import static com.markzhai.lyrichere.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM;
import static com.markzhai.lyrichere.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_ARTIST;
import static com.markzhai.lyrichere.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
import static com.markzhai.lyrichere.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_SEARCH;
/**
* Utility class to help on queue related tasks.
*/
public class QueueHelper {
private static final String TAG = LogUtils.makeLogTag(QueueHelper.class);
public static List<MediaSessionCompat.QueueItem> getPlayingQueue(String mediaId,
MusicProvider musicProvider) {
// extract the browsing hierarchy from the media ID:
String[] hierarchy = MediaIDHelper.getHierarchy(mediaId);
if (hierarchy.length != 2) {
LogUtils.e(TAG, "Could not build a playing queue for this mediaId: ", mediaId);
return null;
}
String categoryType = hierarchy[0];
String categoryValue = hierarchy[1];
LogUtils.d(TAG, "Creating playing queue for ", categoryType, ", ", categoryValue);
Iterable<MediaMetadataCompat> tracks = null;
// This sample only supports genre and by_search category types.
switch (categoryType) {
case MEDIA_ID_MUSICS_BY_GENRE:
tracks = musicProvider.getMusicsByGenre(categoryValue);
break;
case MEDIA_ID_MUSICS_BY_SEARCH:
tracks = musicProvider.searchMusicBySongTitle(categoryValue);
break;
case MEDIA_ID_MUSICS_BY_ALBUM:
tracks = musicProvider.getMusicsByAlbum(categoryValue);
break;
case MEDIA_ID_MUSICS_BY_ARTIST:
tracks = musicProvider.getMusicsByArtist(categoryValue);
break;
}
if (tracks == null) {
LogUtils.e(TAG, "Unrecognized category type: ", categoryType, " for media ", mediaId);
return null;
}
return convertToQueue(tracks, hierarchy[0], hierarchy[1]);
}
public static List<MediaSessionCompat.QueueItem> getPlayingQueueFromSearch(String query,
Bundle queryParams, MusicProvider musicProvider) {
LogUtils.d(TAG, "Creating playing queue for musics from search: ", query,
" params=", queryParams);
VoiceSearchParams params = new VoiceSearchParams(query, queryParams);
LogUtils.d(TAG, "VoiceSearchParams: ", params);
if (params.isAny) {
// If isAny is true, we will play anything. This is app-dependent, and can be,
// for example, favorite playlists, "I'm feeling lucky", most recent, etc.
return getRandomQueue(musicProvider);
}
Iterable<MediaMetadataCompat> result = null;
if (params.isAlbumFocus) {
result = musicProvider.searchMusicByAlbum(params.album);
} else if (params.isGenreFocus) {
result = musicProvider.getMusicsByGenre(params.genre);
} else if (params.isArtistFocus) {
result = musicProvider.searchMusicByArtist(params.artist);
} else if (params.isSongFocus) {
result = musicProvider.searchMusicBySongTitle(params.song);
}
// If there was no results using media focus parameter, we do an unstructured query.
// This is useful when the user is searching for something that looks like an artist
// to Google, for example, but is not. For example, a user searching for Madonna on
// a PodCast application wouldn't get results if we only looked at the
// Artist (podcast author). Then, we can instead do an unstructured search.
if (params.isUnstructured || result == null || !result.iterator().hasNext()) {
// To keep it simple for this example, we do unstructured searches on the
// song title only. A real world application could search on other fields as well.
result = musicProvider.searchMusicBySongTitle(query);
}
return convertToQueue(result, MEDIA_ID_MUSICS_BY_SEARCH, query);
}
public static int getMusicIndexOnQueue(Iterable<MediaSessionCompat.QueueItem> queue,
String mediaId) {
int index = 0;
for (MediaSessionCompat.QueueItem item : queue) {
if (mediaId.equals(item.getDescription().getMediaId())) {
return index;
}
index++;
}
return -1;
}
public static int getMusicIndexOnQueue(Iterable<MediaSessionCompat.QueueItem> queue,
long queueId) {
int index = 0;
for (MediaSessionCompat.QueueItem item : queue) {
if (queueId == item.getQueueId()) {
return index;
}
index++;
}
return -1;
}
private static List<MediaSessionCompat.QueueItem> convertToQueue(
Iterable<MediaMetadataCompat> tracks, String... categories) {
List<MediaSessionCompat.QueueItem> queue = new ArrayList<>();
int count = 0;
for (MediaMetadataCompat track : tracks) {
// We create a hierarchy-aware mediaID, so we know what the queue is about by looking
// at the QueueItem media IDs.
String hierarchyAwareMediaID = MediaIDHelper.createMediaID(
track.getDescription().getMediaId(), categories);
MediaMetadataCompat trackCopy = new MediaMetadataCompat.Builder(track)
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
.build();
// We don't expect queues to change after created, so we use the item index as the
// queueId. Any other number unique in the queue would work.
MediaSessionCompat.QueueItem item = new MediaSessionCompat.QueueItem(trackCopy.getDescription(), count++);
queue.add(item);
}
return queue;
}
/**
* Create a random queue.
*
* @param musicProvider the provider used for fetching music.
* @return list containing {@link MediaSessionCompat.QueueItem}'s
*/
public static List<MediaSessionCompat.QueueItem> getRandomQueue(MusicProvider musicProvider) {
List<MediaMetadataCompat> result = new ArrayList<>();
for (String genre : musicProvider.getGenres()) {
Iterable<MediaMetadataCompat> tracks = musicProvider.getMusicsByGenre(genre);
for (MediaMetadataCompat track : tracks) {
if (ThreadLocalRandom.current().nextBoolean()) {
result.add(track);
}
}
}
LogUtils.d(TAG, "getRandomQueue: result.size=", result.size());
Collections.shuffle(result);
return convertToQueue(result, MEDIA_ID_MUSICS_BY_SEARCH, "random");
}
public static boolean isIndexPlayable(int index, List<MediaSessionCompat.QueueItem> queue) {
return (queue != null && index >= 0 && index < queue.size());
}
}