aboutsummaryrefslogtreecommitdiff
path: root/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
diff options
context:
space:
mode:
authorNicole Borrelli <borrelli@google.com>2017-02-28 14:14:58 -0800
committerNicole Borrelli <borrelli@google.com>2017-05-04 14:10:27 -0700
commit2f144e3d4d6cc18b79f79a962aa8cd790d74b412 (patch)
treeaa32def7148d626aa54c13f2390ea5d0b5e025bc /media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
parent83a1ddaa52c27e8edefe93a68b91c7e26668d0e4 (diff)
downloadandroid-2f144e3d4d6cc18b79f79a962aa8cd790d74b412.tar.gz
Vastly simplify the MediaBrowserService sample.
Bug: 37631425 Test: Existing tests pass. Manually verified. Change-Id: Id7f86295e1c8d7372b4f79b3e511a736190792bf
Diffstat (limited to 'media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java')
-rw-r--r--media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java208
1 files changed, 69 insertions, 139 deletions
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
index 949b0bdc..7eead436 100644
--- a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
@@ -16,11 +16,9 @@
package com.example.android.mediabrowserservice.model;
-import android.media.MediaMetadata;
import android.os.AsyncTask;
import android.support.v4.media.MediaMetadataCompat;
-
-import com.example.android.mediabrowserservice.utils.LogHelper;
+import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
@@ -33,26 +31,26 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.LinkedHashMap;
/**
* Utility class to get a list of MusicTrack's based on a server-side JSON
* configuration.
+ *
+ * In a real application this class may pull data from a remote server, as we do here,
+ * or potentially use {@link android.provider.MediaStore} to locate media files located on
+ * the device.
*/
public class MusicProvider {
- private static final String TAG = LogHelper.makeLogTag(MusicProvider.class);
+ private static final String TAG = MusicProvider.class.getSimpleName();
- private static final String CATALOG_URL =
- "http://storage.googleapis.com/automotive-media/music.json";
+ public static final String MEDIA_ID_ROOT = "__ROOT__";
+ public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY__";
- public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
+ private static final String CATALOG_URL =
+ "https://storage.googleapis.com/automotive-media/music.json";
private static final String JSON_MUSIC = "music";
private static final String JSON_TITLE = "title";
@@ -66,108 +64,54 @@ public class MusicProvider {
private static final String JSON_DURATION = "duration";
// Categorized caches for music track data:
- private ConcurrentMap<String, List<MediaMetadata>> mMusicListByGenre;
- private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
-
- private final Set<String> mFavoriteTracks;
+ private final LinkedHashMap<String, MediaMetadataCompat> mMusicListById;
- enum State {
+ private enum State {
NON_INITIALIZED, INITIALIZING, INITIALIZED
}
private volatile State mCurrentState = State.NON_INITIALIZED;
+ /**
+ * Callback used by MusicService.
+ */
public interface Callback {
void onMusicCatalogReady(boolean success);
}
public MusicProvider() {
- mMusicListByGenre = new ConcurrentHashMap<>();
- mMusicListById = new ConcurrentHashMap<>();
- mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+ mMusicListById = new LinkedHashMap<>();
}
- /**
- * Get an iterator over the list of genres
- *
- * @return genres
- */
- public Iterable<String> getGenres() {
- if (mCurrentState != State.INITIALIZED) {
+ public Iterable<MediaMetadataCompat> getAllMusics() {
+ if (mCurrentState != State.INITIALIZED || mMusicListById.isEmpty()) {
return Collections.emptyList();
}
- return mMusicListByGenre.keySet();
+ return mMusicListById.values();
}
/**
- * Get music tracks of the given genre
- *
- */
- public Iterable<MediaMetadata> getMusicsByGenre(String genre) {
- if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
- return Collections.emptyList();
- }
- return mMusicListByGenre.get(genre);
- }
-
- /**
- * Very basic implementation of a search that filter music tracks which title containing
- * the given query.
+ * Return the MediaMetadata for the given musicID.
*
+ * @param musicId The unique music ID.
*/
- public Iterable<MediaMetadata> searchMusic(String titleQuery) {
- if (mCurrentState != State.INITIALIZED) {
- return Collections.emptyList();
- }
- ArrayList<MediaMetadata> result = new ArrayList<>();
- titleQuery = titleQuery.toLowerCase(Locale.US);
- for (MutableMediaMetadata track : mMusicListById.values()) {
- if (track.metadata.getString(MediaMetadata.METADATA_KEY_TITLE).toLowerCase(Locale.US)
- .contains(titleQuery)) {
- result.add(track.metadata);
- }
- }
- return result;
+ public MediaMetadataCompat getMusic(String musicId) {
+ return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId) : null;
}
/**
- * Return the MediaMetadata for the given musicID.
- *
- * @param musicId The unique, non-hierarchical music ID.
+ * Update the metadata associated with a musicId. If the musicId doesn't exist, the
+ * update is dropped. (That is, it does not create a new mediaId.)
+ * @param musicId The ID
+ * @param metadata New Metadata to associate with it
*/
- public MediaMetadata getMusic(String musicId) {
- return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null;
- }
-
- public synchronized void updateMusic(String musicId, MediaMetadata metadata) {
- MutableMediaMetadata track = mMusicListById.get(musicId);
- if (track == null) {
- return;
- }
-
- String oldGenre = track.metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
- String newGenre = metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
-
- track.metadata = metadata;
-
- // if genre has changed, we need to rebuild the list by genre
- if (!oldGenre.equals(newGenre)) {
- buildListsByGenre();
+ public synchronized void updateMusic(String musicId, MediaMetadataCompat metadata) {
+ MediaMetadataCompat track = mMusicListById.get(musicId);
+ if (track != null) {
+ mMusicListById.put(musicId, metadata);
}
}
- public void setFavorite(String musicId, boolean favorite) {
- if (favorite) {
- mFavoriteTracks.add(musicId);
- } else {
- mFavoriteTracks.remove(musicId);
- }
- }
-
- public boolean isFavorite(String musicId) {
- return mFavoriteTracks.contains(musicId);
- }
-
public boolean isInitialized() {
return mCurrentState == State.INITIALIZED;
}
@@ -177,9 +121,9 @@ public class MusicProvider {
* for future reference, keying tracks by musicId and grouping by genre.
*/
public void retrieveMediaAsync(final Callback callback) {
- LogHelper.d(TAG, "retrieveMediaAsync called");
+ Log.d(TAG, "retrieveMediaAsync called");
if (mCurrentState == State.INITIALIZED) {
- // Nothing to do, execute callback immediately
+ // Already initialized, so call back immediately.
callback.onMusicCatalogReady(true);
return;
}
@@ -201,21 +145,6 @@ public class MusicProvider {
}.execute();
}
- private synchronized void buildListsByGenre() {
- ConcurrentMap<String, List<MediaMetadata>> newMusicListByGenre = new ConcurrentHashMap<>();
-
- for (MutableMediaMetadata m : mMusicListById.values()) {
- String genre = m.metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
- List<MediaMetadata> list = newMusicListByGenre.get(genre);
- if (list == null) {
- list = new ArrayList<>();
- newMusicListByGenre.put(genre, list);
- }
- list.add(m.metadata);
- }
- mMusicListByGenre = newMusicListByGenre;
- }
-
private synchronized void retrieveMedia() {
try {
if (mCurrentState == State.NON_INITIALIZED) {
@@ -229,17 +158,16 @@ public class MusicProvider {
}
JSONArray tracks = jsonObj.getJSONArray(JSON_MUSIC);
if (tracks != null) {
- for (int j = 0; j < tracks.length(); j++) {
- MediaMetadata item = buildFromJSON(tracks.getJSONObject(j), path);
- String musicId = item.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
- mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item));
+ for (int j = tracks.length() - 1; j >= 0; j--) {
+ MediaMetadataCompat item = buildFromJSON(tracks.getJSONObject(j), path);
+ String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
+ mMusicListById.put(musicId, item);
}
- buildListsByGenre();
}
mCurrentState = State.INITIALIZED;
}
- } catch (JSONException e) {
- LogHelper.e(TAG, e, "Could not retrieve music list");
+ } catch (JSONException jsonException) {
+ Log.e(TAG, "Could not retrieve music list", jsonException);
} finally {
if (mCurrentState != State.INITIALIZED) {
// Something bad happened, so we reset state to NON_INITIALIZED to allow
@@ -249,7 +177,9 @@ public class MusicProvider {
}
}
- private MediaMetadata buildFromJSON(JSONObject json, String basePath) throws JSONException {
+ private MediaMetadataCompat buildFromJSON(JSONObject json, String basePath)
+ throws JSONException {
+
String title = json.getString(JSON_TITLE);
String album = json.getString(JSON_ALBUM);
String artist = json.getString(JSON_ARTIST);
@@ -260,13 +190,13 @@ public class MusicProvider {
int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
int duration = json.getInt(JSON_DURATION) * 1000; // ms
- LogHelper.d(TAG, "Found music track: ", json);
+ Log.d(TAG, "Found music track: " + json);
// Media is stored relative to JSON file
- if (!source.startsWith("http")) {
+ if (!source.startsWith("https")) {
source = basePath + source;
}
- if (!iconUrl.startsWith("http")) {
+ if (!iconUrl.startsWith("https")) {
iconUrl = basePath + iconUrl;
}
// Since we don't have a unique ID in the server, we fake one using the hashcode of
@@ -277,18 +207,17 @@ public class MusicProvider {
// mediaSession.setMetadata) is not a good idea for a real world music app, because
// the session metadata can be accessed by notification listeners. This is done in this
// sample for convenience only.
- //noinspection WrongConstant
- return new MediaMetadata.Builder()
- .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, id)
- .putString(CUSTOM_METADATA_TRACK_SOURCE, source)
- .putString(MediaMetadata.METADATA_KEY_ALBUM, album)
- .putString(MediaMetadata.METADATA_KEY_ARTIST, artist)
- .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
- .putString(MediaMetadata.METADATA_KEY_GENRE, genre)
- .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, iconUrl)
- .putString(MediaMetadata.METADATA_KEY_TITLE, title)
- .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, trackNumber)
- .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, totalTrackCount)
+ return new MediaMetadataCompat.Builder()
+ .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
+ .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, 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();
}
@@ -299,28 +228,29 @@ public class MusicProvider {
* @return result JSONObject containing the parsed representation.
*/
private JSONObject fetchJSONFromUrl(String urlString) {
- InputStream is = null;
+ InputStream inputStream = null;
try {
URL url = new URL(urlString);
URLConnection urlConnection = url.openConnection();
- is = new BufferedInputStream(urlConnection.getInputStream());
+ inputStream = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream(), "iso-8859-1"));
- StringBuilder sb = new StringBuilder();
+ StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
- sb.append(line);
+ stringBuilder.append(line);
}
- return new JSONObject(sb.toString());
- } catch (Exception e) {
- LogHelper.e(TAG, "Failed to parse the json for media list", e);
+ return new JSONObject(stringBuilder.toString());
+ } catch (IOException | JSONException exception) {
+ Log.e(TAG, "Failed to parse the json for media list", exception);
return null;
} finally {
- if (is != null) {
+ // If the inputStream was opened, try to close it now.
+ if (inputStream != null) {
try {
- is.close();
- } catch (IOException e) {
- // ignore
+ inputStream.close();
+ } catch (IOException ignored) {
+ // Ignore the exception since there's nothing left to do with the stream
}
}
}