1 // CHECKSTYLE:OFF Generated code 2 /* This file is auto-generated from VideoDetailsFragmentBackgroundController.java. DO NOT MODIFY. */ 3 4 /* 5 * Copyright (C) 2017 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 package android.support.v17.leanback.app; 20 21 import android.animation.PropertyValuesHolder; 22 import android.support.v4.app.Fragment; 23 import android.graphics.Bitmap; 24 import android.graphics.Color; 25 import android.graphics.drawable.ColorDrawable; 26 import android.graphics.drawable.Drawable; 27 import android.support.annotation.ColorInt; 28 import android.support.annotation.NonNull; 29 import android.support.annotation.Nullable; 30 import android.support.v17.leanback.R; 31 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable; 32 import android.support.v17.leanback.media.PlaybackGlue; 33 import android.support.v17.leanback.media.PlaybackGlueHost; 34 import android.support.v17.leanback.widget.DetailsParallaxDrawable; 35 import android.support.v17.leanback.widget.ParallaxTarget; 36 37 /** 38 * Controller for DetailsSupportFragment parallax background and embedded video play. 39 * <p> 40 * The parallax background drawable is made of two parts: cover drawable (by default 41 * {@link FitWidthBitmapDrawable}) above the details overview row and bottom drawable (by default 42 * {@link ColorDrawable}) below the details overview row. While vertically scrolling rows, the size 43 * of cover drawable and bottom drawable will be updated and the cover drawable will by default 44 * perform a parallax shift using {@link FitWidthBitmapDrawable#PROPERTY_VERTICAL_OFFSET}. 45 * </p> 46 * <pre> 47 * *************************** 48 * * Cover Drawable * 49 * * (FitWidthBitmapDrawable)* 50 * * * 51 * *************************** 52 * * DetailsOverviewRow * 53 * * * 54 * *************************** 55 * * Bottom Drawable * 56 * * (ColorDrawable) * 57 * * Related * 58 * * Content * 59 * *************************** 60 * </pre> 61 * Both parallax background drawable and embedded video play are optional. App must call 62 * {@link #enableParallax()} and/or {@link #setupVideoPlayback(PlaybackGlue)} explicitly. 63 * The PlaybackGlue is automatically {@link PlaybackGlue#play()} when fragment starts and 64 * {@link PlaybackGlue#pause()} when fragment stops. When video is ready to play, cover drawable 65 * will be faded out. 66 * Example: 67 * <pre> 68 * DetailsSupportFragmentBackgroundController mController = new DetailsSupportFragmentBackgroundController(this); 69 * 70 * public void onCreate(Bundle savedInstance) { 71 * super.onCreate(savedInstance); 72 * MediaPlayerGlue player = new MediaPlayerGlue(..); 73 * player.setUrl(...); 74 * mController.enableParallax(); 75 * mController.setupVideoPlayback(player); 76 * } 77 * 78 * static class MyLoadBitmapTask extends ... { 79 * WeakReference<MyFragment> mFragmentRef; 80 * MyLoadBitmapTask(MyFragment fragment) { 81 * mFragmentRef = new WeakReference(fragment); 82 * } 83 * protected void onPostExecute(Bitmap bitmap) { 84 * MyFragment fragment = mFragmentRef.get(); 85 * if (fragment != null) { 86 * fragment.mController.setCoverBitmap(bitmap); 87 * } 88 * } 89 * } 90 * 91 * public void onStart() { 92 * new MyLoadBitmapTask(this).execute(url); 93 * } 94 * 95 * public void onStop() { 96 * mController.setCoverBitmap(null); 97 * } 98 * </pre> 99 * <p> 100 * To customize cover drawable and/or bottom drawable, app should call 101 * {@link #enableParallax(Drawable, Drawable, ParallaxTarget.PropertyValuesHolderTarget)}. 102 * If app supplies a custom cover Drawable, it should not call {@link #setCoverBitmap(Bitmap)}. 103 * If app supplies a custom bottom Drawable, it should not call {@link #setSolidColor(int)}. 104 * </p> 105 * <p> 106 * To customize playback fragment, app should override {@link #onCreateVideoSupportFragment()} and 107 * {@link #onCreateGlueHost()}. 108 * </p> 109 * 110 */ 111 public class DetailsSupportFragmentBackgroundController { 112 113 final DetailsSupportFragment mFragment; 114 DetailsParallaxDrawable mParallaxDrawable; 115 int mParallaxDrawableMaxOffset; 116 PlaybackGlue mPlaybackGlue; 117 DetailsBackgroundVideoHelper mVideoHelper; 118 Bitmap mCoverBitmap; 119 int mSolidColor; 120 boolean mCanUseHost = false; 121 boolean mInitialControlVisible = false; 122 123 private Fragment mLastVideoSupportFragmentForGlueHost; 124 125 /** 126 * Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment. Note that 127 * each DetailsSupportFragment can only associate with one DetailsSupportFragmentBackgroundController. 128 * 129 * @param fragment The DetailsSupportFragment to control background and embedded video playing. 130 * @throws IllegalStateException If fragment was already associated with another controller. 131 */ DetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment)132 public DetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment) { 133 if (fragment.mDetailsBackgroundController != null) { 134 throw new IllegalStateException("Each DetailsSupportFragment is allowed to initialize " 135 + "DetailsSupportFragmentBackgroundController once"); 136 } 137 fragment.mDetailsBackgroundController = this; 138 mFragment = fragment; 139 } 140 141 /** 142 * Enables default parallax background using a {@link FitWidthBitmapDrawable} as cover drawable 143 * and {@link ColorDrawable} as bottom drawable. A vertical parallax movement will be applied 144 * to the FitWidthBitmapDrawable. App may use {@link #setSolidColor(int)} and 145 * {@link #setCoverBitmap(Bitmap)} to change the content of bottom drawable and cover drawable. 146 * This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}. 147 * 148 * @see #setCoverBitmap(Bitmap) 149 * @see #setSolidColor(int) 150 * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called. 151 */ enableParallax()152 public void enableParallax() { 153 int offset = mParallaxDrawableMaxOffset; 154 if (offset == 0) { 155 offset = mFragment.getContext().getResources() 156 .getDimensionPixelSize(R.dimen.lb_details_cover_drawable_parallax_movement); 157 } 158 Drawable coverDrawable = new FitWidthBitmapDrawable(); 159 ColorDrawable colorDrawable = new ColorDrawable(); 160 enableParallax(coverDrawable, colorDrawable, 161 new ParallaxTarget.PropertyValuesHolderTarget( 162 coverDrawable, 163 PropertyValuesHolder.ofInt(FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET, 164 0, -offset) 165 )); 166 } 167 168 /** 169 * Enables parallax background using a custom cover drawable at top and a custom bottom 170 * drawable. This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}. 171 * 172 * @param coverDrawable Custom cover drawable shown at top. {@link #setCoverBitmap(Bitmap)} 173 * will not work if coverDrawable is not {@link FitWidthBitmapDrawable}; 174 * in that case it's app's responsibility to set content into 175 * coverDrawable. 176 * @param bottomDrawable Drawable shown at bottom. {@link #setSolidColor(int)} will not work 177 * if bottomDrawable is not {@link ColorDrawable}; in that case it's app's 178 * responsibility to set content of bottomDrawable. 179 * @param coverDrawableParallaxTarget Target to perform parallax effect within coverDrawable. 180 * Use null for no parallax movement effect. 181 * Example to move bitmap within FitWidthBitmapDrawable: 182 * new ParallaxTarget.PropertyValuesHolderTarget( 183 * coverDrawable, PropertyValuesHolder.ofInt( 184 * FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET, 185 * 0, -120)) 186 * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called. 187 */ enableParallax(@onNull Drawable coverDrawable, @NonNull Drawable bottomDrawable, @Nullable ParallaxTarget.PropertyValuesHolderTarget coverDrawableParallaxTarget)188 public void enableParallax(@NonNull Drawable coverDrawable, @NonNull Drawable bottomDrawable, 189 @Nullable ParallaxTarget.PropertyValuesHolderTarget 190 coverDrawableParallaxTarget) { 191 if (mParallaxDrawable != null) { 192 return; 193 } 194 // if bitmap is set before enableParallax, use it as initial value. 195 if (mCoverBitmap != null && coverDrawable instanceof FitWidthBitmapDrawable) { 196 ((FitWidthBitmapDrawable) coverDrawable).setBitmap(mCoverBitmap); 197 } 198 // if solid color is set before enableParallax, use it as initial value. 199 if (mSolidColor != Color.TRANSPARENT && bottomDrawable instanceof ColorDrawable) { 200 ((ColorDrawable) bottomDrawable).setColor(mSolidColor); 201 } 202 if (mPlaybackGlue != null) { 203 throw new IllegalStateException("enableParallaxDrawable must be called before " 204 + "enableVideoPlayback"); 205 } 206 mParallaxDrawable = new DetailsParallaxDrawable( 207 mFragment.getContext(), 208 mFragment.getParallax(), 209 coverDrawable, 210 bottomDrawable, 211 coverDrawableParallaxTarget); 212 mFragment.setBackgroundDrawable(mParallaxDrawable); 213 // create a VideoHelper with null PlaybackGlue for changing CoverDrawable visibility 214 // before PlaybackGlue is ready. 215 mVideoHelper = new DetailsBackgroundVideoHelper(null, 216 mFragment.getParallax(), mParallaxDrawable.getCoverDrawable()); 217 } 218 219 /** 220 * Enable video playback and set proper {@link PlaybackGlueHost}. This method by default 221 * creates a VideoSupportFragment and VideoSupportFragmentGlueHost to host the PlaybackGlue. 222 * This method must be called after calling details Fragment super.onCreate(). This method 223 * can be called multiple times to replace existing PlaybackGlue or calling 224 * setupVideoPlayback(null) to clear. Note a typical {@link PlaybackGlue} subclass releases 225 * resources in {@link PlaybackGlue#onDetachedFromHost()}, when the {@link PlaybackGlue} 226 * subclass is not doing that, it's app's responsibility to release the resources. 227 * 228 * @param playbackGlue The new PlaybackGlue to set as background or null to clear existing one. 229 * @see #onCreateVideoSupportFragment() 230 * @see #onCreateGlueHost(). 231 */ 232 @SuppressWarnings("ReferenceEquality") setupVideoPlayback(@onNull PlaybackGlue playbackGlue)233 public void setupVideoPlayback(@NonNull PlaybackGlue playbackGlue) { 234 if (mPlaybackGlue == playbackGlue) { 235 return; 236 } 237 238 PlaybackGlueHost playbackGlueHost = null; 239 if (mPlaybackGlue != null) { 240 playbackGlueHost = mPlaybackGlue.getHost(); 241 mPlaybackGlue.setHost(null); 242 } 243 244 mPlaybackGlue = playbackGlue; 245 mVideoHelper.setPlaybackGlue(mPlaybackGlue); 246 if (mCanUseHost && mPlaybackGlue != null) { 247 if (playbackGlueHost == null 248 || mLastVideoSupportFragmentForGlueHost != findOrCreateVideoSupportFragment()) { 249 mPlaybackGlue.setHost(createGlueHost()); 250 mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment(); 251 } else { 252 mPlaybackGlue.setHost(playbackGlueHost); 253 } 254 } 255 } 256 257 /** 258 * Returns current PlaybackGlue or null if not set or cleared. 259 * 260 * @return Current PlaybackGlue or null 261 */ getPlaybackGlue()262 public final PlaybackGlue getPlaybackGlue() { 263 return mPlaybackGlue; 264 } 265 266 /** 267 * Precondition allows user navigate to video fragment using DPAD. Default implementation 268 * returns true if PlaybackGlue is not null. Subclass may override, e.g. only allow navigation 269 * when {@link PlaybackGlue#isPrepared()} is true. Note this method does not block 270 * app calls {@link #switchToVideo}. 271 * 272 * @return True allow to navigate to video fragment. 273 */ canNavigateToVideoSupportFragment()274 public boolean canNavigateToVideoSupportFragment() { 275 return mPlaybackGlue != null; 276 } 277 switchToVideoBeforeCreate()278 void switchToVideoBeforeCreate() { 279 mVideoHelper.crossFadeBackgroundToVideo(true, true); 280 mInitialControlVisible = true; 281 } 282 283 /** 284 * Switch to video fragment, note that this method is not affected by result of 285 * {@link #canNavigateToVideoSupportFragment()}. If the method is called in DetailsSupportFragment.onCreate() 286 * it will make video fragment to be initially focused once it is created. 287 * <p> 288 * Calling switchToVideo() in DetailsSupportFragment.onCreate() will clear the activity enter 289 * transition and shared element transition. 290 * </p> 291 * <p> 292 * If switchToVideo() is called after {@link DetailsSupportFragment#prepareEntranceTransition()} and 293 * before {@link DetailsSupportFragment#onEntranceTransitionEnd()}, it will be ignored. 294 * </p> 295 * <p> 296 * If {@link DetailsSupportFragment#prepareEntranceTransition()} is called after switchToVideo(), an 297 * IllegalStateException will be thrown. 298 * </p> 299 */ switchToVideo()300 public final void switchToVideo() { 301 mFragment.switchToVideo(); 302 } 303 304 /** 305 * Switch to rows fragment. 306 */ switchToRows()307 public final void switchToRows() { 308 mFragment.switchToRows(); 309 } 310 311 /** 312 * When fragment is started and no running transition. First set host if not yet set, second 313 * start playing if it was paused before. 314 */ onStart()315 void onStart() { 316 if (!mCanUseHost) { 317 mCanUseHost = true; 318 if (mPlaybackGlue != null) { 319 mPlaybackGlue.setHost(createGlueHost()); 320 mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment(); 321 } 322 } 323 if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) { 324 mPlaybackGlue.play(); 325 } 326 } 327 onStop()328 void onStop() { 329 if (mPlaybackGlue != null) { 330 mPlaybackGlue.pause(); 331 } 332 } 333 334 /** 335 * Disable parallax that would auto-start video playback 336 * @return true if video fragment is visible or false otherwise. 337 */ disableVideoParallax()338 boolean disableVideoParallax() { 339 if (mVideoHelper != null) { 340 mVideoHelper.stopParallax(); 341 return mVideoHelper.isVideoVisible(); 342 } 343 return false; 344 } 345 346 /** 347 * Returns the cover drawable at top. Returns null if {@link #enableParallax()} is not called. 348 * By default it's a {@link FitWidthBitmapDrawable}. 349 * 350 * @return The cover drawable at top. 351 */ getCoverDrawable()352 public final Drawable getCoverDrawable() { 353 if (mParallaxDrawable == null) { 354 return null; 355 } 356 return mParallaxDrawable.getCoverDrawable(); 357 } 358 359 /** 360 * Returns the drawable at bottom. Returns null if {@link #enableParallax()} is not called. 361 * By default it's a {@link ColorDrawable}. 362 * 363 * @return The bottom drawable. 364 */ getBottomDrawable()365 public final Drawable getBottomDrawable() { 366 if (mParallaxDrawable == null) { 367 return null; 368 } 369 return mParallaxDrawable.getBottomDrawable(); 370 } 371 372 /** 373 * Creates a Fragment to host {@link PlaybackGlue}. Returns a new {@link VideoSupportFragment} by 374 * default. App may override and return a different fragment and it also must override 375 * {@link #onCreateGlueHost()}. 376 * 377 * @return A new fragment used in {@link #onCreateGlueHost()}. 378 * @see #onCreateGlueHost() 379 * @see #setupVideoPlayback(PlaybackGlue) 380 */ onCreateVideoSupportFragment()381 public Fragment onCreateVideoSupportFragment() { 382 return new VideoSupportFragment(); 383 } 384 385 /** 386 * Creates a PlaybackGlueHost to host PlaybackGlue. App may override this if it overrides 387 * {@link #onCreateVideoSupportFragment()}. This method must be called after calling Fragment 388 * super.onCreate(). When override this method, app may call 389 * {@link #findOrCreateVideoSupportFragment()} to get or create a fragment. 390 * 391 * @return A new PlaybackGlueHost to host PlaybackGlue. 392 * @see #onCreateVideoSupportFragment() 393 * @see #findOrCreateVideoSupportFragment() 394 * @see #setupVideoPlayback(PlaybackGlue) 395 */ onCreateGlueHost()396 public PlaybackGlueHost onCreateGlueHost() { 397 return new VideoSupportFragmentGlueHost((VideoSupportFragment) findOrCreateVideoSupportFragment()); 398 } 399 createGlueHost()400 PlaybackGlueHost createGlueHost() { 401 PlaybackGlueHost host = onCreateGlueHost(); 402 if (mInitialControlVisible) { 403 host.showControlsOverlay(false); 404 } else { 405 host.hideControlsOverlay(false); 406 } 407 return host; 408 } 409 410 /** 411 * Adds or gets fragment for rendering video in DetailsSupportFragment. A subclass that 412 * overrides {@link #onCreateGlueHost()} should call this method to get a fragment for creating 413 * a {@link PlaybackGlueHost}. 414 * 415 * @return Fragment the added or restored fragment responsible for rendering video. 416 * @see #onCreateGlueHost() 417 */ findOrCreateVideoSupportFragment()418 public final Fragment findOrCreateVideoSupportFragment() { 419 return mFragment.findOrCreateVideoSupportFragment(); 420 } 421 422 /** 423 * Convenient method to set Bitmap in cover drawable. If app is not using default 424 * {@link FitWidthBitmapDrawable}, app should not use this method It's safe to call 425 * setCoverBitmap() before calling {@link #enableParallax()}. 426 * 427 * @param bitmap bitmap to set as cover. 428 */ setCoverBitmap(Bitmap bitmap)429 public final void setCoverBitmap(Bitmap bitmap) { 430 mCoverBitmap = bitmap; 431 Drawable drawable = getCoverDrawable(); 432 if (drawable instanceof FitWidthBitmapDrawable) { 433 ((FitWidthBitmapDrawable) drawable).setBitmap(mCoverBitmap); 434 } 435 } 436 437 /** 438 * Returns Bitmap set by {@link #setCoverBitmap(Bitmap)}. 439 * 440 * @return Bitmap for cover drawable. 441 */ getCoverBitmap()442 public final Bitmap getCoverBitmap() { 443 return mCoverBitmap; 444 } 445 446 /** 447 * Returns color set by {@link #setSolidColor(int)}. 448 * 449 * @return Solid color used for bottom drawable. 450 */ getSolidColor()451 public final @ColorInt int getSolidColor() { 452 return mSolidColor; 453 } 454 455 /** 456 * Convenient method to set color in bottom drawable. If app is not using default 457 * {@link ColorDrawable}, app should not use this method. It's safe to call setSolidColor() 458 * before calling {@link #enableParallax()}. 459 * 460 * @param color color for bottom drawable. 461 */ setSolidColor(@olorInt int color)462 public final void setSolidColor(@ColorInt int color) { 463 mSolidColor = color; 464 Drawable bottomDrawable = getBottomDrawable(); 465 if (bottomDrawable instanceof ColorDrawable) { 466 ((ColorDrawable) bottomDrawable).setColor(color); 467 } 468 } 469 470 /** 471 * Sets default parallax offset in pixels for bitmap moving vertically. This method must 472 * be called before {@link #enableParallax()}. 473 * 474 * @param offset Offset in pixels (e.g. 120). 475 * @see #enableParallax() 476 */ setParallaxDrawableMaxOffset(int offset)477 public final void setParallaxDrawableMaxOffset(int offset) { 478 if (mParallaxDrawable != null) { 479 throw new IllegalStateException("enableParallax already called"); 480 } 481 mParallaxDrawableMaxOffset = offset; 482 } 483 484 /** 485 * Returns Default parallax offset in pixels for bitmap moving vertically. 486 * When 0, a default value would be used. 487 * 488 * @return Default parallax offset in pixels for bitmap moving vertically. 489 * @see #enableParallax() 490 */ getParallaxDrawableMaxOffset()491 public final int getParallaxDrawableMaxOffset() { 492 return mParallaxDrawableMaxOffset; 493 } 494 495 } 496