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