blob: 4a0be6ec228862419b28f05838f66f5e6be73566 [file] [log] [blame]
// CHECKSTYLE:OFF Generated code
/* This file is auto-generated from VideoDetailsFragmentBackgroundController.java. DO NOT MODIFY. */
/*
* 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 android.support.v17.leanback.app;
import android.animation.PropertyValuesHolder;
import android.support.v4.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v17.leanback.R;
import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
import android.support.v17.leanback.media.PlaybackGlue;
import android.support.v17.leanback.media.PlaybackGlueHost;
import android.support.v17.leanback.widget.DetailsParallaxDrawable;
import android.support.v17.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;
}
}