public class

DetailsSupportFragmentBackgroundController

extends java.lang.Object

 java.lang.Object

↳androidx.leanback.app.DetailsSupportFragmentBackgroundController

Gradle dependencies

compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha02'

  • groupId: androidx.leanback
  • artifactId: leanback
  • version: 1.2.0-alpha02

Artifact androidx.leanback:leanback:1.2.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.leanback:leanback com.android.support:leanback-v17

Androidx class mapping:

androidx.leanback.app.DetailsSupportFragmentBackgroundController android.support.v17.leanback.app.DetailsSupportFragmentBackgroundController

Overview

Controller for DetailsSupportFragment parallax background and embedded video play.

The parallax background drawable is made of two parts: cover drawable (by default FitWidthBitmapDrawable) above the details overview row and bottom drawable (by default ) below the details overview row. While vertically scrolling rows, the size of cover drawable and bottom drawable will be updated and the cover drawable will by default perform a parallax shift using FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET.

        ***************************
        *      Cover Drawable     *
        * (FitWidthBitmapDrawable)*
        *                         *
        ***************************
        *    DetailsOverviewRow   *
        *                         *
        ***************************
        *     Bottom Drawable     *
        *      (ColorDrawable)    *
        *         Related         *
        *         Content         *
        ***************************
 
Both parallax background drawable and embedded video play are optional. App must call DetailsSupportFragmentBackgroundController.enableParallax() and/or DetailsSupportFragmentBackgroundController.setupVideoPlayback(PlaybackGlue) explicitly. The PlaybackGlue is automatically PlaybackGlue.play() when fragment starts and PlaybackGlue.pause() when fragment stops. When video is ready to play, cover drawable will be faded out. Example:
 DetailsSupportFragmentBackgroundController mController = new DetailsSupportFragmentBackgroundController(this);

 public void onCreate(Bundle savedInstance) {
     super.onCreate(savedInstance);
     MediaPlayerGlue player = new MediaPlayerGlue(..);
     player.setUrl(...);
     mController.enableParallax();
     mController.setupVideoPlayback(player);
 }

 static class MyLoadBitmapTask extends ... {
     WeakReference mFragmentRef;
     MyLoadBitmapTask(MyFragment fragment) {
         mFragmentRef = new WeakReference(fragment);
     }
     protected void onPostExecute(Bitmap bitmap) {
         MyFragment fragment = mFragmentRef.get();
         if (fragment != null) {
             fragment.mController.setCoverBitmap(bitmap);
         }
     }
 }

 public void onStart() {
     new MyLoadBitmapTask(this).execute(url);
 }

 public void onStop() {
     mController.setCoverBitmap(null);
 }
 

To customize cover drawable and/or bottom drawable, app should call DetailsSupportFragmentBackgroundController.enableParallax(Drawable, Drawable, ParallaxTarget.PropertyValuesHolderTarget). If app supplies a custom cover Drawable, it should not call DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap). If app supplies a custom bottom Drawable, it should not call DetailsSupportFragmentBackgroundController.setSolidColor(int).

To customize playback fragment, app should override DetailsSupportFragmentBackgroundController.onCreateVideoSupportFragment() and DetailsSupportFragmentBackgroundController.onCreateGlueHost().

Summary

Constructors
publicDetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment)

Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment.

Methods
public booleancanNavigateToVideoSupportFragment()

Precondition allows user navigate to video fragment using DPAD.

public voidenableParallax()

Enables default parallax background using a FitWidthBitmapDrawable as cover drawable and as bottom drawable.

public voidenableParallax(Drawable coverDrawable, Drawable bottomDrawable, ParallaxTarget.PropertyValuesHolderTarget coverDrawableParallaxTarget)

Enables parallax background using a custom cover drawable at top and a custom bottom drawable.

public final FragmentfindOrCreateVideoSupportFragment()

