1 /*
2  * Copyright (C) 2014 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 package com.example.android.leanback;
15 
16 import android.content.Context;
17 import android.content.Intent;
18 import android.graphics.Bitmap;
19 import android.graphics.drawable.Drawable;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.util.Log;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 
28 import androidx.core.app.ActivityOptionsCompat;
29 import androidx.core.content.res.ResourcesCompat;
30 import androidx.leanback.app.DetailsFragmentBackgroundController;
31 import androidx.leanback.media.MediaPlayerAdapter;
32 import androidx.leanback.media.MediaPlayerGlue;
33 import androidx.leanback.media.PlaybackGlue;
34 import androidx.leanback.media.PlaybackTransportControlGlue;
35 import androidx.leanback.widget.Action;
36 import androidx.leanback.widget.ArrayObjectAdapter;
37 import androidx.leanback.widget.ClassPresenterSelector;
38 import androidx.leanback.widget.DetailsOverviewRow;
39 import androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter;
40 import androidx.leanback.widget.FullWidthDetailsOverviewSharedElementHelper;
41 import androidx.leanback.widget.HeaderItem;
42 import androidx.leanback.widget.ImageCardView;
43 import androidx.leanback.widget.ListRow;
44 import androidx.leanback.widget.ListRowPresenter;
45 import androidx.leanback.widget.OnActionClickedListener;
46 import androidx.leanback.widget.OnItemViewClickedListener;
47 import androidx.leanback.widget.OnItemViewSelectedListener;
48 import androidx.leanback.widget.Presenter;
49 import androidx.leanback.widget.Row;
50 import androidx.leanback.widget.RowPresenter;
51 import androidx.leanback.widget.SparseArrayObjectAdapter;
52 
53 public class NewDetailsFragment extends androidx.leanback.app.DetailsFragment {
54 
55     private static final String TAG = "leanback.DetailsFragment";
56     private static final String ITEM = "item";
57 
58     private static final int NUM_ROWS = 3;
59     private ArrayObjectAdapter mRowsAdapter;
60     private PhotoItem mPhotoItem;
61     final CardPresenter cardPresenter = new CardPresenter();
62 
63     private static final int ACTION_PLAY = 1;
64     private static final int ACTION_RENT = 2;
65     private static final int ACTION_BUY = 3;
66 
67     private boolean TEST_SHARED_ELEMENT_TRANSITION = true;
68     private boolean TEST_BACKGROUND_PLAYER;
69 
70     private static final long TIME_TO_LOAD_OVERVIEW_ROW_MS = 1000;
71     private static final long TIME_TO_LOAD_RELATED_ROWS_MS = 2000;
72 
73     private Action mActionPlay;
74     private Action mActionRent;
75     private Action mActionBuy;
76 
77     private FullWidthDetailsOverviewSharedElementHelper mHelper;
78     private BackgroundHelper mBackgroundHelper; // used to download bitmap async.
79     private final DetailsFragmentBackgroundController mDetailsBackground =
80             new DetailsFragmentBackgroundController(this);
81 
setupTrailerVideo()82     void setupTrailerVideo() {
83         MediaPlayerGlue mediaPlayerGlue = new MediaPlayerGlue(getActivity());
84         mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
85         mediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ONE);
86         mediaPlayerGlue.setArtist("A Googler");
87         mediaPlayerGlue.setTitle("Diving with Sharks Trailer");
88         mediaPlayerGlue.setMediaSource(Uri.parse("android.resource://com.example.android.leanback/"
89                 + "raw/browse"));
90     }
91 
setupMainVideo()92     void setupMainVideo() {
93         Context context = getActivity();
94         MediaPlayerAdapter adapter = new MediaPlayerAdapter(context);
95         PlaybackTransportControlGlue<MediaPlayerAdapter> mediaPlayerGlue =
96                 new PlaybackTransportControlGlue(context, adapter);
97         mDetailsBackground.setupVideoPlayback(mediaPlayerGlue);
98         mediaPlayerGlue.setSubtitle("A Googler");
99         mediaPlayerGlue.setTitle("Diving with Sharks");
100         mediaPlayerGlue.getPlayerAdapter().setDataSource(Uri.parse(
101                 "https://storage.googleapis.com/android-tv/Sample videos/April Fool's "
102                         + "2013/Explore Treasure Mode with Google Maps.mp4"));
103         mediaPlayerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
104             @Override
105             public void onPreparedStateChanged(PlaybackGlue glue) {
106                 super.onPreparedStateChanged(glue);
107                 PlaybackTransportControlGlue controlGlue = (PlaybackTransportControlGlue) glue;
108                 controlGlue.setSeekProvider(new PlaybackSeekDiskDataProvider(
109                         controlGlue.getDuration(), 1000,
110                         "/sdcard/seek/frame_%04d.jpg"));
111             }
112         });
113     }
114 
115     @Override
onCreate(Bundle savedInstanceState)116     public void onCreate(Bundle savedInstanceState) {
117         Log.i(TAG, "onCreate");
118         super.onCreate(savedInstanceState);
119 
120         mBackgroundHelper = new BackgroundHelper(getActivity());
121         mDetailsBackground.enableParallax();
122         if (TEST_BACKGROUND_PLAYER) {
123             if (MovieData.sStatus == MovieData.STATUS_INIT) {
124                 // not own/rented, play trailer
125                 setupTrailerVideo();
126             } else {
127                 // bought or rented, play the main content
128                 setupMainVideo();
129                 // hide details main ui
130                 mDetailsBackground.switchToVideo();
131             }
132         }
133 
134         final Context context = getActivity();
135         setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_title,
136                 context.getTheme()));
137         setTitle("Leanback Sample App");
138         if (!TEST_BACKGROUND_PLAYER) {
139             setOnSearchClickedListener(new View.OnClickListener() {
140                 @Override
141                 public void onClick(View view) {
142                     Intent intent = new Intent(getActivity(), SearchActivity.class);
143                     startActivity(intent);
144                 }
145             });
146         }
147 
148         mActionPlay = new Action(ACTION_PLAY, "Play");
149         mActionRent = new Action(ACTION_RENT, "Rent", "$3.99", ResourcesCompat.getDrawable(
150                 context.getResources(), R.drawable.ic_action_a, context.getTheme()));
151         mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
152 
153         ClassPresenterSelector ps = new ClassPresenterSelector();
154         FullWidthDetailsOverviewRowPresenter dorPresenter =
155                 new FullWidthDetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
156         dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
157             @Override
158             public void onActionClicked(Action action) {
159                 final Context context = getActivity();
160                 DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(0);
161                 if (action.getId() == ACTION_BUY) {
162                     // on the UI thread, we can modify actions adapter directly
163                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
164                             dor.getActionsAdapter();
165                     actions.set(ACTION_PLAY, mActionPlay);
166                     actions.clear(ACTION_RENT);
167                     actions.clear(ACTION_BUY);
168                     boolean previousRented = MovieData.sStatus == MovieData.STATUS_RENTED;
169                     MovieData.sStatus = MovieData.STATUS_OWN;
170                     dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
171                     dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
172                             R.drawable.details_img_16x9, context.getTheme()));
173                     if (TEST_BACKGROUND_PLAYER) {
174                         if (!previousRented) {
175                             setupMainVideo();
176                             mDetailsBackground.switchToVideo();
177                         }
178                     } else {
179                         Intent intent = new Intent(context, PlaybackActivity.class);
180                         getActivity().startActivity(intent);
181                     }
182                 } else if (action.getId() == ACTION_RENT) {
183                     // on the UI thread, we can modify actions adapter directly
184                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
185                             dor.getActionsAdapter();
186                     actions.set(ACTION_PLAY, mActionPlay);
187                     actions.clear(ACTION_RENT);
188                     MovieData.sStatus = MovieData.STATUS_RENTED;
189                     dor.setItem(getDisplayTitle(mPhotoItem.getTitle()));
190                     if (TEST_BACKGROUND_PLAYER) {
191                         setupMainVideo();
192                         mDetailsBackground.switchToVideo();
193                     } else {
194                         Intent intent = new Intent(context, PlaybackActivity.class);
195                         getActivity().startActivity(intent);
196                     }
197                 } else if (action.getId() == ACTION_PLAY) {
198                     if (TEST_BACKGROUND_PLAYER) {
199                         mDetailsBackground.switchToVideo();
200                     } else {
201                         Intent intent = new Intent(context, PlaybackActivity.class);
202                         getActivity().startActivity(intent);
203                     }
204                 }
205             }
206         });
207 
208         ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
209         ps.addClassPresenter(ListRow.class, new ListRowPresenter());
210 
211         mRowsAdapter = new ArrayObjectAdapter(ps);
212         updateAdapter();
213 
214         setOnItemViewClickedListener(new OnItemViewClickedListener() {
215             @Override
216             public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
217                     RowPresenter.ViewHolder rowViewHolder, Row row) {
218                 Log.i(TAG, "onItemClicked: " + item + " row " + row);
219                 if (item instanceof PhotoItem) {
220                     Intent intent = new Intent(getActivity(), DetailsActivity.class);
221                     intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item);
222 
223                     Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
224                             getActivity(),
225                             ((ImageCardView) itemViewHolder.view).getMainImageView(),
226                             DetailsActivity.SHARED_ELEMENT_NAME).toBundle();
227                     getActivity().startActivity(intent, bundle);
228                 }
229             }
230         });
231         setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
232             @Override
233             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
234                     RowPresenter.ViewHolder rowViewHolder, Row row) {
235                 Log.i(TAG, "onItemSelected: " + item + " row " + row);
236             }
237         });
238 
239         if (TEST_SHARED_ELEMENT_TRANSITION) {
240             mHelper = new FullWidthDetailsOverviewSharedElementHelper();
241             mHelper.setSharedElementEnterTransition(getActivity(),
242                     DetailsActivity.SHARED_ELEMENT_NAME);
243             dorPresenter.setListener(mHelper);
244             dorPresenter.setParticipatingEntranceTransition(false);
245         } else {
246             dorPresenter.setParticipatingEntranceTransition(true);
247         }
248     }
249 
250     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)251     public View onCreateView(LayoutInflater inflater, ViewGroup container,
252             Bundle savedInstanceState) {
253         View view = super.onCreateView(inflater, container, savedInstanceState);
254         return view;
255     }
256 
setBackgroundVideo(boolean backgroundVideo)257     public void setBackgroundVideo(boolean backgroundVideo) {
258         TEST_BACKGROUND_PLAYER = backgroundVideo;
259     }
260 
setItem(PhotoItem photoItem)261     public void setItem(PhotoItem photoItem) {
262         mPhotoItem = photoItem;
263         updateAdapter();
264     }
265 
getDisplayTitle(String title)266     static String getDisplayTitle(String title) {
267         switch (MovieData.sStatus) {
268             case MovieData.STATUS_OWN:
269                 title = title + "(Owned)";
270                 break;
271             case MovieData.STATUS_RENTED:
272                 title = title + "(Rented)";
273                 break;
274             case MovieData.STATUS_INIT:
275             default:
276         }
277         return title;
278     }
279 
updateAdapter()280     void updateAdapter() {
281         if (mRowsAdapter == null) {
282             return;
283         }
284         mRowsAdapter.clear();
285         new Handler().postDelayed(new Runnable() {
286             @Override
287             public void run() {
288                 final Context context = getActivity();
289                 if (context == null) {
290                     return;
291                 }
292 
293                 DetailsOverviewRow dor = new DetailsOverviewRow(
294                         getDisplayTitle(mPhotoItem.getTitle()));
295                 dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
296                         mPhotoItem.getImageResourceId(), context.getTheme()));
297                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
298                 switch (MovieData.sStatus) {
299                     case MovieData.STATUS_INIT:
300                         adapter.set(ACTION_RENT, mActionRent);
301                         adapter.set(ACTION_BUY, mActionBuy);
302                         break;
303                     case MovieData.STATUS_OWN:
304                         adapter.set(ACTION_PLAY, mActionPlay);
305                         break;
306                     case MovieData.STATUS_RENTED:
307                         adapter.set(ACTION_PLAY, mActionPlay);
308                         adapter.set(ACTION_BUY, mActionBuy);
309                         break;
310                 }
311                 // one line text with icon
312                 Drawable d = ResourcesCompat.getDrawable(context.getResources(),
313                         R.drawable.ic_action_a, context.getTheme());
314                 adapter.set(202, new Action(202, "Top", null, d));
315                 dor.setActionsAdapter(adapter);
316                 mRowsAdapter.add(0, dor);
317                 setSelectedPosition(0, true);
318                 if (TEST_SHARED_ELEMENT_TRANSITION) {
319                     if (mHelper != null && !mHelper.getAutoStartSharedElementTransition()) {
320                         mHelper.startPostponedEnterTransition();
321                     }
322                 }
323             }
324         }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
325 
326         new Handler().postDelayed(new Runnable() {
327             @Override
328             public void run() {
329                 if (getActivity() == null) {
330                     return;
331                 }
332                 for (int i = 0; i < NUM_ROWS; ++i) {
333                     ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
334                     listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1));
335                     listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2));
336                     listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3));
337                     listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4));
338                     HeaderItem header = new HeaderItem(i, "Row " + i);
339                     mRowsAdapter.add(new ListRow(header, listRowAdapter));
340                 }
341             }
342         }, TIME_TO_LOAD_RELATED_ROWS_MS);
343         setAdapter(mRowsAdapter);
344     }
345 
346     @Override
onStart()347     public void onStart() {
348         super.onStart();
349 
350         // Restore background drawable in onStart():
351         mBackgroundHelper.loadBitmap(R.drawable.spiderman,
352                 new BackgroundHelper.BitmapLoadCallback() {
353                     @Override
354                     public void onBitmapLoaded(Bitmap bitmap) {
355                         mDetailsBackground.setCoverBitmap(bitmap);
356                     }
357                 });
358     }
359 
360     @Override
onStop()361     public void onStop() {
362         mDetailsBackground.setCoverBitmap(null);
363         super.onStop();
364     }
365 }
366