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