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 
15 package android.leanbackjank.app.ui;
16 
17 import android.content.Intent;
18 import android.content.res.Resources;
19 import android.content.res.Resources.Theme;
20 import android.leanbackjank.app.IntentKeys;
21 import android.leanbackjank.app.R;
22 import android.leanbackjank.app.data.VideoProvider;
23 import android.leanbackjank.app.model.Movie;
24 import android.leanbackjank.app.presenter.CardPresenter;
25 import android.leanbackjank.app.presenter.IconHeaderItemPresenter;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import androidx.leanback.app.BackgroundManager;
29 import androidx.leanback.app.BrowseFragment;
30 import androidx.leanback.widget.ArrayObjectAdapter;
31 import androidx.leanback.widget.HeaderItem;
32 import androidx.leanback.widget.ListRow;
33 import androidx.leanback.widget.ListRowPresenter;
34 import androidx.leanback.widget.Presenter;
35 import androidx.leanback.widget.PresenterSelector;
36 import androidx.leanback.widget.ShadowOverlayHelper;
37 import android.util.DisplayMetrics;
38 import android.view.View;
39 
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Timer;
44 import java.util.TimerTask;
45 
46 class ScrollPatternGenerator {
47     private int currentRow;
48     private int topRow;
49     private int bottomRow;
50     private enum ScrollDirection {DOWN, UP};
51     private ScrollDirection direction;
52 
ScrollPatternGenerator(int topRow, int bottomRow)53     public ScrollPatternGenerator(int topRow, int bottomRow) {
54         this.topRow = topRow;
55         this.bottomRow = bottomRow;
56         assert(topRow < bottomRow);
57         direction = ScrollDirection.DOWN;
58         currentRow = topRow;
59     }
60 
61     public int next() {
62         if (currentRow == bottomRow) {
63             direction = ScrollDirection.UP;
64         } else if (currentRow == topRow) {
65             direction = ScrollDirection.DOWN;
66         }
67         if (direction == ScrollDirection.DOWN) {
68             currentRow++;
69         } else {
70             currentRow--;
71         }
72         return currentRow;
73     }
74 }
75 
76 /**
77  * Main class to show BrowseFragment with header and rows of videos
78  */
79 public class MainFragment extends BrowseFragment {
80     private static final int NUM_ROWS = 20;
81 
82     // Minimum number of rows that should show above / below the cursor by auto scroll.
83     // This is intended to avoid that the work load fluctuates largely when # of cards in the
84     // display lowers when the cursor is positioned near the top or bottom of the list.
85     private static final int MARGIN_ROWS = 4;
86 
87     private final Handler mHandler = new Handler();
88     private Timer mAutoScrollTimer;
89     private int mAutoScrollCount;
90 
91     private ArrayObjectAdapter mRowsAdapter;
92     private DisplayMetrics mMetrics;
93     private BackgroundManager mBackgroundManager;
94     private ScrollPatternGenerator scrollPatternGenerator;
95 
96     @Override
97     public void onActivityCreated(Bundle savedInstanceState) {
98         super.onActivityCreated(savedInstanceState);
99 
100         buildRowAdapterItems(VideoProvider.buildMedia(NUM_ROWS));
101         prepareBackgroundManager();
102         setupUIElements();
103         setupEventListeners();
104         Intent intent = getActivity().getIntent();
105         if (intent.getExtras() != null) {
106             int initialDelay = intent.getExtras().getInt(IntentKeys.SCROLL_DELAY);
107             int scrollCount = intent.getExtras().getInt(IntentKeys.SCROLL_COUNT);
108             int scrollInterval = intent.getExtras().getInt(IntentKeys.SCROLL_INTERVAL);
109             if (scrollInterval != 0 && scrollCount != 0) {
110                 startAutoScrollTimer(initialDelay, scrollInterval, scrollCount);
111             }
112             scrollPatternGenerator =
113                     new ScrollPatternGenerator(MARGIN_ROWS, NUM_ROWS - 1 - MARGIN_ROWS);
114         }
115     }
116 
117     @Override
118     public void onDestroy() {
119         if (null != mAutoScrollTimer) {
120             mAutoScrollTimer.cancel();
121             mAutoScrollTimer = null;
122         }
123         super.onDestroy();
124     }
125 
126     @Override
127     public void onStop() {
128         mBackgroundManager.release();
129         super.onStop();
130     }
131 
132     private void prepareBackgroundManager() {
133         mBackgroundManager = BackgroundManager.getInstance(getActivity());
134         mBackgroundManager.attach(getActivity().getWindow());
135         mBackgroundManager.setDrawable(getActivity().getResources().getDrawable(
136                 R.drawable.default_background, getContext().getTheme()));
137         mMetrics = new DisplayMetrics();
138         getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
139     }
140 
141     private void setupUIElements() {
142         setBadgeDrawable(getActivity().getResources().getDrawable(
143                 R.drawable.videos_by_google_banner, getContext().getTheme()));
144         setTitle(getString(R.string.browse_title));
145         setHeadersState(HEADERS_ENABLED);
146         setHeadersTransitionOnBackEnabled(true);
147 
148         Theme theme = getContext().getTheme();
149         setBrandColor(getResources().getColor(R.color.fastlane_background, theme));
150 
151         setSearchAffordanceColor(getResources().getColor(R.color.search_opaque, theme));
152 
153         setHeaderPresenterSelector(new PresenterSelector() {
154             @Override
155             public Presenter getPresenter(Object o) {
156                 return new IconHeaderItemPresenter();
157             }
158         });
159     }
160 
161     private void setupEventListeners() {
162         // Add lister to show the search button.
163         setOnSearchClickedListener(new View.OnClickListener() {
164             @Override
165             public void onClick(View view) {
166             }
167         });
168     }
169 
170     public void buildRowAdapterItems(HashMap<String, List<Movie>> data) {
171         ListRowPresenter listRowPresenter = new ListRowPresenter() {
172             @Override
173             protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
174                 Resources res = getResources();
175                 ShadowOverlayHelper.Options options = new ShadowOverlayHelper.Options();
176                 options.dynamicShadowZ(res.getDimension(R.dimen.shadow_unfocused_z),
177                         res.getDimension(R.dimen.shadow_focused_z));
178                 return options;
179             }
180         };
181         mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
182         CardPresenter cardPresenter = new CardPresenter();
183 
184         int i = 0;
185 
186         for (Map.Entry<String, List<Movie>> entry : data.entrySet()) {
187             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
188             List<Movie> list = entry.getValue();
189 
190             for (int j = 0; j < list.size(); j++) {
191                 listRowAdapter.add(list.get(j));
192             }
193             HeaderItem header = new HeaderItem(i, entry.getKey());
194             i++;
195             mRowsAdapter.add(new ListRow(header, listRowAdapter));
196         }
197 
198         setAdapter(mRowsAdapter);
199     }
200 
201     private class UpdateAutoScrollTask extends TimerTask {
202         @Override
203         public void run() {
204             mHandler.post(new Runnable() {
205                 @Override
206                 public void run() {
207                     if (mAutoScrollCount == 0) {
208                       mAutoScrollTimer.cancel();
209                       return;
210                     }
211                     setSelectedPosition(scrollPatternGenerator.next());
212                     mAutoScrollCount--;
213                 }
214             });
215         }
216     }
217 
218     private void startAutoScrollTimer(int initialDelay, int interval, int count) {
219         if (null != mAutoScrollTimer) {
220             mAutoScrollTimer.cancel();
221         }
222         mAutoScrollCount = count;
223         mAutoScrollTimer = new Timer();
224         mAutoScrollTimer.schedule(new UpdateAutoScrollTask(), initialDelay, interval);
225     }
226 
227     public void selectRow(int row) {
228         setSelectedPosition(row);
229     }
230 }
231