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