1a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/*
2a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Copyright (C) 2015 The Android Open Source Project
3a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
4a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License");
5a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * you may not use this file except in compliance with the License.
6a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * You may obtain a copy of the License at
7a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
8a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *      http://www.apache.org/licenses/LICENSE-2.0
9a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
10a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Unless required by applicable law or agreed to in writing, software
11a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS,
12a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * See the License for the specific language governing permissions and
14a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * limitations under the License.
15a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */
16a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
17a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpackage com.example.android.supportv4.media.model;
18a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
19a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.os.AsyncTask;
20a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.MediaMetadataCompat;
21a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.util.Log;
22a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
23a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport org.json.JSONArray;
24a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport org.json.JSONException;
25a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport org.json.JSONObject;
26a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
27a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.BufferedInputStream;
28a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.BufferedReader;
29a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.IOException;
30a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.InputStream;
31a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.io.InputStreamReader;
32a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.net.URL;
33a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.net.URLConnection;
34a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.ArrayList;
35a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.Collections;
36a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.List;
37a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.Set;
38a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.concurrent.ConcurrentHashMap;
39a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport java.util.concurrent.ConcurrentMap;
40a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
41a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/**
42a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Utility class to get a list of MusicTrack's based on a server-side JSON
43a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * configuration.
44a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */
45a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpublic class MusicProvider {
46a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
47a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String TAG = "MusicProvider";
48a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
49a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String CATALOG_URL =
50a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        "http://storage.googleapis.com/automotive-media/music.json";
51a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
52a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
53a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
54a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_MUSIC = "music";
55a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_TITLE = "title";
56a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_ALBUM = "album";
57a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_ARTIST = "artist";
58a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_GENRE = "genre";
59a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_SOURCE = "source";
60a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_IMAGE = "image";
61a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_TRACK_NUMBER = "trackNumber";
62a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
63a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String JSON_DURATION = "duration";
64a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
65a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    // Categorized caches for music track data:
66a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByGenre;
673ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon    private List<String> mMusicGenres;
68a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
69a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
70a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private final Set<String> mFavoriteTracks;
71a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
72a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    enum State {
73a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        NON_INITIALIZED, INITIALIZING, INITIALIZED
74a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
75a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
76a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private volatile State mCurrentState = State.NON_INITIALIZED;
77a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
78a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public interface Callback {
79a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        void onMusicCatalogReady(boolean success);
80a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
81a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
82a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public MusicProvider() {
83a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mMusicListByGenre = new ConcurrentHashMap<>();
84a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mMusicListById = new ConcurrentHashMap<>();
85a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
863ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon        mMusicGenres = new ArrayList<>();
87a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
88a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
89a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
903ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon     * Get the list of genres
91a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     *
92a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * @return genres
93a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
943ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon    public List<String> getGenres() {
95a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mCurrentState != State.INITIALIZED) {
96a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return Collections.emptyList();
97a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
983ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon        return mMusicGenres;
99a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
100a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
101a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
102a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Get music tracks of the given genre
103a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
1043ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon    public List<MediaMetadataCompat> getMusicsByGenre(String genre) {
105a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
106a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return Collections.emptyList();
107a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
108a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return mMusicListByGenre.get(genre);
109a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
110a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
111a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
112a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Very basic implementation of a search that filter music tracks which title containing
113a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * the given query.
114a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     *
115a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
116a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public Iterable<MediaMetadataCompat> searchMusic(String titleQuery) {
117a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mCurrentState != State.INITIALIZED) {
118a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return Collections.emptyList();
119a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
120a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        ArrayList<MediaMetadataCompat> result = new ArrayList<>();
121a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        titleQuery = titleQuery.toLowerCase();
122a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        for (MutableMediaMetadata track : mMusicListById.values()) {
123a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (track.metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE).toLowerCase()
124a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    .contains(titleQuery)) {
125a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                result.add(track.metadata);
126a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
127a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
128a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return result;
129a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
130a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
131a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
132a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Return the MediaMetadata for the given musicID.
133a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     *
134a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * @param musicId The unique, non-hierarchical music ID.
135a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
136a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public MediaMetadataCompat getMusic(String musicId) {
137a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null;
138a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
139a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
140a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public synchronized void updateMusic(String musicId, MediaMetadataCompat metadata) {
141a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        MutableMediaMetadata track = mMusicListById.get(musicId);
142a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (track == null) {
143a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return;
144a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
145a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
146a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String oldGenre = track.metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE);
147a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String newGenre = metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE);
148a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
149a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        track.metadata = metadata;
150a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
151a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // if genre has changed, we need to rebuild the list by genre
152a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (!oldGenre.equals(newGenre)) {
153a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            buildListsByGenre();
154a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
155a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
156a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
157a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public void setFavorite(String musicId, boolean favorite) {
158a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (favorite) {
159a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mFavoriteTracks.add(musicId);
160a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } else {
161a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mFavoriteTracks.remove(musicId);
162a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
163a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
164a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
165a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public boolean isFavorite(String musicId) {
166a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return mFavoriteTracks.contains(musicId);
167a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
168a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
169a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public boolean isInitialized() {
170a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return mCurrentState == State.INITIALIZED;
171a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
172a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
173a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
174a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Get the list of music tracks from a server and caches the track information
175a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * for future reference, keying tracks by musicId and grouping by genre.
176a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
177a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public void retrieveMediaAsync(final Callback callback) {
178a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "retrieveMediaAsync called");
179a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mCurrentState == State.INITIALIZED) {
180a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            // Nothing to do, execute callback immediately
181a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            callback.onMusicCatalogReady(true);
182a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return;
183a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
184a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
185a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Asynchronously load the music catalog in a separate thread
186a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        new AsyncTask<Void, Void, State>() {
187a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            @Override
188a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            protected State doInBackground(Void... params) {
189a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                retrieveMedia();
190a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                return mCurrentState;
191a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
192a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
193a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            @Override
194a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            protected void onPostExecute(State current) {
195a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                if (callback != null) {
196a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    callback.onMusicCatalogReady(current == State.INITIALIZED);
197a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
198a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
199a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }.execute();
200a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
201a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
202a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private synchronized void buildListsByGenre() {
2033ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon        ConcurrentMap<String, List<MediaMetadataCompat>> newMusicListByGenre
2043ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon                = new ConcurrentHashMap<>();
205a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
206a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        for (MutableMediaMetadata m : mMusicListById.values()) {
207a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            String genre = m.metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE);
208a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            List<MediaMetadataCompat> list = newMusicListByGenre.get(genre);
209a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (list == null) {
210a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                list = new ArrayList<>();
211a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                newMusicListByGenre.put(genre, list);
212a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
213a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            list.add(m.metadata);
214a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
215a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mMusicListByGenre = newMusicListByGenre;
2163ee2ea199ba4802754fb9108a45ef7c646c0eebbHyundo Moon        mMusicGenres = new ArrayList<>(mMusicListByGenre.keySet());
217a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
218a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
219a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private synchronized void retrieveMedia() {
220a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        try {
221a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (mCurrentState == State.NON_INITIALIZED) {
222a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mCurrentState = State.INITIALIZING;
223a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
224a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                int slashPos = CATALOG_URL.lastIndexOf('/');
225a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                String path = CATALOG_URL.substring(0, slashPos + 1);
226a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                JSONObject jsonObj = fetchJSONFromUrl(CATALOG_URL);
227a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                if (jsonObj == null) {
228a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    return;
229a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
230a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                JSONArray tracks = jsonObj.getJSONArray(JSON_MUSIC);
231a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                if (tracks != null) {
232a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    for (int j = 0; j < tracks.length(); j++) {
233a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                        MediaMetadataCompat item = buildFromJSON(tracks.getJSONObject(j), path);
234a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                        String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
235a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                        mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item));
236a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    }
237a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    buildListsByGenre();
238a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
239a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mCurrentState = State.INITIALIZED;
240a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
241a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } catch (JSONException e) {
242a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.e(TAG, "Could not retrieve music list", e);
243a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } finally {
244a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (mCurrentState != State.INITIALIZED) {
245a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                // Something bad happened, so we reset state to NON_INITIALIZED to allow
246a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                // retries (eg if the network connection is temporary unavailable)
247a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mCurrentState = State.NON_INITIALIZED;
248a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
249a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
250a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
251a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
252a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private MediaMetadataCompat buildFromJSON(JSONObject json, String basePath) throws JSONException {
253a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String title = json.getString(JSON_TITLE);
254a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String album = json.getString(JSON_ALBUM);
255a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String artist = json.getString(JSON_ARTIST);
256a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String genre = json.getString(JSON_GENRE);
257a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String source = json.getString(JSON_SOURCE);
258a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String iconUrl = json.getString(JSON_IMAGE);
259a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        int trackNumber = json.getInt(JSON_TRACK_NUMBER);
260a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
261a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        int duration = json.getInt(JSON_DURATION) * 1000; // ms
262a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
263a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "Found music track: " + json);
264a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
265a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Media is stored relative to JSON file
266a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (!source.startsWith("http")) {
267a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            source = basePath + source;
268a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
269a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (!iconUrl.startsWith("http")) {
270a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            iconUrl = basePath + iconUrl;
271a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
272a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Since we don't have a unique ID in the server, we fake one using the hashcode of
273a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // the music source. In a real world app, this could come from the server.
274a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String id = String.valueOf(source.hashCode());
275a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
276a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Adding the music source to the MediaMetadata (and consequently using it in the
277a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // mediaSession.setMetadata) is not a good idea for a real world music app, because
278a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // the session metadata can be accessed by notification listeners. This is done in this
279a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // sample for convenience only.
280a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return new MediaMetadataCompat.Builder()
281a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
282a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(CUSTOM_METADATA_TRACK_SOURCE, source)
283a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
284a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
285a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
286a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_GENRE, genre)
287a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, iconUrl)
288a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
289a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, trackNumber)
290a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, totalTrackCount)
291a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .build();
292a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
293a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
294a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
295a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Download a JSON file from a server, parse the content and return the JSON
296a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * object.
297a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     *
298a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * @return result JSONObject containing the parsed representation.
299a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
300a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private JSONObject fetchJSONFromUrl(String urlString) {
301a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        InputStream is = null;
302a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        try {
303a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            URL url = new URL(urlString);
304a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            URLConnection urlConnection = url.openConnection();
305a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            is = new BufferedInputStream(urlConnection.getInputStream());
306a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            BufferedReader reader = new BufferedReader(new InputStreamReader(
307a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    urlConnection.getInputStream(), "iso-8859-1"));
308a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            StringBuilder sb = new StringBuilder();
309a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            String line;
310a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            while ((line = reader.readLine()) != null) {
311a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                sb.append(line);
312a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
313a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return new JSONObject(sb.toString());
314a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } catch (Exception e) {
315a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.e(TAG, "Failed to parse the json for media list", e);
316a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return null;
317a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } finally {
318a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (is != null) {
319a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                try {
320a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    is.close();
321a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                } catch (IOException e) {
322a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    // ignore
323a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
324a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
325a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
326a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
327a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim}
328