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