1/*
2 * Copyright (C) 2014 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.example.android.musicservicedemo.browser;
18
19import android.database.MatrixCursor;
20import android.media.session.PlaybackState;
21import android.net.Uri;
22import android.util.Log;
23
24import com.example.android.musicservicedemo.BrowserService;
25
26import org.json.JSONArray;
27import org.json.JSONException;
28import org.json.JSONObject;
29
30import java.io.BufferedInputStream;
31import java.io.BufferedReader;
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.InputStreamReader;
35import java.net.URLConnection;
36import java.util.ArrayList;
37import java.util.List;
38
39/**
40 * Utility class to get a list of MusicTrack's based on a server-side JSON
41 * configuration.
42 */
43public class MusicProvider {
44
45    private static final String TAG = "MusicProvider";
46
47    private static final String MUSIC_URL = "http://storage.googleapis.com/automotive-media/music.json";
48
49    private static String MUSIC = "music";
50    private static String TITLE = "title";
51    private static String ALBUM = "album";
52    private static String ARTIST = "artist";
53    private static String GENRE = "genre";
54    private static String SOURCE = "source";
55    private static String IMAGE = "image";
56    private static String TRACK_NUMBER = "trackNumber";
57    private static String TOTAL_TRACK_COUNT = "totalTrackCount";
58    private static String DURATION = "duration";
59
60    // Cache for music track data
61    private static List<MusicTrack> mMusicList;
62
63    /**
64     * Get the cached list of music tracks
65     *
66     * @return
67     * @throws JSONException
68     */
69    public List<MusicTrack> getMedia() throws JSONException {
70        if (null != mMusicList && mMusicList.size() > 0) {
71            return mMusicList;
72        }
73        return null;
74    }
75
76    /**
77     * Get the list of music tracks from a server and return the list of
78     * MusicTrack objects.
79     *
80     * @return
81     * @throws JSONException
82     */
83    public List<MusicTrack> retreiveMedia() throws JSONException {
84        if (null != mMusicList) {
85            return mMusicList;
86        }
87        int slashPos = MUSIC_URL.lastIndexOf('/');
88        String path = MUSIC_URL.substring(0, slashPos + 1);
89        JSONObject jsonObj = parseUrl(MUSIC_URL);
90
91        try {
92            JSONArray videos = jsonObj.getJSONArray(MUSIC);
93            if (null != videos) {
94                mMusicList = new ArrayList<MusicTrack>();
95                for (int j = 0; j < videos.length(); j++) {
96                    JSONObject music = videos.getJSONObject(j);
97                    String title = music.getString(TITLE);
98                    String album = music.getString(ALBUM);
99                    String artist = music.getString(ARTIST);
100                    String genre = music.getString(GENRE);
101                    String source = music.getString(SOURCE);
102                    // Media is stored relative to JSON file
103                    if (!source.startsWith("http")) {
104                        source = path + source;
105                    }
106                    String image = music.getString(IMAGE);
107                    if (!image.startsWith("http")) {
108                        image = path + image;
109                    }
110                    int trackNumber = music.getInt(TRACK_NUMBER);
111                    int totalTrackCount = music.getInt(TOTAL_TRACK_COUNT);
112                    int duration = music.getInt(DURATION) * 1000; // ms
113
114                    mMusicList.add(new MusicTrack(title, album, artist, genre, source,
115                            image, trackNumber, totalTrackCount, duration));
116                }
117            }
118        } catch (NullPointerException e) {
119            Log.e(TAG, "retreiveMedia", e);
120        }
121        return mMusicList;
122    }
123
124    /**
125     * Download a JSON file from a server, parse the content and return the JSON
126     * object.
127     *
128     * @param urlString
129     * @return
130     */
131    private JSONObject parseUrl(String urlString) {
132        InputStream is = null;
133        try {
134            java.net.URL url = new java.net.URL(urlString);
135            URLConnection urlConnection = url.openConnection();
136            is = new BufferedInputStream(urlConnection.getInputStream());
137            BufferedReader reader = new BufferedReader(new InputStreamReader(
138                    urlConnection.getInputStream(), "iso-8859-1"), 8);
139            StringBuilder sb = new StringBuilder();
140            String line = null;
141            while ((line = reader.readLine()) != null) {
142                sb.append(line);
143            }
144            return new JSONObject(sb.toString());
145        } catch (Exception e) {
146            Log.d(TAG, "Failed to parse the json for media list", e);
147            return null;
148        } finally {
149            if (null != is) {
150                try {
151                    is.close();
152                } catch (IOException e) {
153                    // ignore
154                }
155            }
156        }
157    }
158
159    public MatrixCursor getRootContainerCurser() {
160        MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
161        Uri.Builder pianoBuilder = new Uri.Builder();
162        pianoBuilder.authority(BrowserService.AUTHORITY);
163        pianoBuilder.appendPath(BrowserService.PIANO_BASE_PATH);
164        matrixCursor.addRow(new Object[] {
165                pianoBuilder.build(),
166                BrowserService.PIANO_BASE_PATH,
167                "subtitle",
168                null,
169                0
170        });
171
172        Uri.Builder voiceBuilder = new Uri.Builder();
173        voiceBuilder.authority(BrowserService.AUTHORITY);
174        voiceBuilder.appendPath(BrowserService.VOICE_BASE_PATH);
175        matrixCursor.addRow(new Object[] {
176                voiceBuilder.build(),
177                BrowserService.VOICE_BASE_PATH,
178                "subtitle",
179                null,
180                0
181        });
182        return matrixCursor;
183    }
184
185    public MatrixCursor getRootItemCursor(int type) {
186        if (type == BrowserService.NOW_PLAYING) {
187            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
188
189            try {
190                // Just return all of the tracks for now
191                List<MusicTrack> musicTracks = retreiveMedia();
192                for (MusicTrack musicTrack : musicTracks) {
193                    Uri.Builder builder = new Uri.Builder();
194                    builder.authority(BrowserService.AUTHORITY);
195                    builder.appendPath(BrowserService.NOW_PLAYING_PATH);
196                    builder.appendPath(musicTrack.getTitle());
197                    matrixCursor.addRow(new Object[] {
198                            builder.build(),
199                            musicTrack.getTitle(),
200                            musicTrack.getArtist(),
201                            musicTrack.getImage(),
202                            PlaybackState.ACTION_PLAY
203                    });
204                    Log.d(TAG, "Uri " + builder.build());
205                }
206            } catch (JSONException e) {
207                Log.e(TAG, "::getRootItemCursor:", e);
208            }
209
210            Log.d(TAG, "cursor: " + matrixCursor.getCount());
211            return matrixCursor;
212        } else if (type == BrowserService.PIANO) {
213            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
214
215            try {
216                List<MusicTrack> musicTracks = retreiveMedia();
217                for (MusicTrack musicTrack : musicTracks) {
218                    Uri.Builder builder = new Uri.Builder();
219                    builder.authority(BrowserService.AUTHORITY);
220                    builder.appendPath(BrowserService.PIANO_BASE_PATH);
221                    builder.appendPath(musicTrack.getTitle());
222                    matrixCursor.addRow(new Object[] {
223                            builder.build(),
224                            musicTrack.getTitle(),
225                            musicTrack.getArtist(),
226                            musicTrack.getImage(),
227                            PlaybackState.ACTION_PLAY
228                    });
229                    Log.d(TAG, "Uri " + builder.build());
230                }
231            } catch (JSONException e) {
232                Log.e(TAG, "::getRootItemCursor:", e);
233            }
234
235            Log.d(TAG, "cursor: " + matrixCursor.getCount());
236            return matrixCursor;
237        } else if (type == BrowserService.VOICE) {
238            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
239
240            try {
241                List<MusicTrack> musicTracks = retreiveMedia();
242                for (MusicTrack musicTrack : musicTracks) {
243                    Uri.Builder builder = new Uri.Builder();
244                    builder.authority(BrowserService.AUTHORITY);
245                    builder.appendPath(BrowserService.VOICE_BASE_PATH);
246                    builder.appendPath(musicTrack.getTitle());
247                    matrixCursor.addRow(new Object[] {
248                            builder.build(),
249                            musicTrack.getTitle(),
250                            musicTrack.getArtist(),
251                            musicTrack.getImage(),
252                            PlaybackState.ACTION_PLAY
253                    });
254                    Log.d(TAG, "Uri " + builder.build());
255                }
256            } catch (JSONException e) {
257                Log.e(TAG, "::getRootItemCursor:", e);
258            }
259
260            Log.d(TAG, "cursor: " + matrixCursor.getCount());
261            return matrixCursor;
262
263        }
264        return null;
265    }
266}
267