DetailsSupportFragment.java revision 0908efd712e79f77e0cf9307bd5c32753c855561
1// CHECKSTYLE:OFF Generated code
2/* This file is auto-generated from DetailsFragment.java.  DO NOT MODIFY. */
3
4/*
5 * Copyright (C) 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software distributed under the License
13 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14 * or implied. See the License for the specific language governing permissions and limitations under
15 * the License.
16 */
17package android.support.v17.leanback.app;
18
19import android.support.v4.app.Fragment;
20import android.support.v4.app.FragmentTransaction;
21import android.os.Bundle;
22import android.support.v17.leanback.R;
23import android.support.v17.leanback.transition.TransitionHelper;
24import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
25import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
26import android.support.v17.leanback.widget.BrowseFrameLayout;
27import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
28import android.support.v17.leanback.widget.ItemAlignmentFacet;
29import android.support.v17.leanback.widget.ItemBridgeAdapter;
30import android.support.v17.leanback.widget.ObjectAdapter;
31import android.support.v17.leanback.widget.Presenter;
32import android.support.v17.leanback.widget.PresenterSelector;
33import android.support.v17.leanback.widget.RowPresenter;
34import android.support.v17.leanback.widget.VerticalGridView;
35import android.util.Log;
36import android.view.KeyEvent;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
40
41/**
42 * A fragment for creating Leanback details screens.
43 *
44 * <p>
45 * A DetailsSupportFragment renders the elements of its {@link ObjectAdapter} as a set
46 * of rows in a vertical list.The Adapter's {@link PresenterSelector} must maintain 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    static final String TAG = "DetailsSupportFragment";
77    static boolean DEBUG = false;
78
79    private class SetSelectionRunnable implements Runnable {
80        int mPosition;
81        boolean mSmooth = true;
82
83        SetSelectionRunnable() {
84        }
85
86        @Override
87        public void run() {
88            if (mRowsSupportFragment == null) {
89                return;
90            }
91            mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
92        }
93    }
94
95    BrowseFrameLayout mRootView;
96    Fragment mVideoSupportFragment;
97    DetailsParallaxManager mDetailsParallaxManager;
98    RowsSupportFragment mRowsSupportFragment;
99    ObjectAdapter mAdapter;
100    int mContainerListAlignTop;
101    BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
102    BaseOnItemViewClickedListener mOnItemViewClickedListener;
103
104    Object mSceneAfterEntranceTransition;
105
106    final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
107
108    final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
109            new BaseOnItemViewSelectedListener<Object>() {
110        @Override
111        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
112                                   RowPresenter.ViewHolder rowViewHolder, Object row) {
113            int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
114            int subposition = mRowsSupportFragment.getVerticalGridView().getSelectedSubPosition();
115            if (DEBUG) Log.v(TAG, "row selected position " + position
116                    + " subposition " + subposition);
117            onRowSelected(position, subposition);
118            if (mExternalOnItemViewSelectedListener != null) {
119                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
120                        rowViewHolder, row);
121            }
122        }
123    };
124
125    /**
126     * Sets the list of rows for the fragment.
127     */
128    public void setAdapter(ObjectAdapter adapter) {
129        mAdapter = adapter;
130        Presenter[] presenters = adapter.getPresenterSelector().getPresenters();
131        if (presenters != null) {
132            for (int i = 0; i < presenters.length; i++) {
133                setupPresenter(presenters[i]);
134            }
135        } else {
136            Log.e(TAG, "PresenterSelector.getPresenters() not implemented");
137        }
138        if (mRowsSupportFragment != null) {
139            mRowsSupportFragment.setAdapter(adapter);
140        }
141    }
142
143    /**
144     * Returns the list of rows.
145     */
146    public ObjectAdapter getAdapter() {
147        return mAdapter;
148    }
149
150    /**
151     * Sets an item selection listener.
152     */
153    public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
154        mExternalOnItemViewSelectedListener = listener;
155    }
156
157    /**
158     * Sets an item clicked listener.
159     */
160    public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
161        if (mOnItemViewClickedListener != listener) {
162            mOnItemViewClickedListener = listener;
163            if (mRowsSupportFragment != null) {
164                mRowsSupportFragment.setOnItemViewClickedListener(listener);
165            }
166        }
167    }
168
169    /**
170     * Returns the item clicked listener.
171     */
172    public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
173        return mOnItemViewClickedListener;
174    }
175
176    @Override
177    public void onCreate(Bundle savedInstanceState) {
178        super.onCreate(savedInstanceState);
179        mContainerListAlignTop =
180            getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
181    }
182
183    @Override
184    public View onCreateView(LayoutInflater inflater, ViewGroup container,
185            Bundle savedInstanceState) {
186        mRootView = (BrowseFrameLayout) inflater.inflate(
187                R.layout.lb_details_fragment, container, false);
188        mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
189                R.id.details_rows_dock);
190        if (mRowsSupportFragment == null) {
191            mRowsSupportFragment = new RowsSupportFragment();
192            getChildFragmentManager().beginTransaction()
193                    .replace(R.id.details_rows_dock, mRowsSupportFragment).commit();
194        }
195        mRowsSupportFragment.setAdapter(mAdapter);
196        mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
197        mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
198
199        mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
200            @Override
201            public void run() {
202                mRowsSupportFragment.setEntranceTransitionState(true);
203            }
204        });
205
206        setupVideoPlayback();
207
208        return mRootView;
209    }
210
211
212    /**
213     * @deprecated override {@link #onInflateTitleView(LayoutInflater,ViewGroup,Bundle)} instead.
214     */
215    @Deprecated
216    protected View inflateTitle(LayoutInflater inflater, ViewGroup parent,
217            Bundle savedInstanceState) {
218        return super.onInflateTitleView(inflater, parent, savedInstanceState);
219    }
220
221    @Override
222    public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
223                                   Bundle savedInstanceState) {
224        return inflateTitle(inflater, parent, savedInstanceState);
225    }
226
227    void setVerticalGridViewLayout(VerticalGridView listview) {
228        // align the top edge of item to a fixed position
229        listview.setItemAlignmentOffset(-mContainerListAlignTop);
230        listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
231        listview.setWindowAlignmentOffset(0);
232        listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
233        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
234    }
235
236    /**
237     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
238     * that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.
239     * For example how a row is aligned in details Fragment.   The default implementation invokes
240     * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
241     *
242     */
243    protected void setupPresenter(Presenter rowPresenter) {
244        if (rowPresenter instanceof FullWidthDetailsOverviewRowPresenter) {
245            setupDetailsOverviewRowPresenter((FullWidthDetailsOverviewRowPresenter) rowPresenter);
246        }
247    }
248
249    /**
250     * Called to setup {@link FullWidthDetailsOverviewRowPresenter}.  The default implementation
251     * adds two alignment positions({@link ItemAlignmentFacet}) for ViewHolder of
252     * FullWidthDetailsOverviewRowPresenter to align in fragment.
253     */
254    protected void setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter) {
255        ItemAlignmentFacet facet = new ItemAlignmentFacet();
256        // by default align details_frame to half window height
257        ItemAlignmentFacet.ItemAlignmentDef alignDef1 = new ItemAlignmentFacet.ItemAlignmentDef();
258        alignDef1.setItemAlignmentViewId(R.id.details_frame);
259        alignDef1.setItemAlignmentOffset(- getResources()
260                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions));
261        alignDef1.setItemAlignmentOffsetPercent(0);
262        // when description is selected, align details_frame to top edge
263        ItemAlignmentFacet.ItemAlignmentDef alignDef2 = new ItemAlignmentFacet.ItemAlignmentDef();
264        alignDef2.setItemAlignmentViewId(R.id.details_frame);
265        alignDef2.setItemAlignmentFocusViewId(R.id.details_overview_description);
266        alignDef2.setItemAlignmentOffset(- getResources()
267                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description));
268        alignDef2.setItemAlignmentOffsetPercent(0);
269        ItemAlignmentFacet.ItemAlignmentDef[] defs =
270                new ItemAlignmentFacet.ItemAlignmentDef[] {alignDef1, alignDef2};
271        facet.setAlignmentDefs(defs);
272        presenter.setFacet(ItemAlignmentFacet.class, facet);
273    }
274
275    VerticalGridView getVerticalGridView() {
276        return mRowsSupportFragment == null ? null : mRowsSupportFragment.getVerticalGridView();
277    }
278
279    /**
280     * Gets embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.  If view of
281     * DetailsSupportFragment is not created, the method returns null.
282     * @return Embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.
283     */
284    public RowsSupportFragment getRowsSupportFragment() {
285        return mRowsSupportFragment;
286    }
287
288    /**
289     * Setup dimensions that are only meaningful when the child Fragments are inside
290     * DetailsSupportFragment.
291     */
292    private void setupChildFragmentLayout() {
293        setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
294    }
295
296    /**
297     * Sets the selected row position with smooth animation.
298     */
299    public void setSelectedPosition(int position) {
300        setSelectedPosition(position, true);
301    }
302
303    /**
304     * Sets the selected row position.
305     */
306    public void setSelectedPosition(int position, boolean smooth) {
307        mSetSelectionRunnable.mPosition = position;
308        mSetSelectionRunnable.mSmooth = smooth;
309        if (getView() != null && getView().getHandler() != null) {
310            getView().getHandler().post(mSetSelectionRunnable);
311        }
312    }
313
314    /**
315     * Creates an instance of {@link VideoSupportFragment}. Subclasses can override this method
316     * and provide their own instance of a {@link Fragment}. When you provide your own instance of
317     * video fragment, you MUST also provide a custom
318     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
319     */
320    public Fragment onCreateVideoSupportFragment() {
321        return new VideoSupportFragment();
322    }
323
324    /**
325     * Creates an instance of
326     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}. The implementation
327     * of this host depends on the instance of video fragment {@link #onCreateVideoSupportFragment()}.
328     */
329    public PlaybackGlue.PlaybackGlueHost onCreateVideoSupportFragmentHost(Fragment fragment) {
330        return new VideoSupportFragmentGlueHost((VideoSupportFragment) fragment);
331    }
332
333    /**
334     * This method adds a fragment for rendering video to the layout. In case the
335     * fragment is being restored, it will return the video fragment in there.
336     *
337     * @return Fragment the added or restored fragment responsible for rendering video.
338     */
339    public final Fragment findOrCreateVideoSupportFragment() {
340        Fragment fragment = getFragmentManager().findFragmentById(R.id.video_surface_container);
341        if (fragment == null) {
342            FragmentTransaction ft2 = getFragmentManager().beginTransaction();
343            ft2.add(android.support.v17.leanback.R.id.video_surface_container,
344                    fragment = onCreateVideoSupportFragment());
345            ft2.commit();
346        }
347        mVideoSupportFragment = fragment;
348        return mVideoSupportFragment;
349    }
350
351    /**
352     * This method initializes a video fragment, create an instance of
353     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} using that fragment
354     * and return it.
355     */
356    public final PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost() {
357        Fragment fragment = findOrCreateVideoSupportFragment();
358        return onCreateVideoSupportFragmentHost(fragment);
359    }
360
361    void onRowSelected(int selectedPosition, int selectedSubPosition) {
362        ObjectAdapter adapter = getAdapter();
363        if (adapter == null || adapter.size() == 0
364                || (selectedPosition == 0 && selectedSubPosition == 0)) {
365            showTitle(true);
366        } else {
367            showTitle(false);
368        }
369        if (adapter != null && adapter.size() > selectedPosition) {
370            final VerticalGridView gridView = getVerticalGridView();
371            final int count = gridView.getChildCount();
372            for (int i = 0; i < count; i++) {
373                ItemBridgeAdapter.ViewHolder bridgeViewHolder = (ItemBridgeAdapter.ViewHolder)
374                        gridView.getChildViewHolder(gridView.getChildAt(i));
375                RowPresenter rowPresenter = (RowPresenter) bridgeViewHolder.getPresenter();
376                onSetRowStatus(rowPresenter,
377                        rowPresenter.getRowViewHolder(bridgeViewHolder.getViewHolder()),
378                        bridgeViewHolder.getAdapterPosition(),
379                        selectedPosition, selectedSubPosition);
380            }
381        }
382    }
383
384    /**
385     * Called on every visible row to change view status when current selected row position
386     * or selected sub position changed.  Subclass may override.   The default
387     * implementation calls {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
388     * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)} if presenter is
389     * instance of {@link FullWidthDetailsOverviewRowPresenter}.
390     *
391     * @param presenter   The presenter used to create row ViewHolder.
392     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
393     *                    be selected.
394     * @param adapterPosition  The adapter position of viewHolder inside adapter.
395     * @param selectedPosition The adapter position of currently selected row.
396     * @param selectedSubPosition The sub position within currently selected row.  This is used
397     *                            When a row has multiple alignment positions.
398     */
399    protected void onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int
400            adapterPosition, int selectedPosition, int selectedSubPosition) {
401        if (presenter instanceof FullWidthDetailsOverviewRowPresenter) {
402            onSetDetailsOverviewRowStatus((FullWidthDetailsOverviewRowPresenter) presenter,
403                    (FullWidthDetailsOverviewRowPresenter.ViewHolder) viewHolder,
404                    adapterPosition, selectedPosition, selectedSubPosition);
405        }
406    }
407
408    /**
409     * Called to change DetailsOverviewRow view status when current selected row position
410     * or selected sub position changed.  Subclass may override.   The default
411     * implementation switches between three states based on the positions:
412     * {@link FullWidthDetailsOverviewRowPresenter#STATE_HALF},
413     * {@link FullWidthDetailsOverviewRowPresenter#STATE_FULL} and
414     * {@link FullWidthDetailsOverviewRowPresenter#STATE_SMALL}.
415     *
416     * @param presenter   The presenter used to create row ViewHolder.
417     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
418     *                    be selected.
419     * @param adapterPosition  The adapter position of viewHolder inside adapter.
420     * @param selectedPosition The adapter position of currently selected row.
421     * @param selectedSubPosition The sub position within currently selected row.  This is used
422     *                            When a row has multiple alignment positions.
423     */
424    protected void onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter,
425            FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition,
426            int selectedPosition, int selectedSubPosition) {
427        if (selectedPosition > adapterPosition) {
428            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
429        } else if (selectedPosition == adapterPosition && selectedSubPosition == 1) {
430            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
431        } else if (selectedPosition == adapterPosition && selectedSubPosition == 0){
432            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_FULL);
433        } else {
434            presenter.setState(viewHolder,
435                    FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
436        }
437    }
438
439    @Override
440    public void onStart() {
441        super.onStart();
442        setupChildFragmentLayout();
443        if (isEntranceTransitionEnabled()) {
444            mRowsSupportFragment.setEntranceTransitionState(false);
445        }
446        if (mDetailsParallaxManager != null) {
447            mDetailsParallaxManager.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
448        }
449        mRowsSupportFragment.getVerticalGridView().requestFocus();
450    }
451
452    @Override
453    protected Object createEntranceTransition() {
454        return TransitionHelper.loadTransition(getActivity(),
455                R.transition.lb_details_enter_transition);
456    }
457
458    @Override
459    protected void runEntranceTransition(Object entranceTransition) {
460        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
461    }
462
463    @Override
464    protected void onEntranceTransitionEnd() {
465        mRowsSupportFragment.onTransitionEnd();
466    }
467
468    @Override
469    protected void onEntranceTransitionPrepare() {
470        mRowsSupportFragment.onTransitionPrepare();
471    }
472
473    @Override
474    protected void onEntranceTransitionStart() {
475        mRowsSupportFragment.onTransitionStart();
476    }
477
478    /**
479     * Create a DetailsParallaxManager that will be used to configure parallax effect of background
480     * and start/stop Video playback. Subclass may override.
481     *
482     * @return The new created DetailsParallaxManager.
483     * @see #getParallaxManager()
484     */
485    public DetailsParallaxManager onCreateParallaxManager() {
486        return new DetailsParallaxManager();
487    }
488
489    /**
490     * Returns the {@link DetailsParallaxManager} instance used to configure parallax effect of
491     * background.
492     *
493     * @return The DetailsParallaxManager instance attached to the DetailsSupportFragment.
494     * @see #onCreateParallaxManager()
495     */
496    public DetailsParallaxManager getParallaxManager() {
497        if (mDetailsParallaxManager == null) {
498            mDetailsParallaxManager = onCreateParallaxManager();
499            if (mRowsSupportFragment != null) {
500                mDetailsParallaxManager.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
501            }
502        }
503        return mDetailsParallaxManager;
504    }
505
506    /**
507     * Returns background View that above VideoSupportFragment. App can set a background drawable to this
508     * view to hide the VideoSupportFragment before it is ready to play.
509     *
510     * @see #findOrCreateVideoSupportFragment()
511     */
512    public View getBackgroundView() {
513        return mRootView == null ? null : mRootView.findViewById(R.id.details_background_view);
514    }
515
516    /**
517     * This method does the following
518     * <ul>
519     * <li>sets up focus search handling logic in the root view to enable transitioning between
520     * half screen/full screen/no video mode.</li>
521     *
522     * <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
523     * transition to appropriate mode like half/full screen video.</li>
524     * </ul>
525     */
526    void setupVideoPlayback() {
527        mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
528            @Override
529            public View onFocusSearch(View focused, int direction) {
530                if (mVideoSupportFragment == null) {
531                    return null;
532                }
533                if (mRowsSupportFragment.getVerticalGridView() != null
534                        && mRowsSupportFragment.getVerticalGridView().hasFocus()) {
535                    if (direction == View.FOCUS_UP) {
536                        slideOutGridView();
537                        return mVideoSupportFragment.getView();
538                    }
539                } else if (mVideoSupportFragment.getView() != null
540                        && mVideoSupportFragment.getView().hasFocus()) {
541                    if (direction == View.FOCUS_DOWN) {
542                        slideInGridView();
543                        return mRowsSupportFragment.getVerticalGridView();
544                    }
545                }
546                return focused;
547            }
548        });
549
550        // If we press BACK or DOWN on remote while in full screen video mode, we should
551        // transition back to half screen video playback mode.
552        mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
553            @Override
554            public boolean onKey(View v, int keyCode, KeyEvent event) {
555                // This is used to check if we are in full screen video mode. This is somewhat
556                // hacky and relies on the behavior of the video helper class to update the
557                // focusability of the video surface view.
558                if (mVideoSupportFragment != null && mVideoSupportFragment.getView() != null
559                        && mVideoSupportFragment.getView().hasFocus()) {
560                    if (keyCode == KeyEvent.KEYCODE_BACK) {
561                        slideInGridView();
562                        getVerticalGridView().requestFocus();
563                        return true;
564                    }
565                }
566
567                return false;
568            }
569        });
570    }
571
572    /**
573     * Slides vertical grid view (displaying media item details) out of the screen from below.
574     */
575    void slideOutGridView() {
576        getVerticalGridView().animateOut();
577    }
578
579    /**
580     * Slides in vertical grid view (displaying media item details) from below.
581     */
582    void slideInGridView() {
583        getVerticalGridView().animateIn();
584    }
585}
586