1 /*
2  * Copyright 2021 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 
17 package androidx.core.splashscreen
18 
19 import android.annotation.SuppressLint
20 import android.app.Activity
21 import android.os.Build
22 import android.view.View
23 import android.view.ViewGroup
24 import android.widget.FrameLayout
25 import android.window.SplashScreenView
26 import androidx.annotation.RequiresApi
27 
28 /**
29  * Contains a copy of the splash screen used to create a custom animation from the splash screen to
30  * the application.
31  *
32  * The splashscreen is accessible using [SplashScreenViewProvider.view] and the view containing the
33  * icon using [SplashScreenViewProvider.iconView].
34  *
35  * This class also contains time information about the animated icon (for API 31+).
36  *
37  * The application always needs to call [SplashScreenViewProvider.remove] once it's done with it.
38  */
39 @SuppressLint("ViewConstructor")
40 public class SplashScreenViewProvider internal constructor(ctx: Activity) {
41 
42     @RequiresApi(31)
43     internal constructor(platformView: SplashScreenView, ctx: Activity) : this(ctx) {
44         (impl as ViewImpl31).platformView = platformView
45     }
46 
47     private val impl: ViewImpl =
48         when {
49             Build.VERSION.SDK_INT >= 31 -> ViewImpl31(ctx)
50             else -> ViewImpl(ctx)
<lambda>null51         }.apply { createSplashScreenView() }
52 
53     /**
54      * The splash screen view, copied into this application process.
55      *
56      * This view can be used to create custom animation from the splash screen to the application
57      */
58     public val view: View
59         get() = impl.splashScreenView
60 
61     /**
62      * The view containing the splashscreen icon as defined by
63      * [R.attr.windowSplashScreenAnimatedIcon]
64      */
65     public val iconView: View
66         get() = impl.iconView
67 
68     /**
69      * Start time of the icon animation.
70      *
71      * On API 31+, returns the number of millisecond since the Epoch time (1970-1-1T00:00:00Z)
72      *
73      * Below API 31, returns 0 because the icon cannot be animated.
74      */
75     public val iconAnimationStartMillis: Long
76         get() = impl.iconAnimationStartMillis
77 
78     /** Duration of the icon animation as provided in [R.attr. */
79     public val iconAnimationDurationMillis: Long
80         get() = impl.iconAnimationDurationMillis
81 
82     /**
83      * Remove the SplashScreen's view from the view hierarchy.
84      *
85      * This always needs to be called when an
86      * [androidx.core.splashscreen.SplashScreen.OnExitAnimationListener] is set.
87      */
removenull88     public fun remove(): Unit = impl.remove()
89 
90     private open class ViewImpl(val activity: Activity) {
91 
92         private val _splashScreenView: ViewGroup by lazy {
93             FrameLayout.inflate(activity, R.layout.splash_screen_view, null) as ViewGroup
94         }
95 
96         open fun createSplashScreenView() {
97             val content = activity.findViewById<ViewGroup>(android.R.id.content)
98             (content.rootView as? ViewGroup)?.addView(_splashScreenView)
99         }
100 
101         open val splashScreenView: ViewGroup
102             get() = _splashScreenView
103 
104         open val iconView: View
105             get() = splashScreenView.findViewById(R.id.splashscreen_icon_view)
106 
107         open val iconAnimationStartMillis: Long
108             get() = 0
109 
110         open val iconAnimationDurationMillis: Long
111             get() = 0
112 
113         open fun remove() {
114             (splashScreenView.parent as? ViewGroup)?.removeView(splashScreenView)
115         }
116     }
117 
118     @RequiresApi(31)
119     private class ViewImpl31(activity: Activity) : ViewImpl(activity) {
120         lateinit var platformView: SplashScreenView
121 
createSplashScreenViewnull122         override fun createSplashScreenView() {
123             // Do nothing
124         }
125 
126         override val splashScreenView
127             get() = platformView
128 
129         override val iconView: View
130             get() = if (platformView.iconView != null) platformView.iconView!! else View(activity)
131 
132         override val iconAnimationStartMillis: Long
133             get() = platformView.iconAnimationStart?.toEpochMilli() ?: 0
134 
135         override val iconAnimationDurationMillis: Long
136             get() = platformView.iconAnimationDuration?.toMillis() ?: 0
137 
removenull138         override fun remove() {
139             platformView.remove()
140             if (Build.VERSION.SDK_INT < 33) {
141                 ThemeUtils.Api31.applyThemesSystemBarAppearance(
142                     activity.theme,
143                     activity.window.decorView
144                 )
145             }
146         }
147     }
148 }
149