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