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