Adds or gets fragment for rendering video in DetailsSupportFragment.

public final DrawablegetBottomDrawable()

Returns the drawable at bottom.

public final BitmapgetCoverBitmap()

Returns Bitmap set by DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap).

public final DrawablegetCoverDrawable()

Returns the cover drawable at top.

public final intgetParallaxDrawableMaxOffset()

Returns Default parallax offset in pixels for bitmap moving vertically.

public final PlaybackGluegetPlaybackGlue()

Returns current PlaybackGlue or null if not set or cleared.

public final intgetSolidColor()

Returns color set by DetailsSupportFragmentBackgroundController.setSolidColor(int).

public PlaybackGlueHostonCreateGlueHost()

Creates a PlaybackGlueHost to host PlaybackGlue.

public FragmentonCreateVideoSupportFragment()

Creates a Fragment to host PlaybackGlue.

public final voidsetCoverBitmap(Bitmap bitmap)

Convenient method to set Bitmap in cover drawable.

public final voidsetParallaxDrawableMaxOffset(int offset)

Sets default parallax offset in pixels for bitmap moving vertically.

public final voidsetSolidColor(int color)

Convenient method to set color in bottom drawable.

public voidsetupVideoPlayback(PlaybackGlue playbackGlue)

Enable video playback and set proper PlaybackGlueHost.

public final voidswitchToRows()

Switch to rows fragment.

public final voidswitchToVideo()

Switch to video fragment, note that this method is not affected by result of DetailsSupportFragmentBackgroundController.canNavigateToVideoSupportFragment().

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public DetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment)

Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment. Note that each DetailsSupportFragment can only associate with one DetailsSupportFragmentBackgroundController.

Parameters:

fragment: The DetailsSupportFragment to control background and embedded video playing.

Methods

public void enableParallax()

Enables default parallax background using a FitWidthBitmapDrawable as cover drawable and as bottom drawable. A vertical parallax movement will be applied to the FitWidthBitmapDrawable. App may use DetailsSupportFragmentBackgroundController.setSolidColor(int) and DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap) to change the content of bottom drawable and cover drawable. This method must be called before DetailsSupportFragmentBackgroundController.setupVideoPlayback(PlaybackGlue).

See also: DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap), DetailsSupportFragmentBackgroundController.setSolidColor(int)

public void enableParallax(Drawable coverDrawable, Drawable bottomDrawable, ParallaxTarget.PropertyValuesHolderTarget coverDrawableParallaxTarget)

Enables parallax background using a custom cover drawable at top and a custom bottom drawable. This method must be called before DetailsSupportFragmentBackgroundController.setupVideoPlayback(PlaybackGlue).

Parameters:

coverDrawable: Custom cover drawable shown at top. DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap) will not work if coverDrawable is not FitWidthBitmapDrawable; in that case it's app's responsibility to set content into coverDrawable.
bottomDrawable: Drawable shown at bottom. DetailsSupportFragmentBackgroundController.setSolidColor(int) will not work if bottomDrawable is not ; in that case it's app's responsibility to set content of bottomDrawable.
coverDrawableParallaxTarget: Target to perform parallax effect within coverDrawable. Use null for no parallax movement effect. Example to move bitmap within FitWidthBitmapDrawable: new ParallaxTarget.PropertyValuesHolderTarget( coverDrawable, PropertyValuesHolder.ofInt( FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET, 0, -120))

public void setupVideoPlayback(PlaybackGlue playbackGlue)

Enable video playback and set proper PlaybackGlueHost. This method by default creates a VideoSupportFragment and VideoSupportFragmentGlueHost to host the PlaybackGlue. This method must be called after calling details Fragment super.onCreate(). This method can be called multiple times to replace existing PlaybackGlue or calling setupVideoPlayback(null) to clear. Note a typical PlaybackGlue subclass releases resources in PlaybackGlue.onDetachedFromHost(), when the PlaybackGlue subclass is not doing that, it's app's responsibility to release the resources.

