1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package android.support.v17.leanback.supportleanbackshowcase.app.media;
16
17import android.content.Context;
18import android.content.res.Resources;
19import android.graphics.drawable.Drawable;
20import android.net.Uri;
21import android.os.Bundle;
22import android.support.v17.leanback.app.PlaybackOverlayFragment;
23import android.support.v17.leanback.supportleanbackshowcase.utils.Constants;
24import android.support.v17.leanback.supportleanbackshowcase.R;
25import android.support.v17.leanback.supportleanbackshowcase.utils.Utils;
26import android.support.v17.leanback.supportleanbackshowcase.models.Song;
27import android.support.v17.leanback.supportleanbackshowcase.models.SongList;
28import android.support.v17.leanback.widget.*;
29import android.support.v17.leanback.widget.AbstractMediaItemPresenter;
30import android.util.Log;
31
32import com.google.gson.Gson;
33
34import java.util.List;
35
36/**
37 * This example shows how to play music files and build a simple track list.
38 */
39public class MusicConsumptionExampleFragment extends PlaybackOverlayFragment implements
40        BaseOnItemViewClickedListener, BaseOnItemViewSelectedListener,
41        MediaPlayerGlue.OnMediaFileFinishedPlayingListener {
42
43    private static final String TAG = "MusicConsumptionExampleFragment";
44    private static final int PLAYLIST_ACTION_ID = 0;
45    private static final int FAVORITE_ACTION_ID = 1;
46    private ArrayObjectAdapter mRowsAdapter;
47    private MediaPlayerGlue mGlue;
48    private int mCurrentSongIndex = 0;
49    private List<Song> mSongList;
50    private boolean mAdapterNotified = false;
51
52    @Override public void onCreate(Bundle savedInstanceState) {
53        super.onCreate(savedInstanceState);
54        if (Constants.LOCAL_LOGD) Log.d(TAG, "onCreate");
55
56        mGlue = new MediaPlayerGlue(getActivity(), this) {
57
58            @Override protected void onRowChanged(PlaybackControlsRow row) {
59                if (mRowsAdapter == null || mAdapterNotified) return;
60                //mAdapterNotified = true;
61                mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
62            }
63        };
64        mGlue.setOnMediaFileFinishedPlayingListener(this);
65
66        String json = Utils.inputStreamToString(
67                getResources().openRawResource(R.raw.music_consumption_example));
68
69
70        mSongList = new Gson().fromJson(json, SongList.class).getSongs();
71
72        Resources res = getActivity().getResources();
73
74        // For each song add a playlist and favorite actions.
75        for(Song song : mSongList) {
76            MultiActionsProvider.MultiAction[] mediaRowActions = new
77                    MultiActionsProvider.MultiAction[2];
78            MultiActionsProvider.MultiAction playlistAction = new
79                    MultiActionsProvider.MultiAction(PLAYLIST_ACTION_ID);
80            Drawable[] playlistActionDrawables = new Drawable[] {
81                    res.getDrawable(R.drawable.ic_playlist_add_white_24dp,
82                            getActivity().getTheme()),
83                    res.getDrawable(R.drawable.ic_playlist_add_filled_24dp,
84                            getActivity().getTheme())};
85            playlistAction.setDrawables(playlistActionDrawables);
86            mediaRowActions[0] = playlistAction;
87
88            MultiActionsProvider.MultiAction favoriteAction = new
89                    MultiActionsProvider.MultiAction(FAVORITE_ACTION_ID);
90            Drawable[] favoriteActionDrawables = new Drawable[] {
91                    res.getDrawable(R.drawable.ic_favorite_border_white_24dp,
92                            getActivity().getTheme()),
93                    res.getDrawable(R.drawable.ic_favorite_filled_24dp,
94                            getActivity().getTheme())};
95            favoriteAction.setDrawables(favoriteActionDrawables);
96            mediaRowActions[1] = favoriteAction;
97            song.setMediaRowActions(mediaRowActions);
98        }
99
100        Song song = mSongList.get(mCurrentSongIndex);
101        MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
102        metaData.setArtist(song.getDescription());
103        metaData.setTitle(song.getTitle());
104        metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
105        Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
106        mGlue.setMetaData(metaData);
107        mGlue.setMediaSource(uri);
108        mGlue.prepareMediaForPlaying();
109
110        addPlaybackControlsRow();
111    }
112
113    @Override public void onStart() {
114        super.onStart();
115        mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
116    }
117
118    @Override public void onStop() {
119        super.onStop();
120        mGlue.enableProgressUpdating(false);
121        mGlue.reset();
122    }
123
124    static class SongPresenter extends AbstractMediaItemPresenter {
125
126        SongPresenter() {
127            super();
128        }
129
130        SongPresenter(Context context, int themeResId) {
131            super(themeResId);
132            setHasMediaRowSeparator(true);
133        }
134
135        @Override
136        protected void onBindMediaDetails(ViewHolder vh, Object item) {
137
138            int favoriteTextColor =  vh.view.getContext().getResources().getColor(
139                    R.color.song_row_favorite_color);
140            Song song = (Song) item;
141            vh.getMediaItemNumberView().setText("" + song.getNumber());
142
143            String songTitle = song.getTitle() + " / " + song.getDescription();
144            vh.getMediaItemNameView().setText(songTitle);
145
146            vh.getMediaItemDurationView().setText("" + song.getDuration());
147
148            if (song.isFavorite()) {
149                vh.getMediaItemNumberView().setTextColor(favoriteTextColor);
150                vh.getMediaItemNameView().setTextColor(favoriteTextColor);
151                vh.getMediaItemDurationView().setTextColor(favoriteTextColor);
152            } else {
153                Context context = vh.getMediaItemNumberView().getContext();
154                vh.getMediaItemNumberView().setTextAppearance(context,
155                        R.style.TextAppearance_Leanback_PlaybackMediaItemNumber);
156                vh.getMediaItemNameView().setTextAppearance(context,
157                        R.style.TextAppearance_Leanback_PlaybackMediaItemName);
158                vh.getMediaItemDurationView().setTextAppearance(context,
159                        R.style.TextAppearance_Leanback_PlaybackMediaItemDuration);
160            }
161        }
162    };
163
164    static class SongPresenterSelector extends PresenterSelector {
165        Presenter mRegularPresenter;
166        Presenter mFavoritePresenter;
167
168        /**
169         * Adds a presenter to be used for the given class.
170         */
171        public SongPresenterSelector setSongPresenterRegular(Presenter presenter) {
172            mRegularPresenter = presenter;
173            return this;
174        }
175
176        /**
177         * Adds a presenter to be used for the given class.
178         */
179        public SongPresenterSelector setSongPresenterFavorite(Presenter presenter) {
180            mFavoritePresenter = presenter;
181            return this;
182        }
183
184        @Override
185        public Presenter[] getPresenters() {
186            return new Presenter[]{mRegularPresenter, mFavoritePresenter};
187        }
188
189        @Override
190        public Presenter getPresenter(Object item) {
191            return ( (Song) item).isFavorite() ? mFavoritePresenter : mRegularPresenter;
192        }
193
194    }
195
196    static class TrackListHeaderPresenter extends AbstractMediaListHeaderPresenter {
197
198        TrackListHeaderPresenter() {
199            super();
200        }
201
202        @Override
203        protected void onBindMediaListHeaderViewHolder(ViewHolder vh, Object item) {
204            vh.getHeaderView().setText("Tracklist");
205        }
206    };
207
208    private void addPlaybackControlsRow() {
209        mRowsAdapter = new ArrayObjectAdapter(new ClassPresenterSelector()
210                .addClassPresenterSelector(Song.class, new SongPresenterSelector()
211                        .setSongPresenterRegular(new SongPresenter(getActivity(),
212                                R.style.Theme_Example_LeanbackMusic_RegularSongNumbers))
213                        .setSongPresenterFavorite(new SongPresenter(getActivity(),
214                                R.style.Theme_Example_LeanbackMusic_FavoriteSongNumbers)))
215                .addClassPresenter(TrackListHeader.class, new TrackListHeaderPresenter())
216                .addClassPresenter(PlaybackControlsRow.class,
217                        mGlue.createControlsRowAndPresenter()));
218        mRowsAdapter.add(mGlue.getControlsRow());
219        mRowsAdapter.add(new TrackListHeader());
220        mRowsAdapter.addAll(2, mSongList);
221        setAdapter(mRowsAdapter);
222        setOnItemViewClickedListener(this);
223        setOnItemViewSelectedListener(this);
224    }
225
226    public MusicConsumptionExampleFragment() {
227        super();
228    }
229
230
231
232    @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
233                                        RowPresenter.ViewHolder rowViewHolder, Object row) {
234
235        if (item instanceof  Action) {
236            // if the clicked item is a primary or secondary action in the playback controller
237            mGlue.onActionClicked((Action) item);
238        } else if (row instanceof  Song) {
239            // if a media item row is clicked
240            Song clickedSong = (Song) row;
241            AbstractMediaItemPresenter.ViewHolder songRowVh =
242                    (AbstractMediaItemPresenter.ViewHolder) rowViewHolder;
243
244            // if an action within a media item row is clicked
245            if (item instanceof MultiActionsProvider.MultiAction) {
246                if ( ((MultiActionsProvider.MultiAction) item).getId() == FAVORITE_ACTION_ID) {
247                    MultiActionsProvider.MultiAction favoriteAction =
248                            (MultiActionsProvider.MultiAction) item;
249                    MultiActionsProvider.MultiAction playlistAction =
250                            songRowVh.getMediaItemRowActions()[0];
251                    favoriteAction.incrementIndex();
252                    playlistAction.incrementIndex();;
253
254                    clickedSong.setFavorite(!clickedSong.isFavorite());
255                    songRowVh.notifyDetailsChanged();
256                    songRowVh.notifyActionChanged(playlistAction);
257                    songRowVh.notifyActionChanged(favoriteAction);
258                }
259            } else if (item == null){
260                // if a media item details is clicked, start playing that media item
261                onSongDetailsClicked(clickedSong);
262            }
263
264        }
265
266
267    }
268
269    @Override
270    public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
271                               RowPresenter.ViewHolder rowViewHolder, Object row) {
272    }
273
274
275    public void onSongDetailsClicked(Song song) {
276        int nextSongIndex = mSongList.indexOf(song);
277        mCurrentSongIndex = nextSongIndex;
278        startPlayback();
279    }
280
281
282    @Override public void onMediaFileFinishedPlaying(MediaPlayerGlue.MetaData song) {
283        if (mGlue.repeatOne()) {
284        } else if (mGlue.useShuffle()) {
285            mCurrentSongIndex = (int) (Math.random() * mSongList.size());
286        } else {
287            mCurrentSongIndex++;
288            if (mCurrentSongIndex >= mSongList.size()) {
289                mCurrentSongIndex = 0;
290                if (!mGlue.repeatAll()) {
291                    return;
292                }
293            }
294        }
295        startPlayback();
296    }
297
298    private void startPlayback() {
299        Song song = mSongList.get(mCurrentSongIndex);
300        MediaPlayerGlue.MetaData metaData = new MediaPlayerGlue.MetaData();
301        metaData.setArtist(song.getDescription());
302        metaData.setTitle(song.getTitle());
303        metaData.setCover(getResources().getDrawable(song.getImageResource(getActivity()), null));
304
305        Uri uri = Utils.getResourceUri(getActivity(), song.getFileResource(getActivity()));
306        mGlue.setMetaData(metaData);
307
308        if (mGlue.setMediaSource(uri)) {
309            mGlue.prepareMediaForPlaying();
310        }
311        mGlue.startPlayback();
312    }
313}
314