1/* This file is auto-generated from DetailsFragment.java. DO NOT MODIFY. */ 2 3/* 4 * Copyright (C) 2014 The Android Open Source Project 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 * in compliance with the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software distributed under the License 12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 * or implied. See the License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16package android.support.v17.leanback.app; 17 18import android.support.v17.leanback.R; 19import android.support.v17.leanback.widget.BrowseFrameLayout; 20import android.support.v17.leanback.widget.DetailsOverviewRow; 21import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter; 22import android.support.v17.leanback.widget.ItemAlignmentFacet; 23import android.support.v17.leanback.widget.ItemBridgeAdapter; 24import android.support.v17.leanback.widget.ObjectAdapter; 25import android.support.v17.leanback.widget.OnItemViewClickedListener; 26import android.support.v17.leanback.widget.OnItemViewSelectedListener; 27import android.support.v17.leanback.widget.Presenter; 28import android.support.v17.leanback.widget.PresenterSelector; 29import android.support.v17.leanback.widget.Row; 30import android.support.v17.leanback.widget.RowPresenter; 31import android.support.v17.leanback.widget.TitleHelper; 32import android.support.v17.leanback.widget.TitleView; 33import android.support.v17.leanback.widget.VerticalGridView; 34import android.os.Bundle; 35import android.util.Log; 36import android.view.LayoutInflater; 37import android.view.View; 38import android.view.ViewGroup; 39 40/** 41 * A fragment for creating Leanback details screens. 42 * 43 * <p> 44 * A DetailsSupportFragment renders the elements of its {@link ObjectAdapter} as a set 45 * of rows in a vertical list. The elements in this adapter must be subclasses 46 * of {@link Row}, the Adapter's {@link PresenterSelector} must maintains subclasses 47 * of {@link RowPresenter}. 48 * </p> 49 * 50 * When {@link FullWidthDetailsOverviewRowPresenter} is found in adapter, DetailsSupportFragment will 51 * setup default behavior of the DetailsOverviewRow: 52 * <li> 53 * The alignment of FullWidthDetailsOverviewRowPresenter is setup in 54 * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}. 55 * </li> 56 * <li> 57 * The view status switching of FullWidthDetailsOverviewRowPresenter is done in 58 * {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter, 59 * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)}. 60 * </li> 61 * 62 * <p> 63 * The recommended activity themes to use with a DetailsSupportFragment are 64 * <li> 65 * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details} with activity 66 * shared element transition for {@link FullWidthDetailsOverviewRowPresenter}. 67 * </li> 68 * <li> 69 * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details_NoSharedElementTransition} 70 * if shared element transition is not needed, for example if first row is not rendered by 71 * {@link FullWidthDetailsOverviewRowPresenter}. 72 * </li> 73 * </p> 74 */ 75public class DetailsSupportFragment extends BaseSupportFragment { 76 private static final String TAG = "DetailsSupportFragment"; 77 private static boolean DEBUG = false; 78 79 private class SetSelectionRunnable implements Runnable { 80 int mPosition; 81 boolean mSmooth = true; 82 83 @Override 84 public void run() { 85 mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth); 86 } 87 } 88 89 private RowsSupportFragment mRowsSupportFragment; 90 91 private ObjectAdapter mAdapter; 92 private int mContainerListAlignTop; 93 private OnItemViewSelectedListener mExternalOnItemViewSelectedListener; 94 private OnItemViewClickedListener mOnItemViewClickedListener; 95 96 private Object mSceneAfterEntranceTransition; 97 98 private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable(); 99 100 private final OnItemViewSelectedListener mOnItemViewSelectedListener = 101 new OnItemViewSelectedListener() { 102 @Override 103 public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 104 RowPresenter.ViewHolder rowViewHolder, Row row) { 105 int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition(); 106 int subposition = mRowsSupportFragment.getVerticalGridView().getSelectedSubPosition(); 107 if (DEBUG) Log.v(TAG, "row selected position " + position 108 + " subposition " + subposition); 109 onRowSelected(position, subposition); 110 if (mExternalOnItemViewSelectedListener != null) { 111 mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item, 112 rowViewHolder, row); 113 } 114 } 115 }; 116 117 /** 118 * Sets the list of rows for the fragment. 119 */ 120 public void setAdapter(ObjectAdapter adapter) { 121 mAdapter = adapter; 122 Presenter[] presenters = adapter.getPresenterSelector().getPresenters(); 123 if (presenters != null) { 124 for (int i = 0; i < presenters.length; i++) { 125 setupPresenter(presenters[i]); 126 } 127 } else { 128 Log.e(TAG, "PresenterSelector.getPresenters() not implemented"); 129 } 130 if (mRowsSupportFragment != null) { 131 mRowsSupportFragment.setAdapter(adapter); 132 } 133 } 134 135 /** 136 * Returns the list of rows. 137 */ 138 public ObjectAdapter getAdapter() { 139 return mAdapter; 140 } 141 142 /** 143 * Sets an item selection listener. 144 */ 145 public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 146 mExternalOnItemViewSelectedListener = listener; 147 } 148 149 /** 150 * Sets an item clicked listener. 151 */ 152 public void setOnItemViewClickedListener(OnItemViewClickedListener listener) { 153 if (mOnItemViewClickedListener != listener) { 154 mOnItemViewClickedListener = listener; 155 if (mRowsSupportFragment != null) { 156 mRowsSupportFragment.setOnItemViewClickedListener(listener); 157 } 158 } 159 } 160 161 /** 162 * Returns the item clicked listener. 163 */ 164 public OnItemViewClickedListener getOnItemViewClickedListener() { 165 return mOnItemViewClickedListener; 166 } 167 168 @Override 169 public void onCreate(Bundle savedInstanceState) { 170 super.onCreate(savedInstanceState); 171 172 mContainerListAlignTop = 173 getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top); 174 } 175 176 @Override 177 public View onCreateView(LayoutInflater inflater, ViewGroup container, 178 Bundle savedInstanceState) { 179 View view = inflater.inflate(R.layout.lb_details_fragment, container, false); 180 ViewGroup fragment_root = (ViewGroup) view.findViewById(R.id.details_fragment_root); 181 View titleView = inflateTitle(inflater, fragment_root, savedInstanceState); 182 if (titleView != null) { 183 fragment_root.addView(titleView); 184 } 185 mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById( 186 R.id.details_rows_dock); 187 if (mRowsSupportFragment == null) { 188 mRowsSupportFragment = new RowsSupportFragment(); 189 getChildFragmentManager().beginTransaction() 190 .replace(R.id.details_rows_dock, mRowsSupportFragment).commit(); 191 } 192 mRowsSupportFragment.setAdapter(mAdapter); 193 mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener); 194 mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener); 195 196 if (titleView != null) { 197 View titleGroup = titleView.findViewById(R.id.browse_title_group); 198 if (titleGroup instanceof TitleView) { 199 setTitleView((TitleView) titleGroup); 200 } else { 201 setTitleView(null); 202 } 203 } 204 205 mSceneAfterEntranceTransition = sTransitionHelper.createScene( 206 (ViewGroup) view, new Runnable() { 207 @Override 208 public void run() { 209 mRowsSupportFragment.setEntranceTransitionState(true); 210 } 211 }); 212 return view; 213 } 214 215 /** 216 * Called by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to inflate 217 * TitleView. Default implementation uses layout file lb_browse_title. 218 * Subclass may override and use its own layout or return null if no title is needed. 219 */ 220 protected View inflateTitle(LayoutInflater inflater, ViewGroup parent, 221 Bundle savedInstanceState) { 222 return inflater.inflate(R.layout.lb_browse_title, parent, false); 223 } 224 225 void setVerticalGridViewLayout(VerticalGridView listview) { 226 // align the top edge of item to a fixed position 227 listview.setItemAlignmentOffset(-mContainerListAlignTop); 228 listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED); 229 listview.setWindowAlignmentOffset(0); 230 listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED); 231 listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE); 232 } 233 234 /** 235 * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}. Note 236 * that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment. For 237 * example how a row is aligned in details Fragment. The default implementation invokes 238 * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)} 239 * 240 */ 241 protected void setupPresenter(Presenter rowPresenter) { 242 if (rowPresenter instanceof FullWidthDetailsOverviewRowPresenter) { 243 setupDetailsOverviewRowPresenter((FullWidthDetailsOverviewRowPresenter) rowPresenter); 244 } 245 } 246 247 /** 248 * Called to setup {@link FullWidthDetailsOverviewRowPresenter}. The default implementation 249 * adds two aligment positions({@link ItemAlignmentFacet}) for ViewHolder of 250 * FullWidthDetailsOverviewRowPresenter to align in fragment. 251 */ 252 protected void setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter) { 253 ItemAlignmentFacet facet = new ItemAlignmentFacet(); 254 // by default align details_frame to half window height 255 ItemAlignmentFacet.ItemAlignmentDef alignDef1 = new ItemAlignmentFacet.ItemAlignmentDef(); 256 alignDef1.setItemAlignmentViewId(R.id.details_frame); 257 alignDef1.setItemAlignmentOffset(- getResources() 258 .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions)); 259 alignDef1.setItemAlignmentOffsetPercent(0); 260 // when description is selected, align details_frame to top edge 261 ItemAlignmentFacet.ItemAlignmentDef alignDef2 = new ItemAlignmentFacet.ItemAlignmentDef(); 262 alignDef2.setItemAlignmentViewId(R.id.details_frame); 263 alignDef2.setItemAlignmentFocusViewId(R.id.details_overview_description); 264 alignDef2.setItemAlignmentOffset(- getResources() 265 .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description)); 266 alignDef2.setItemAlignmentOffsetPercent(0); 267 ItemAlignmentFacet.ItemAlignmentDef[] defs = 268 new ItemAlignmentFacet.ItemAlignmentDef[] {alignDef1, alignDef2}; 269 facet.setAlignmentDefs(defs); 270 presenter.setFacet(ItemAlignmentFacet.class, facet); 271 } 272 273 VerticalGridView getVerticalGridView() { 274 return mRowsSupportFragment == null ? null : mRowsSupportFragment.getVerticalGridView(); 275 } 276 277 RowsSupportFragment getRowsSupportFragment() { 278 return mRowsSupportFragment; 279 } 280 281 /** 282 * Setup dimensions that are only meaningful when the child Fragments are inside 283 * DetailsSupportFragment. 284 */ 285 private void setupChildFragmentLayout() { 286 setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView()); 287 } 288 289 private void setupFocusSearchListener() { 290 TitleHelper titleHelper = getTitleHelper(); 291 if (titleHelper != null) { 292 BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById( 293 R.id.details_fragment_root); 294 browseFrameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener()); 295 } 296 } 297 298 /** 299 * Sets the selected row position with smooth animation. 300 */ 301 public void setSelectedPosition(int position) { 302 setSelectedPosition(position, true); 303 } 304 305 /** 306 * Sets the selected row position. 307 */ 308 public void setSelectedPosition(int position, boolean smooth) { 309 mSetSelectionRunnable.mPosition = position; 310 mSetSelectionRunnable.mSmooth = smooth; 311 if (getView() != null && getView().getHandler() != null) { 312 getView().getHandler().post(mSetSelectionRunnable); 313 } 314 } 315 316 private void onRowSelected(int selectedPosition, int selectedSubPosition) { 317 ObjectAdapter adapter = getAdapter(); 318 if (adapter == null || adapter.size() == 0 || 319 (selectedPosition == 0 && selectedSubPosition == 0)) { 320 showTitle(true); 321 } else { 322 showTitle(false); 323 } 324 if (adapter != null && adapter.size() > selectedPosition) { 325 final VerticalGridView gridView = getVerticalGridView(); 326 final int count = gridView.getChildCount(); 327 for (int i = 0; i < count; i++) { 328 ItemBridgeAdapter.ViewHolder bridgeViewHolder = (ItemBridgeAdapter.ViewHolder) 329 gridView.getChildViewHolder(gridView.getChildAt(i)); 330 RowPresenter rowPresenter = (RowPresenter) bridgeViewHolder.getPresenter(); 331 onSetRowStatus(rowPresenter, 332 rowPresenter.getRowViewHolder(bridgeViewHolder.getViewHolder()), 333 bridgeViewHolder.getAdapterPosition(), 334 selectedPosition, selectedSubPosition); 335 } 336 } 337 } 338 339 /** 340 * Called on every visible row to change view status when current selected row position 341 * or selected sub position changed. Subclass may override. The default 342 * implementation calls {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter, 343 * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)} if presenter is 344 * instance of {@link FullWidthDetailsOverviewRowPresenter}. 345 * 346 * @param presenter The presenter used to create row ViewHolder. 347 * @param viewHolder The visible (attached) row ViewHolder, note that it may or may not 348 * be selected. 349 * @param adapterPosition The adapter position of viewHolder inside adapter. 350 * @param selectedPosition The adapter position of currently selected row. 351 * @param selectedSubPosition The sub position within currently selected row. This is used 352 * When a row has multiple alignment positions. 353 */ 354 protected void onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int 355 adapterPosition, int selectedPosition, int selectedSubPosition) { 356 if (presenter instanceof FullWidthDetailsOverviewRowPresenter) { 357 onSetDetailsOverviewRowStatus((FullWidthDetailsOverviewRowPresenter) presenter, 358 (FullWidthDetailsOverviewRowPresenter.ViewHolder) viewHolder, 359 adapterPosition, selectedPosition, selectedSubPosition); 360 } 361 } 362 363 /** 364 * Called to change DetailsOverviewRow view status when current selected row position 365 * or selected sub position changed. Subclass may override. The default 366 * implementation switches between three states based on the positions: 367 * {@link FullWidthDetailsOverviewRowPresenter#STATE_HALF}, 368 * {@link FullWidthDetailsOverviewRowPresenter#STATE_FULL} and 369 * {@link FullWidthDetailsOverviewRowPresenter#STATE_SMALL}. 370 * 371 * @param presenter The presenter used to create row ViewHolder. 372 * @param viewHolder The visible (attached) row ViewHolder, note that it may or may not 373 * be selected. 374 * @param adapterPosition The adapter position of viewHolder inside adapter. 375 * @param selectedPosition The adapter position of currently selected row. 376 * @param selectedSubPosition The sub position within currently selected row. This is used 377 * When a row has multiple alignment positions. 378 */ 379 protected void onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter, 380 FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition, 381 int selectedPosition, int selectedSubPosition) { 382 if (selectedPosition > adapterPosition) { 383 presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF); 384 } else if (selectedPosition == adapterPosition && selectedSubPosition == 1) { 385 presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF); 386 } else if (selectedPosition == adapterPosition && selectedSubPosition == 0){ 387 presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_FULL); 388 } else { 389 presenter.setState(viewHolder, 390 FullWidthDetailsOverviewRowPresenter.STATE_SMALL); 391 } 392 } 393 394 @Override 395 public void onStart() { 396 super.onStart(); 397 setupChildFragmentLayout(); 398 setupFocusSearchListener(); 399 mRowsSupportFragment.getView().requestFocus(); 400 if (isEntranceTransitionEnabled()) { 401 // make sure recycler view animation is disabled 402 mRowsSupportFragment.onTransitionPrepare(); 403 mRowsSupportFragment.onTransitionStart(); 404 mRowsSupportFragment.setEntranceTransitionState(false); 405 } 406 } 407 408 @Override 409 protected Object createEntranceTransition() { 410 return sTransitionHelper.loadTransition(getActivity(), 411 R.transition.lb_details_enter_transition); 412 } 413 414 @Override 415 protected void runEntranceTransition(Object entranceTransition) { 416 sTransitionHelper.runTransition(mSceneAfterEntranceTransition, 417 entranceTransition); 418 } 419 420 @Override 421 protected void onEntranceTransitionEnd() { 422 mRowsSupportFragment.onTransitionEnd(); 423 } 424 425} 426