1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.window; 17 18 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 19 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 20 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 21 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 22 23 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_AVD; 24 25 import android.animation.Animator; 26 import android.animation.AnimatorListenerAdapter; 27 import android.annotation.ColorInt; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.TestApi; 31 import android.annotation.UiThread; 32 import android.app.Activity; 33 import android.content.Context; 34 import android.graphics.Bitmap; 35 import android.graphics.Canvas; 36 import android.graphics.Color; 37 import android.graphics.PixelFormat; 38 import android.graphics.Rect; 39 import android.graphics.drawable.BitmapDrawable; 40 import android.graphics.drawable.Drawable; 41 import android.os.Build; 42 import android.os.Parcel; 43 import android.os.Parcelable; 44 import android.os.RemoteCallback; 45 import android.os.Trace; 46 import android.util.AttributeSet; 47 import android.util.Log; 48 import android.view.Gravity; 49 import android.view.LayoutInflater; 50 import android.view.SurfaceControlViewHost; 51 import android.view.SurfaceView; 52 import android.view.View; 53 import android.view.ViewGroup; 54 import android.view.Window; 55 import android.view.WindowInsetsController; 56 import android.view.WindowManager; 57 import android.widget.FrameLayout; 58 import android.widget.ImageView; 59 60 import com.android.internal.R; 61 import com.android.internal.jank.InteractionJankMonitor; 62 import com.android.internal.policy.DecorView; 63 import com.android.internal.util.ContrastColorUtil; 64 65 import java.time.Duration; 66 import java.time.Instant; 67 import java.util.function.Consumer; 68 69 /** 70 * <p>The view which allows an activity to customize its splash screen exit animation.</p> 71 * 72 * <p>Activities will receive this view as a parameter of 73 * {@link SplashScreen.OnExitAnimationListener#onSplashScreenExit} if 74 * they set {@link SplashScreen#setOnExitAnimationListener}. 75 * When this callback is called, this view will be on top of the activity.</p> 76 * 77 * <p>This view is composed of a view containing the splashscreen icon (see 78 * windowSplashscreenAnimatedIcon) and a background. 79 * Developers can use {@link #getIconView} to get this view and replace the drawable or 80 * add animation to it. The background of this view is filled with a single color, which can be 81 * edited during the animation by {@link View#setBackground} or {@link View#setBackgroundColor}.</p> 82 * 83 * @see SplashScreen 84 */ 85 public final class SplashScreenView extends FrameLayout { 86 private static final String TAG = SplashScreenView.class.getSimpleName(); 87 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 88 89 private static final int LIGHT_BARS_MASK = 90 WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS 91 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 92 private static final int WINDOW_FLAG_MASK = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 93 | FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS; 94 95 private boolean mNotCopyable; 96 private boolean mIsCopied; 97 private int mInitBackgroundColor; 98 private View mIconView; 99 private Bitmap mParceledIconBitmap; 100 private View mBrandingImageView; 101 private Bitmap mParceledBrandingBitmap; 102 private Bitmap mParceledIconBackgroundBitmap; 103 private Duration mIconAnimationDuration; 104 private Instant mIconAnimationStart; 105 106 // The host activity when transfer view to it. 107 private Activity mHostActivity; 108 109 @Nullable 110 private SurfaceControlViewHost.SurfacePackage mSurfacePackageCopy; 111 @Nullable 112 private SurfaceControlViewHost.SurfacePackage mSurfacePackage; 113 @Nullable 114 private SurfaceView mSurfaceView; 115 @Nullable 116 private SurfaceControlViewHost mSurfaceHost; 117 @Nullable 118 private RemoteCallback mClientCallback; 119 120 // cache original window and status 121 private Window mWindow; 122 private int mAppWindowFlags; 123 private int mStatusBarColor; 124 private int mNavigationBarColor; 125 private int mSystemBarsAppearance; 126 private boolean mHasRemoved; 127 private boolean mNavigationContrastEnforced; 128 private boolean mStatusContrastEnforced; 129 private boolean mDecorFitsSystemWindows; 130 131 /** 132 * Internal builder to create a SplashScreenView object. 133 * @hide 134 */ 135 public static class Builder { 136 private final Context mContext; 137 private int mIconSize; 138 private @ColorInt int mBackgroundColor; 139 private Bitmap mParceledIconBitmap; 140 private Bitmap mParceledIconBackgroundBitmap; 141 private Drawable mIconDrawable; 142 // It is only set for legacy splash screen which won't be sent across processes. 143 private Drawable mOverlayDrawable; 144 private Drawable mIconBackground; 145 private SurfaceControlViewHost.SurfacePackage mSurfacePackage; 146 private RemoteCallback mClientCallback; 147 private int mBrandingImageWidth; 148 private int mBrandingImageHeight; 149 private Drawable mBrandingDrawable; 150 private Bitmap mParceledBrandingBitmap; 151 private Instant mIconAnimationStart; 152 private Duration mIconAnimationDuration; 153 private Consumer<Runnable> mUiThreadInitTask; 154 Builder(@onNull Context context)155 public Builder(@NonNull Context context) { 156 mContext = context; 157 } 158 159 /** 160 * When create from {@link SplashScreenViewParcelable}, all the materials were be settled so 161 * you do not need to call other set methods. 162 */ createFromParcel(SplashScreenViewParcelable parcelable)163 public Builder createFromParcel(SplashScreenViewParcelable parcelable) { 164 mIconSize = parcelable.getIconSize(); 165 mBackgroundColor = parcelable.getBackgroundColor(); 166 mSurfacePackage = parcelable.mSurfacePackage; 167 if (mSurfacePackage == null && parcelable.mIconBitmap != null) { 168 // We only create a Bitmap copies of immobile icons since animated icon are using 169 // a surface view 170 mIconDrawable = new BitmapDrawable(mContext.getResources(), parcelable.mIconBitmap); 171 mParceledIconBitmap = parcelable.mIconBitmap; 172 } 173 if (parcelable.mIconBackground != null) { 174 mIconBackground = new BitmapDrawable(mContext.getResources(), 175 parcelable.mIconBackground); 176 mParceledIconBackgroundBitmap = parcelable.mIconBackground; 177 } 178 if (parcelable.mBrandingBitmap != null) { 179 setBrandingDrawable(new BitmapDrawable(mContext.getResources(), 180 parcelable.mBrandingBitmap), parcelable.mBrandingWidth, 181 parcelable.mBrandingHeight); 182 mParceledBrandingBitmap = parcelable.mBrandingBitmap; 183 } 184 mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis); 185 mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis); 186 mClientCallback = parcelable.mClientCallback; 187 if (DEBUG) { 188 Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable)); 189 } 190 return this; 191 } 192 193 /** 194 * Set the rectangle size for the center view. 195 */ setIconSize(int iconSize)196 public Builder setIconSize(int iconSize) { 197 mIconSize = iconSize; 198 return this; 199 } 200 201 /** 202 * Set the background color for the view. 203 */ setBackgroundColor(@olorInt int backgroundColor)204 public Builder setBackgroundColor(@ColorInt int backgroundColor) { 205 mBackgroundColor = backgroundColor; 206 return this; 207 } 208 209 /** 210 * Set the Drawable object to fill entire view 211 */ setOverlayDrawable(@ullable Drawable drawable)212 public Builder setOverlayDrawable(@Nullable Drawable drawable) { 213 mOverlayDrawable = drawable; 214 return this; 215 } 216 217 /** 218 * Set the Drawable object to fill the center view. 219 */ setCenterViewDrawable(@ullable Drawable drawable)220 public Builder setCenterViewDrawable(@Nullable Drawable drawable) { 221 mIconDrawable = drawable; 222 return this; 223 } 224 225 /** 226 * Set the background color for the icon. 227 */ setIconBackground(Drawable iconBackground)228 public Builder setIconBackground(Drawable iconBackground) { 229 mIconBackground = iconBackground; 230 return this; 231 } 232 233 /** 234 * Set the animation duration if icon is animatable. 235 */ setAnimationDurationMillis(int duration)236 public Builder setAnimationDurationMillis(int duration) { 237 mIconAnimationDuration = Duration.ofMillis(duration); 238 return this; 239 } 240 241 /** 242 * Set the Runnable that can receive the task which should be executed on UI thread. 243 * @param uiThreadInitTask 244 */ setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask)245 public Builder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) { 246 mUiThreadInitTask = uiThreadInitTask; 247 return this; 248 } 249 250 /** 251 * Set the Drawable object and size for the branding view. 252 */ setBrandingDrawable(@ullable Drawable branding, int width, int height)253 public Builder setBrandingDrawable(@Nullable Drawable branding, int width, int height) { 254 mBrandingDrawable = branding; 255 mBrandingImageWidth = width; 256 mBrandingImageHeight = height; 257 return this; 258 } 259 260 /** 261 * Create SplashScreenWindowView object from materials. 262 */ build()263 public SplashScreenView build() { 264 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SplashScreenView#build"); 265 final LayoutInflater layoutInflater = LayoutInflater.from(mContext); 266 final SplashScreenView view = (SplashScreenView) 267 layoutInflater.inflate(R.layout.splash_screen_view, null, false); 268 view.mInitBackgroundColor = mBackgroundColor; 269 if (mOverlayDrawable != null) { 270 view.setBackground(mOverlayDrawable); 271 } else { 272 view.setBackgroundColor(mBackgroundColor); 273 } 274 view.mClientCallback = mClientCallback; 275 276 view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view); 277 278 // center icon 279 if (mIconDrawable instanceof SplashScreenView.IconAnimateListener 280 || mSurfacePackage != null) { 281 if (mUiThreadInitTask != null) { 282 mUiThreadInitTask.accept(() -> view.mIconView = createSurfaceView(view)); 283 } else { 284 view.mIconView = createSurfaceView(view); 285 } 286 view.initIconAnimation(mIconDrawable, 287 mIconAnimationDuration != null ? mIconAnimationDuration.toMillis() : 0); 288 view.mIconAnimationStart = mIconAnimationStart; 289 view.mIconAnimationDuration = mIconAnimationDuration; 290 } else if (mIconSize != 0) { 291 ImageView imageView = view.findViewById(R.id.splashscreen_icon_view); 292 assert imageView != null; 293 294 final ViewGroup.LayoutParams params = imageView.getLayoutParams(); 295 params.width = mIconSize; 296 params.height = mIconSize; 297 imageView.setLayoutParams(params); 298 if (mIconDrawable != null) { 299 imageView.setImageDrawable(mIconDrawable); 300 } 301 if (mIconBackground != null) { 302 imageView.setBackground(mIconBackground); 303 } 304 view.mIconView = imageView; 305 } 306 if (mOverlayDrawable != null || mIconDrawable == null) { 307 view.setNotCopyable(); 308 } 309 310 view.mParceledIconBackgroundBitmap = mParceledIconBackgroundBitmap; 311 view.mParceledIconBitmap = mParceledIconBitmap; 312 313 // branding image 314 if (mBrandingImageHeight > 0 && mBrandingImageWidth > 0 && mBrandingDrawable != null) { 315 final ViewGroup.LayoutParams params = view.mBrandingImageView.getLayoutParams(); 316 params.width = mBrandingImageWidth; 317 params.height = mBrandingImageHeight; 318 view.mBrandingImageView.setLayoutParams(params); 319 view.mBrandingImageView.setBackground(mBrandingDrawable); 320 } else { 321 view.mBrandingImageView.setVisibility(GONE); 322 } 323 if (mParceledBrandingBitmap != null) { 324 view.mParceledBrandingBitmap = mParceledBrandingBitmap; 325 } 326 if (DEBUG) { 327 Log.d(TAG, "Build " + view 328 + "\nIcon: view: " + view.mIconView + " drawable: " 329 + mIconDrawable + " size: " + mIconSize 330 + "\nBranding: view: " + view.mBrandingImageView + " drawable: " 331 + mBrandingDrawable + " size w: " + mBrandingImageWidth + " h: " 332 + mBrandingImageHeight); 333 } 334 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 335 return view; 336 } 337 createSurfaceView(@onNull SplashScreenView view)338 private SurfaceView createSurfaceView(@NonNull SplashScreenView view) { 339 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SplashScreenView#createSurfaceView"); 340 final Context viewContext = view.getContext(); 341 final SurfaceView surfaceView = new SurfaceView(viewContext); 342 surfaceView.setPadding(0, 0, 0, 0); 343 surfaceView.setBackground(mIconBackground); 344 if (mSurfacePackage == null) { 345 if (DEBUG) { 346 Log.d(TAG, 347 "SurfaceControlViewHost created on thread " 348 + Thread.currentThread().getId()); 349 } 350 351 SurfaceControlViewHost viewHost = new SurfaceControlViewHost(viewContext, 352 viewContext.getDisplay(), 353 surfaceView.getHostToken()); 354 ImageView imageView = new ImageView(viewContext); 355 imageView.setBackground(mIconDrawable); 356 viewHost.setView(imageView, mIconSize, mIconSize); 357 SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); 358 surfaceView.setChildSurfacePackage(surfacePackage); 359 view.mSurfacePackage = surfacePackage; 360 view.mSurfaceHost = viewHost; 361 view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage( 362 surfacePackage); 363 } else { 364 if (DEBUG) { 365 Log.d(TAG, "Using copy of SurfacePackage in the client"); 366 } 367 view.mSurfacePackage = mSurfacePackage; 368 } 369 if (mIconSize != 0) { 370 LayoutParams lp = new FrameLayout.LayoutParams(mIconSize, mIconSize); 371 lp.gravity = Gravity.CENTER; 372 surfaceView.setLayoutParams(lp); 373 if (DEBUG) { 374 Log.d(TAG, "Icon size " + mIconSize); 375 } 376 } 377 378 // We ensure that we can blend the alpha of the surface view with the SplashScreenView 379 surfaceView.setUseAlpha(); 380 surfaceView.setZOrderOnTop(true); 381 surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); 382 383 view.addView(surfaceView); 384 view.mSurfaceView = surfaceView; 385 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 386 return surfaceView; 387 } 388 } 389 390 /** @hide */ SplashScreenView(Context context)391 public SplashScreenView(Context context) { 392 super(context); 393 } 394 395 /** @hide */ SplashScreenView(Context context, AttributeSet attributeSet)396 public SplashScreenView(Context context, AttributeSet attributeSet) { 397 super(context, attributeSet); 398 } 399 400 /** 401 * Declared this view is not copyable. 402 * @hide 403 */ setNotCopyable()404 public void setNotCopyable() { 405 mNotCopyable = true; 406 } 407 408 /** 409 * Whether this view is copyable. 410 * @hide 411 */ isCopyable()412 public boolean isCopyable() { 413 return !mNotCopyable; 414 } 415 416 /** 417 * Called when this {@link SplashScreenView} has been copied to be transferred to the client. 418 * 419 * @hide 420 */ onCopied()421 public void onCopied() { 422 mIsCopied = true; 423 if (mSurfaceView == null) { 424 return; 425 } 426 if (DEBUG) { 427 Log.d(TAG, "Setting SurfaceView's SurfacePackage to null."); 428 } 429 // If we don't release the surface package, the surface will be reparented to this 430 // surface view. So once it's copied into the client process, we release it. 431 mSurfacePackage.release(); 432 mSurfacePackage = null; 433 } 434 435 /** @hide **/ 436 @Nullable getSurfaceHost()437 public SurfaceControlViewHost getSurfaceHost() { 438 return mSurfaceHost; 439 } 440 441 @Override setAlpha(float alpha)442 public void setAlpha(float alpha) { 443 super.setAlpha(alpha); 444 445 // The surface view's alpha is not multiplied with the containing view's alpha, so we 446 // manually do it here 447 if (mSurfaceView != null) { 448 mSurfaceView.setAlpha(mSurfaceView.getAlpha() * alpha); 449 } 450 } 451 452 /** 453 * Returns the duration of the icon animation if icon is animatable. 454 * 455 * @see android.R.attr#windowSplashScreenAnimatedIcon 456 * @see android.R.attr#windowSplashScreenAnimationDuration 457 */ 458 @Nullable getIconAnimationDuration()459 public Duration getIconAnimationDuration() { 460 return mIconAnimationDuration; 461 } 462 463 /** 464 * If the replaced icon is animatable, return the animation start time based on system clock. 465 */ 466 @Nullable getIconAnimationStart()467 public Instant getIconAnimationStart() { 468 return mIconAnimationStart; 469 } 470 471 472 /** 473 * @hide 474 */ syncTransferSurfaceOnDraw()475 public void syncTransferSurfaceOnDraw() { 476 if (mSurfacePackage == null) { 477 return; 478 } 479 if (DEBUG) { 480 mSurfacePackage.getSurfaceControl().addOnReparentListener( 481 (transaction, parent) -> Log.e(TAG, 482 String.format("SurfacePackage'surface reparented to %s", parent))); 483 Log.d(TAG, "Transferring surface " + mSurfaceView.toString()); 484 } 485 486 mSurfaceView.setChildSurfacePackageOnDraw(mSurfacePackage); 487 } 488 initIconAnimation(Drawable iconDrawable, long duration)489 void initIconAnimation(Drawable iconDrawable, long duration) { 490 if (!(iconDrawable instanceof IconAnimateListener)) { 491 return; 492 } 493 IconAnimateListener aniDrawable = (IconAnimateListener) iconDrawable; 494 aniDrawable.prepareAnimate(duration, this::animationStartCallback); 495 aniDrawable.setAnimationJankMonitoring(new AnimatorListenerAdapter() { 496 @Override 497 public void onAnimationCancel(Animator animation) { 498 InteractionJankMonitor.getInstance().cancel(CUJ_SPLASHSCREEN_AVD); 499 } 500 501 @Override 502 public void onAnimationEnd(Animator animation) { 503 InteractionJankMonitor.getInstance().end(CUJ_SPLASHSCREEN_AVD); 504 } 505 506 @Override 507 public void onAnimationStart(Animator animation) { 508 InteractionJankMonitor.getInstance().begin( 509 SplashScreenView.this, CUJ_SPLASHSCREEN_AVD); 510 } 511 }); 512 } 513 animationStartCallback()514 private void animationStartCallback() { 515 mIconAnimationStart = Instant.now(); 516 } 517 518 /** 519 * <p>Remove this view and release its resource. </p> 520 * <p><strong>Do not</strong> invoke this method from a drawing method 521 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 522 */ 523 @UiThread remove()524 public void remove() { 525 if (mHasRemoved) { 526 return; 527 } 528 setVisibility(GONE); 529 if (mParceledIconBitmap != null) { 530 if (mIconView instanceof ImageView) { 531 ((ImageView) mIconView).setImageDrawable(null); 532 } else if (mIconView != null) { 533 mIconView.setBackground(null); 534 } 535 mParceledIconBitmap.recycle(); 536 mParceledIconBitmap = null; 537 } 538 if (mParceledBrandingBitmap != null) { 539 mBrandingImageView.setBackground(null); 540 mParceledBrandingBitmap.recycle(); 541 mParceledBrandingBitmap = null; 542 } 543 if (mParceledIconBackgroundBitmap != null) { 544 if (mIconView != null) { 545 mIconView.setBackground(null); 546 } 547 mParceledIconBackgroundBitmap.recycle(); 548 mParceledIconBackgroundBitmap = null; 549 } 550 if (mWindow != null) { 551 final DecorView decorView = (DecorView) mWindow.peekDecorView(); 552 if (DEBUG) { 553 Log.d(TAG, "remove starting view"); 554 } 555 if (decorView != null) { 556 decorView.removeView(this); 557 } 558 restoreSystemUIColors(); 559 mWindow = null; 560 } 561 mHasRemoved = true; 562 } 563 564 /** @hide **/ 565 @Override onDetachedFromWindow()566 protected void onDetachedFromWindow() { 567 super.onDetachedFromWindow(); 568 releaseAnimationSurfaceHost(); 569 } 570 releaseAnimationSurfaceHost()571 private void releaseAnimationSurfaceHost() { 572 if (mSurfaceHost != null && !mIsCopied) { 573 if (DEBUG) { 574 Log.d(TAG, 575 "Shell removed splash screen." 576 + " Releasing SurfaceControlViewHost on thread #" 577 + Thread.currentThread().getId()); 578 } 579 releaseIconHost(mSurfaceHost); 580 mSurfaceHost = null; 581 } else if (mSurfacePackage != null && mSurfaceHost == null) { 582 mSurfacePackage = null; 583 mClientCallback.sendResult(null); 584 } 585 } 586 587 /** 588 * Release the host which hold the SurfaceView of the icon. 589 * @hide 590 */ releaseIconHost(SurfaceControlViewHost host)591 public static void releaseIconHost(SurfaceControlViewHost host) { 592 final Drawable background = host.getView().getBackground(); 593 if (background instanceof SplashScreenView.IconAnimateListener) { 594 ((SplashScreenView.IconAnimateListener) background).stopAnimation(); 595 } 596 host.release(); 597 } 598 599 /** 600 * Called when this view is attached to an activity. This also makes SystemUI colors 601 * transparent so the content of splash screen view can draw fully. 602 * 603 * @hide 604 */ attachHostActivityAndSetSystemUIColors(Activity activity, Window window)605 public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) { 606 mHostActivity = activity; 607 mWindow = window; 608 final WindowManager.LayoutParams attr = window.getAttributes(); 609 mAppWindowFlags = attr.flags; 610 mStatusBarColor = window.getStatusBarColor(); 611 mNavigationBarColor = window.getNavigationBarColor(); 612 mSystemBarsAppearance = window.getInsetsController().getSystemBarsAppearance(); 613 mNavigationContrastEnforced = window.isNavigationBarContrastEnforced(); 614 mStatusContrastEnforced = window.isStatusBarContrastEnforced(); 615 mDecorFitsSystemWindows = window.decorFitsSystemWindows(); 616 617 applySystemBarsContrastColor(window.getInsetsController(), mInitBackgroundColor); 618 // Let app draw the background of bars. 619 window.addFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 620 // Use specified bar colors instead of window background. 621 window.clearFlags(FLAG_TRANSLUCENT_STATUS | FLAG_TRANSLUCENT_NAVIGATION); 622 window.setStatusBarColor(Color.TRANSPARENT); 623 window.setNavigationBarColor(Color.TRANSPARENT); 624 window.setDecorFitsSystemWindows(false); 625 window.setStatusBarContrastEnforced(false); 626 window.setNavigationBarContrastEnforced(false); 627 } 628 629 /** Called when this view is removed from the host activity. */ restoreSystemUIColors()630 private void restoreSystemUIColors() { 631 mWindow.setFlags(mAppWindowFlags, WINDOW_FLAG_MASK); 632 mWindow.setStatusBarColor(mStatusBarColor); 633 mWindow.setNavigationBarColor(mNavigationBarColor); 634 mWindow.getInsetsController().setSystemBarsAppearance(mSystemBarsAppearance, 635 LIGHT_BARS_MASK); 636 mWindow.setDecorFitsSystemWindows(mDecorFitsSystemWindows); 637 mWindow.setStatusBarContrastEnforced(mStatusContrastEnforced); 638 mWindow.setNavigationBarContrastEnforced(mNavigationContrastEnforced); 639 } 640 641 /** 642 * Makes the icon color of system bars contrast. 643 * @hide 644 */ applySystemBarsContrastColor(WindowInsetsController windowInsetsController, int backgroundColor)645 public static void applySystemBarsContrastColor(WindowInsetsController windowInsetsController, 646 int backgroundColor) { 647 final int lightBarAppearance = ContrastColorUtil.isColorLight(backgroundColor) 648 ? LIGHT_BARS_MASK : 0; 649 windowInsetsController.setSystemBarsAppearance(lightBarAppearance, LIGHT_BARS_MASK); 650 } 651 652 /** 653 * Get the view containing the Splash Screen icon and its background. 654 * @see android.R.attr#windowSplashScreenAnimatedIcon 655 */ getIconView()656 public @Nullable View getIconView() { 657 return mIconView; 658 } 659 660 /** 661 * Get the branding image view. 662 * @hide 663 */ 664 @TestApi getBrandingView()665 public @Nullable View getBrandingView() { 666 return mBrandingImageView; 667 } 668 669 /** 670 * Get the initial background color of this view. 671 * @hide 672 */ getInitBackgroundColor()673 public @ColorInt int getInitBackgroundColor() { 674 return mInitBackgroundColor; 675 } 676 677 /** 678 * An interface for an animatable drawable object to register a callback when animation start. 679 * @hide 680 */ 681 public interface IconAnimateListener { 682 /** 683 * Prepare the animation if this drawable also be animatable. 684 * @param duration The animation duration. 685 * @param startListener The callback listener used to receive the start of the animation. 686 * @return true if this drawable object can also be animated and it can be played now. 687 */ prepareAnimate(long duration, Runnable startListener)688 boolean prepareAnimate(long duration, Runnable startListener); 689 690 /** 691 * Stop animation. 692 */ stopAnimation()693 void stopAnimation(); 694 695 /** 696 * Provides a chance to start interaction jank monitoring in avd animation. 697 * @param listener a listener to start jank monitoring 698 */ setAnimationJankMonitoring(AnimatorListenerAdapter listener)699 default void setAnimationJankMonitoring(AnimatorListenerAdapter listener) {} 700 } 701 702 /** 703 * Use to create {@link SplashScreenView} object across process. 704 * @hide 705 */ 706 public static class SplashScreenViewParcelable implements Parcelable { 707 private int mIconSize; 708 private int mBackgroundColor; 709 private Bitmap mIconBackground; 710 711 private Bitmap mIconBitmap = null; 712 private int mBrandingWidth; 713 private int mBrandingHeight; 714 private Bitmap mBrandingBitmap; 715 716 private long mIconAnimationStartMillis; 717 private long mIconAnimationDurationMillis; 718 719 private SurfaceControlViewHost.SurfacePackage mSurfacePackage; 720 private RemoteCallback mClientCallback; 721 SplashScreenViewParcelable(SplashScreenView view)722 public SplashScreenViewParcelable(SplashScreenView view) { 723 mIconSize = view.mIconView.getWidth(); 724 mBackgroundColor = view.getInitBackgroundColor(); 725 mIconBackground = copyDrawable(view.getIconView().getBackground()); 726 mSurfacePackage = view.mSurfacePackageCopy; 727 if (mSurfacePackage == null) { 728 // We only need to copy the drawable if we are not using a SurfaceView 729 mIconBitmap = copyDrawable(((ImageView) view.getIconView()).getDrawable()); 730 } 731 mBrandingBitmap = copyDrawable(view.getBrandingView().getBackground()); 732 733 ViewGroup.LayoutParams params = view.getBrandingView().getLayoutParams(); 734 mBrandingWidth = params.width; 735 mBrandingHeight = params.height; 736 737 if (view.getIconAnimationStart() != null) { 738 mIconAnimationStartMillis = view.getIconAnimationStart().toEpochMilli(); 739 } 740 if (view.getIconAnimationDuration() != null) { 741 mIconAnimationDurationMillis = view.getIconAnimationDuration().toMillis(); 742 } 743 } 744 copyDrawable(Drawable drawable)745 private Bitmap copyDrawable(Drawable drawable) { 746 if (drawable != null) { 747 final Rect initialBounds = drawable.copyBounds(); 748 final int width = initialBounds.width(); 749 final int height = initialBounds.height(); 750 751 final Bitmap snapshot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 752 final Canvas bmpCanvas = new Canvas(snapshot); 753 drawable.setBounds(0, 0, width, height); 754 drawable.draw(bmpCanvas); 755 final Bitmap copyBitmap = snapshot.createAshmemBitmap(); 756 snapshot.recycle(); 757 return copyBitmap; 758 } 759 return null; 760 } 761 SplashScreenViewParcelable(@onNull Parcel source)762 private SplashScreenViewParcelable(@NonNull Parcel source) { 763 readParcel(source); 764 } 765 readParcel(@onNull Parcel source)766 private void readParcel(@NonNull Parcel source) { 767 mIconSize = source.readInt(); 768 mBackgroundColor = source.readInt(); 769 mIconBitmap = source.readTypedObject(Bitmap.CREATOR); 770 mBrandingWidth = source.readInt(); 771 mBrandingHeight = source.readInt(); 772 mBrandingBitmap = source.readTypedObject(Bitmap.CREATOR); 773 mIconAnimationStartMillis = source.readLong(); 774 mIconAnimationDurationMillis = source.readLong(); 775 mIconBackground = source.readTypedObject(Bitmap.CREATOR); 776 mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR); 777 mClientCallback = source.readTypedObject(RemoteCallback.CREATOR); 778 } 779 780 @Override describeContents()781 public int describeContents() { 782 return 0; 783 } 784 785 @Override writeToParcel(Parcel dest, int flags)786 public void writeToParcel(Parcel dest, int flags) { 787 dest.writeInt(mIconSize); 788 dest.writeInt(mBackgroundColor); 789 dest.writeTypedObject(mIconBitmap, flags); 790 dest.writeInt(mBrandingWidth); 791 dest.writeInt(mBrandingHeight); 792 dest.writeTypedObject(mBrandingBitmap, flags); 793 dest.writeLong(mIconAnimationStartMillis); 794 dest.writeLong(mIconAnimationDurationMillis); 795 dest.writeTypedObject(mIconBackground, flags); 796 dest.writeTypedObject(mSurfacePackage, flags); 797 dest.writeTypedObject(mClientCallback, flags); 798 } 799 800 public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR = 801 new Parcelable.Creator<SplashScreenViewParcelable>() { 802 public SplashScreenViewParcelable createFromParcel(@NonNull Parcel source) { 803 return new SplashScreenViewParcelable(source); 804 } 805 public SplashScreenViewParcelable[] newArray(int size) { 806 return new SplashScreenViewParcelable[size]; 807 } 808 }; 809 810 /** 811 * Release the bitmap if another process cannot handle it. 812 */ clearIfNeeded()813 public void clearIfNeeded() { 814 if (mIconBitmap != null) { 815 mIconBitmap.recycle(); 816 mIconBitmap = null; 817 } 818 if (mBrandingBitmap != null) { 819 mBrandingBitmap.recycle(); 820 mBrandingBitmap = null; 821 } 822 } 823 getIconSize()824 int getIconSize() { 825 return mIconSize; 826 } 827 getBackgroundColor()828 int getBackgroundColor() { 829 return mBackgroundColor; 830 } 831 832 /** 833 * Sets the {@link RemoteCallback} that will be called by the client to notify the shell 834 * of the removal of the {@link SplashScreenView}. 835 */ setClientCallback(@onNull RemoteCallback clientCallback)836 public void setClientCallback(@NonNull RemoteCallback clientCallback) { 837 mClientCallback = clientCallback; 838 } 839 } 840 } 841