Parameters:

playbackGlue: The new PlaybackGlue to set as background or null to clear existing one.

See also: DetailsSupportFragmentBackgroundController.onCreateVideoSupportFragment(), DetailsSupportFragmentBackgroundController.onCreateGlueHost()

public final PlaybackGlue getPlaybackGlue()

Returns current PlaybackGlue or null if not set or cleared.

Returns:

Current PlaybackGlue or null

public boolean canNavigateToVideoSupportFragment()

Precondition allows user navigate to video fragment using DPAD. Default implementation returns true if PlaybackGlue is not null. Subclass may override, e.g. only allow navigation when PlaybackGlue.isPrepared() is true. Note this method does not block app calls DetailsSupportFragmentBackgroundController.switchToVideo().

Returns:

True allow to navigate to video fragment.

public final void switchToVideo()

Switch to video fragment, note that this method is not affected by result of DetailsSupportFragmentBackgroundController.canNavigateToVideoSupportFragment(). If the method is called in DetailsSupportFragment.onCreate() it will make video fragment to be initially focused once it is created.

Calling switchToVideo() in DetailsSupportFragment.onCreate() will clear the activity enter transition and shared element transition.

If switchToVideo() is called after BaseSupportFragment.prepareEntranceTransition() and before DetailsSupportFragment.onEntranceTransitionEnd(), it will be ignored.

If BaseSupportFragment.prepareEntranceTransition() is called after switchToVideo(), an IllegalStateException will be thrown.

public final void switchToRows()

Switch to rows fragment.

public final Drawable getCoverDrawable()

Returns the cover drawable at top. Returns null if DetailsSupportFragmentBackgroundController.enableParallax() is not called. By default it's a FitWidthBitmapDrawable.

Returns:

The cover drawable at top.

public final Drawable getBottomDrawable()

Returns the drawable at bottom. Returns null if DetailsSupportFragmentBackgroundController.enableParallax() is not called. By default it's a .

Returns:

The bottom drawable.

public Fragment onCreateVideoSupportFragment()

Creates a Fragment to host PlaybackGlue. Returns a new VideoSupportFragment by default. App may override and return a different fragment and it also must override DetailsSupportFragmentBackgroundController.onCreateGlueHost().

Returns:

A new fragment used in DetailsSupportFragmentBackgroundController.onCreateGlueHost().

See also: DetailsSupportFragmentBackgroundController.onCreateGlueHost(), DetailsSupportFragmentBackgroundController.setupVideoPlayback(PlaybackGlue)

public PlaybackGlueHost onCreateGlueHost()

Creates a PlaybackGlueHost to host PlaybackGlue. App may override this if it overrides DetailsSupportFragmentBackgroundController.onCreateVideoSupportFragment(). This method must be called after calling Fragment super.onCreate(). When override this method, app may call DetailsSupportFragmentBackgroundController.findOrCreateVideoSupportFragment() to get or create a fragment.

Returns:

A new PlaybackGlueHost to host PlaybackGlue.

See also: DetailsSupportFragmentBackgroundController.onCreateVideoSupportFragment(), DetailsSupportFragmentBackgroundController.findOrCreateVideoSupportFragment(), DetailsSupportFragmentBackgroundController.setupVideoPlayback(PlaybackGlue)

public final Fragment findOrCreateVideoSupportFragment()

Adds or gets fragment for rendering video in DetailsSupportFragment. A subclass that overrides DetailsSupportFragmentBackgroundController.onCreateGlueHost() should call this method to get a fragment for creating a PlaybackGlueHost.

Returns:

Fragment the added or restored fragment responsible for rendering video.

See also: DetailsSupportFragmentBackgroundController.onCreateGlueHost()

public final void setCoverBitmap(Bitmap bitmap)

Convenient method to set Bitmap in cover drawable. If app is not using default FitWidthBitmapDrawable, app should not use this method It's safe to call setCoverBitmap() before calling DetailsSupportFragmentBackgroundController.enableParallax().

Parameters:

bitmap: bitmap to set as cover.

public final Bitmap getCoverBitmap()

Returns Bitmap set by DetailsSupportFragmentBackgroundController.setCoverBitmap(Bitmap).

Returns:

Bitmap for cover drawable.

public final int getSolidColor()

Returns color set by DetailsSupportFragmentBackgroundController.setSolidColor(int).

Returns:

Solid color used for bottom drawable.

public final void setSolidColor(int color)

Convenient method to set color in bottom drawable. If app is not using default , app should not use this method. It's safe to call setSolidColor() before calling DetailsSupportFragmentBackgroundController.enableParallax().

Parameters:

color: color for bottom drawable.

public final void setParallaxDrawableMaxOffset(int offset)

Sets default parallax offset in pixels for bitmap moving vertically. This method must be called before DetailsSupportFragmentBackgroundController.enableParallax().

Parameters:

offset: Offset in pixels (e.g. 120).

See also: DetailsSupportFragmentBackgroundController.enableParallax()

public final int getParallaxDrawableMaxOffset()

Returns Default parallax offset in pixels for bitmap moving vertically. When 0, a default value would be used.

Returns:

Default parallax offset in pixels for bitmap moving vertically.

See also: DetailsSupportFragmentBackgroundController.enableParallax()

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.leanback.app;

import android.animation.PropertyValuesHolder;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.leanback.R;
import androidx.leanback.graphics.FitWidthBitmapDrawable;
import androidx.leanback.media.PlaybackGlue;
import androidx.leanback.media.PlaybackGlueHost;
import androidx.leanback.widget.DetailsParallaxDrawable;
import androidx.leanback.widget.ParallaxTarget;

/**
 * Controller for DetailsSupportFragment parallax background and embedded video play.
 * <p>
 * The parallax background drawable is made of two parts: cover drawable (by default
 * {@link FitWidthBitmapDrawable}) above the details overview row and bottom drawable (by default
 * {@link ColorDrawable}) below the details overview row. While vertically scrolling rows, the size
 * of cover drawable and bottom drawable will be updated and the cover drawable will by default
 * perform a parallax shift using {@link FitWidthBitmapDrawable#PROPERTY_VERTICAL_OFFSET}.
 * </p>
 * <pre>
 *        ***************************
 *        *      Cover Drawable     *
 *        * (FitWidthBitmapDrawable)*
 *        *                         *
 *        ***************************
 *        *    DetailsOverviewRow   *
 *        *                         *
 *        ***************************
 *        *     Bottom Drawable     *
 *        *      (ColorDrawable)    *
 *        *         Related         *
 *        *         Content         *
 *        ***************************
 * </pre>
 * Both parallax background drawable and embedded video play are optional. App must call
 * {@link #enableParallax()} and/or {@link #setupVideoPlayback(PlaybackGlue)} explicitly.
 * The PlaybackGlue is automatically {@link PlaybackGlue#play()} when fragment starts and
 * {@link PlaybackGlue#pause()} when fragment stops. When video is ready to play, cover drawable
 * will be faded out.
 * Example:
 * <pre>
 * DetailsSupportFragmentBackgroundController mController = new DetailsSupportFragmentBackgroundController(this);
 *
 * public void onCreate(Bundle savedInstance) {
 *     super.onCreate(savedInstance);
 *     MediaPlayerGlue player = new MediaPlayerGlue(..);
 *     player.setUrl(...);
 *     mController.enableParallax();
 *     mController.setupVideoPlayback(player);
 * }
 *
 * static class MyLoadBitmapTask extends ... {
 *     WeakReference<MyFragment> mFragmentRef;
 *     MyLoadBitmapTask(MyFragment fragment) {
 *         mFragmentRef = new WeakReference(fragment);
 *     }
 *     protected void onPostExecute(Bitmap bitmap) {
 *         MyFragment fragment = mFragmentRef.get();
 *         if (fragment != null) {
 *             fragment.mController.setCoverBitmap(bitmap);
 *         }
 *     }
 * }
 *
 * public void onStart() {
 *     new MyLoadBitmapTask(this).execute(url);
 * }
 *
 * public void onStop() {
 *     mController.setCoverBitmap(null);
 * }
 * </pre>
 * <p>
 * To customize cover drawable and/or bottom drawable, app should call
 * {@link #enableParallax(Drawable, Drawable, ParallaxTarget.PropertyValuesHolderTarget)}.
 * If app supplies a custom cover Drawable, it should not call {@link #setCoverBitmap(Bitmap)}.
 * If app supplies a custom bottom Drawable, it should not call {@link #setSolidColor(int)}.
 * </p>
 * <p>
 * To customize playback fragment, app should override {@link #onCreateVideoSupportFragment()} and
 * {@link #onCreateGlueHost()}.
 * </p>
 *
 */
public class DetailsSupportFragmentBackgroundController {

    final DetailsSupportFragment mFragment;
    DetailsParallaxDrawable mParallaxDrawable;
    int mParallaxDrawableMaxOffset;
    PlaybackGlue mPlaybackGlue;
    DetailsBackgroundVideoHelper mVideoHelper;
    Bitmap mCoverBitmap;
    int mSolidColor;
    boolean mCanUseHost = false;
    boolean mInitialControlVisible = false;

    private Fragment mLastVideoSupportFragmentForGlueHost;

    /**
     * Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment. Note that
     * each DetailsSupportFragment can only associate with one DetailsSupportFragmentBackgroundController.
     *
     * @param fragment The DetailsSupportFragment to control background and embedded video playing.
     * @throws IllegalStateException If fragment was already associated with another controller.
     */
    public DetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment) {
        if (fragment.mDetailsBackgroundController != null) {
            throw new IllegalStateException("Each DetailsSupportFragment is allowed to initialize "
                    + "DetailsSupportFragmentBackgroundController once");
        }
        fragment.mDetailsBackgroundController = this;
        mFragment = fragment;
    }

    /**
     * Enables default parallax background using a {@link FitWidthBitmapDrawable} as cover drawable
     * and {@link ColorDrawable} as bottom drawable. A vertical parallax movement will be applied
     * to the FitWidthBitmapDrawable. App may use {@link #setSolidColor(int)} and
     * {@link #setCoverBitmap(Bitmap)} to change the content of bottom drawable and cover drawable.
     * This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
     *
     * @see #setCoverBitmap(Bitmap)
     * @see #setSolidColor(int)
     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
     */
    public void enableParallax() {
        int offset = mParallaxDrawableMaxOffset;
        if (offset == 0) {
            offset = mFragment.getContext().getResources()
                    .getDimensionPixelSize(R.dimen.lb_details_cover_drawable_parallax_movement);
        }
        Drawable coverDrawable = new FitWidthBitmapDrawable();
        ColorDrawable colorDrawable = new ColorDrawable();
        enableParallax(coverDrawable, colorDrawable,
                new ParallaxTarget.PropertyValuesHolderTarget(
                        coverDrawable,
                        PropertyValuesHolder.ofInt(FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
                                0, -offset)
                ));
    }

    /**
     * Enables parallax background using a custom cover drawable at top and a custom bottom
     * drawable. This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
     *
     * @param coverDrawable Custom cover drawable shown at top. {@link #setCoverBitmap(Bitmap)}
     *                      will not work if coverDrawable is not {@link FitWidthBitmapDrawable};
     *                      in that case it's app's responsibility to set content into
     *                      coverDrawable.
     * @param bottomDrawable Drawable shown at bottom. {@link #setSolidColor(int)} will not work
     *                       if bottomDrawable is not {@link ColorDrawable}; in that case it's app's
     *                       responsibility to set content of bottomDrawable.
     * @param coverDrawableParallaxTarget Target to perform parallax effect within coverDrawable.
     *                                    Use null for no parallax movement effect.
     *                                    Example to move bitmap within FitWidthBitmapDrawable:
     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
     *                                        coverDrawable, PropertyValuesHolder.ofInt(
     *                                            FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
     *                                            0, -120))
     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
     */
    public void enableParallax(@NonNull Drawable coverDrawable, @NonNull Drawable bottomDrawable,
                               @Nullable ParallaxTarget.PropertyValuesHolderTarget
                                       coverDrawableParallaxTarget) {
        if (mParallaxDrawable != null) {
            return;
        }
        // if bitmap is set before enableParallax, use it as initial value.
        if (mCoverBitmap != null && coverDrawable instanceof FitWidthBitmapDrawable) {
            ((FitWidthBitmapDrawable) coverDrawable).setBitmap(mCoverBitmap);
        }
        // if solid color is set before enableParallax, use it as initial value.
        if (mSolidColor != Color.TRANSPARENT && bottomDrawable instanceof ColorDrawable) {
            ((ColorDrawable) bottomDrawable).setColor(mSolidColor);
        }
        if (mPlaybackGlue != null) {
            throw new IllegalStateException("enableParallaxDrawable must be called before "
                    + "enableVideoPlayback");
        }
        mParallaxDrawable = new DetailsParallaxDrawable(
                mFragment.getContext(),
                mFragment.getParallax(),
                coverDrawable,
                bottomDrawable,
                coverDrawableParallaxTarget);
        mFragment.setBackgroundDrawable(mParallaxDrawable);
        // create a VideoHelper with null PlaybackGlue for changing CoverDrawable visibility
        // before PlaybackGlue is ready.
        mVideoHelper = new DetailsBackgroundVideoHelper(null,
                mFragment.getParallax(), mParallaxDrawable.getCoverDrawable());
    }

    /**
     * Enable video playback and set proper {@link PlaybackGlueHost}. This method by default
     * creates a VideoSupportFragment and VideoSupportFragmentGlueHost to host the PlaybackGlue.
     * This method must be called after calling details Fragment super.onCreate(). This method
     * can be called multiple times to replace existing PlaybackGlue or calling
     * setupVideoPlayback(null) to clear. Note a typical {@link PlaybackGlue} subclass releases
     * resources in {@link PlaybackGlue#onDetachedFromHost()}, when the {@link PlaybackGlue}
     * subclass is not doing that, it's app's responsibility to release the resources.
     *
     * @param playbackGlue The new PlaybackGlue to set as background or null to clear existing one.
     * @see #onCreateVideoSupportFragment()
     * @see #onCreateGlueHost().
     */
    @SuppressWarnings("ReferenceEquality")
    public void setupVideoPlayback(@NonNull PlaybackGlue playbackGlue) {
        if (mPlaybackGlue == playbackGlue) {
            return;
        }

        PlaybackGlueHost playbackGlueHost = null;
        if (mPlaybackGlue != null) {
            playbackGlueHost = mPlaybackGlue.getHost();
            mPlaybackGlue.setHost(null);
        }

        mPlaybackGlue = playbackGlue;
        mVideoHelper.setPlaybackGlue(mPlaybackGlue);
        if (mCanUseHost && mPlaybackGlue != null) {
            if (playbackGlueHost == null
                    || mLastVideoSupportFragmentForGlueHost != findOrCreateVideoSupportFragment()) {
                mPlaybackGlue.setHost(createGlueHost());
                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
            } else {
                mPlaybackGlue.setHost(playbackGlueHost);
            }
        }
    }

    /**
     * Returns current PlaybackGlue or null if not set or cleared.
     *
     * @return Current PlaybackGlue or null
     */
    public final PlaybackGlue getPlaybackGlue() {
        return mPlaybackGlue;
    }

    /**
     * Precondition allows user navigate to video fragment using DPAD. Default implementation
     * returns true if PlaybackGlue is not null. Subclass may override, e.g. only allow navigation
     * when {@link PlaybackGlue#isPrepared()} is true. Note this method does not block
     * app calls {@link #switchToVideo}.
     *
     * @return True allow to navigate to video fragment.
     */
    public boolean canNavigateToVideoSupportFragment() {
        return mPlaybackGlue != null;
    }

    void switchToVideoBeforeCreate() {
        mVideoHelper.crossFadeBackgroundToVideo(true, true);
        mInitialControlVisible = true;
    }

    /**
     * Switch to video fragment, note that this method is not affected by result of
     * {@link #canNavigateToVideoSupportFragment()}. If the method is called in DetailsSupportFragment.onCreate()
     * it will make video fragment to be initially focused once it is created.
     * <p>
     * Calling switchToVideo() in DetailsSupportFragment.onCreate() will clear the activity enter
     * transition and shared element transition.
     * </p>
     * <p>
     * If switchToVideo() is called after {@link DetailsSupportFragment#prepareEntranceTransition()} and
     * before {@link DetailsSupportFragment#onEntranceTransitionEnd()}, it will be ignored.
     * </p>
     * <p>
     * If {@link DetailsSupportFragment#prepareEntranceTransition()} is called after switchToVideo(), an
     * IllegalStateException will be thrown.
     * </p>
     */
    public final void switchToVideo() {
        mFragment.switchToVideo();
    }

    /**
     * Switch to rows fragment.
     */
    public final void switchToRows() {
        mFragment.switchToRows();
    }

    /**
     * When fragment is started and no running transition. First set host if not yet set, second
     * start playing if it was paused before.
     */
    void onStart() {
        if (!mCanUseHost) {
            mCanUseHost = true;
            if (mPlaybackGlue != null) {
                mPlaybackGlue.setHost(createGlueHost());
                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
            }
        }
        if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) {
            mPlaybackGlue.play();
        }
    }

    void onStop() {
        if (mPlaybackGlue != null) {
            mPlaybackGlue.pause();
        }
    }

    /**
     * Disable parallax that would auto-start video playback
     * @return true if video fragment is visible or false otherwise.
     */
    boolean disableVideoParallax() {
        if (mVideoHelper != null) {
            mVideoHelper.stopParallax();
            return mVideoHelper.isVideoVisible();
        }
        return false;
    }

    /**
     * Returns the cover drawable at top. Returns null if {@link #enableParallax()} is not called.
     * By default it's a {@link FitWidthBitmapDrawable}.
     *
     * @return The cover drawable at top.
     */
    public final Drawable getCoverDrawable() {
        if (mParallaxDrawable == null) {
            return null;
        }
        return mParallaxDrawable.getCoverDrawable();
    }

    /**
     * Returns the drawable at bottom. Returns null if {@link #enableParallax()} is not called.
     * By default it's a {@link ColorDrawable}.
     *
     * @return The bottom drawable.
     */
    public final Drawable getBottomDrawable() {
        if (mParallaxDrawable == null) {
            return null;
        }
        return mParallaxDrawable.getBottomDrawable();
    }

    /**
     * Creates a Fragment to host {@link PlaybackGlue}. Returns a new {@link VideoSupportFragment} by
     * default. App may override and return a different fragment and it also must override
     * {@link #onCreateGlueHost()}.
     *
     * @return A new fragment used in {@link #onCreateGlueHost()}.
     * @see #onCreateGlueHost()
     * @see #setupVideoPlayback(PlaybackGlue)
     */
    public Fragment onCreateVideoSupportFragment() {
        return new VideoSupportFragment();
    }

    /**
     * Creates a PlaybackGlueHost to host PlaybackGlue. App may override this if it overrides
     * {@link #onCreateVideoSupportFragment()}. This method must be called after calling Fragment
     * super.onCreate(). When override this method, app may call
     * {@link #findOrCreateVideoSupportFragment()} to get or create a fragment.
     *
     * @return A new PlaybackGlueHost to host PlaybackGlue.
     * @see #onCreateVideoSupportFragment()
     * @see #findOrCreateVideoSupportFragment()
     * @see #setupVideoPlayback(PlaybackGlue)
     */
    public PlaybackGlueHost onCreateGlueHost() {
        return new VideoSupportFragmentGlueHost((VideoSupportFragment) findOrCreateVideoSupportFragment());
    }

    PlaybackGlueHost createGlueHost() {
        PlaybackGlueHost host = onCreateGlueHost();
        if (mInitialControlVisible) {
            host.showControlsOverlay(false);
        } else {
            host.hideControlsOverlay(false);
        }
        return host;
    }

    /**
     * Adds or gets fragment for rendering video in DetailsSupportFragment. A subclass that
     * overrides {@link #onCreateGlueHost()} should call this method to get a fragment for creating
     * a {@link PlaybackGlueHost}.
     *
     * @return Fragment the added or restored fragment responsible for rendering video.
     * @see #onCreateGlueHost()
     */
    public final Fragment findOrCreateVideoSupportFragment() {
        return mFragment.findOrCreateVideoSupportFragment();
    }

    /**
     * Convenient method to set Bitmap in cover drawable. If app is not using default
     * {@link FitWidthBitmapDrawable}, app should not use this method  It's safe to call
     * setCoverBitmap() before calling {@link #enableParallax()}.
     *
     * @param bitmap bitmap to set as cover.
     */
    public final void setCoverBitmap(Bitmap bitmap) {
        mCoverBitmap = bitmap;
        Drawable drawable = getCoverDrawable();
        if (drawable instanceof FitWidthBitmapDrawable) {
            ((FitWidthBitmapDrawable) drawable).setBitmap(mCoverBitmap);
        }
    }

    /**
     * Returns Bitmap set by {@link #setCoverBitmap(Bitmap)}.
     *
     * @return Bitmap for cover drawable.
     */
    public final Bitmap getCoverBitmap() {
        return mCoverBitmap;
    }

    /**
     * Returns color set by {@link #setSolidColor(int)}.
     *
     * @return Solid color used for bottom drawable.
     */
    public final @ColorInt int getSolidColor() {
        return mSolidColor;
    }

    /**
     * Convenient method to set color in bottom drawable. If app is not using default
     * {@link ColorDrawable}, app should not use this method. It's safe to call setSolidColor()
     * before calling {@link #enableParallax()}.
     *
     * @param color color for bottom drawable.
     */
    public final void setSolidColor(@ColorInt int color) {
        mSolidColor = color;
        Drawable bottomDrawable = getBottomDrawable();
        if (bottomDrawable instanceof ColorDrawable) {
            ((ColorDrawable) bottomDrawable).setColor(color);
        }
    }

    /**
     * Sets default parallax offset in pixels for bitmap moving vertically. This method must
     * be called before {@link #enableParallax()}.
     *
     * @param offset Offset in pixels (e.g. 120).
     * @see #enableParallax()
     */
    public final void setParallaxDrawableMaxOffset(int offset) {
        if (mParallaxDrawable != null) {
            throw new IllegalStateException("enableParallax already called");
        }
        mParallaxDrawableMaxOffset = offset;
    }

    /**
     * Returns Default parallax offset in pixels for bitmap moving vertically.
     * When 0, a default value would be used.
     *
     * @return Default parallax offset in pixels for bitmap moving vertically.
     * @see #enableParallax()
     */
    public final int getParallaxDrawableMaxOffset() {
        return mParallaxDrawableMaxOffset;
    }

}