• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 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 com.android.systemui
17 
18 import android.app.Activity
19 import android.app.Application
20 import android.app.Service
21 import android.content.BroadcastReceiver
22 import android.content.ContentProvider
23 import android.content.Context
24 import android.content.Intent
25 import android.util.Log
26 import androidx.core.app.AppComponentFactory
27 import com.android.systemui.dagger.ContextComponentHelper
28 import java.lang.reflect.InvocationTargetException
29 import java.util.concurrent.ExecutionException
30 import javax.inject.Inject
31 
32 /**
33  * Implementation of AppComponentFactory that injects into constructors.
34  *
35  * This class sets up dependency injection when creating our application.
36  *
37  * Activities, Services, and BroadcastReceivers support dependency injection into
38  * their constructors.
39  *
40  * ContentProviders support injection into member variables - _not_ constructors.
41  */
42 abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
43     companion object {
44         private const val TAG = "AppComponentFactory"
45         // Must be static due to http://b/141008541.
46         var systemUIInitializer: SystemUIInitializer? = null
47     }
48 
49     @set:Inject
50     lateinit var componentHelper: ContextComponentHelper
51 
52     /**
53      * Returns a new [SystemUIInitializer].
54      *
55      * The returned implementation should be specific to your build.
56      */
57     protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer
58 
59     private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer {
60         return systemUIInitializer ?: run {
61             val initializer = createSystemUIInitializer(context.applicationContext)
62             try {
63                 initializer.init(false)
64             } catch (exception: ExecutionException) {
65                 throw RuntimeException("Failed to initialize SysUI", exception)
66             } catch (exception: InterruptedException) {
67                 throw RuntimeException("Failed to initialize SysUI", exception)
68             }
69             initializer.sysUIComponent.inject(
70                 this@SystemUIAppComponentFactoryBase
71             )
72 
73             systemUIInitializer = initializer
74             return initializer
75         }
76     }
77 
78     override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
79         val app = super.instantiateApplicationCompat(cl, className)
80         if (app !is ContextInitializer) {
81             throw RuntimeException("App must implement ContextInitializer")
82         } else {
83             app.setContextAvailableCallback { context ->
84                 createSystemUIInitializerInternal(context)
85             }
86         }
87 
88         return app
89     }
90 
91     override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
92         val contentProvider = super.instantiateProviderCompat(cl, className)
93         if (contentProvider is ContextInitializer) {
94             contentProvider.setContextAvailableCallback { context ->
95                 val initializer = createSystemUIInitializerInternal(context)
96                 val rootComponent = initializer.sysUIComponent
97                 try {
98                     val injectMethod = rootComponent.javaClass
99                         .getMethod("inject", contentProvider.javaClass)
100                     injectMethod.invoke(rootComponent, contentProvider)
101                 } catch (e: NoSuchMethodException) {
102                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
103                 } catch (e: IllegalAccessException) {
104                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
105                 } catch (e: InvocationTargetException) {
106                     Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
107                 }
108                 initializer
109             }
110         }
111         return contentProvider
112     }
113 
114     override fun instantiateActivityCompat(
115         cl: ClassLoader,
116         className: String,
117         intent: Intent?
118     ): Activity {
119         if (!this::componentHelper.isInitialized) {
120             // This shouldn't happen, but is seen on occasion.
121             // Bug filed against framework to take a look: http://b/141008541
122             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
123         }
124         return componentHelper.resolveActivity(className)
125             ?: super.instantiateActivityCompat(cl, className, intent)
126     }
127 
128     override fun instantiateServiceCompat(
129         cl: ClassLoader,
130         className: String,
131         intent: Intent?
132     ): Service {
133         if (!this::componentHelper.isInitialized) {
134             // This shouldn't happen, but does when a device is freshly formatted.
135             // Bug filed against framework to take a look: http://b/141008541
136             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
137         }
138         return componentHelper.resolveService(className)
139             ?: super.instantiateServiceCompat(cl, className, intent)
140     }
141 
142     override fun instantiateReceiverCompat(
143         cl: ClassLoader,
144         className: String,
145         intent: Intent?
146     ): BroadcastReceiver {
147         if (!this::componentHelper.isInitialized) {
148             // This shouldn't happen, but does when a device is freshly formatted.
149             // Bug filed against framework to take a look: http://b/141008541
150             systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
151         }
152         return componentHelper.resolveBroadcastReceiver(className)
153             ?: super.instantiateReceiverCompat(cl, className, intent)
154     }
155 
156     /**
157      * An Interface for classes that can be notified when an Application Context becomes available.
158      *
159      * An instance of this will be passed to implementers of [ContextInitializer].
160      */
161     fun interface ContextAvailableCallback {
162         /** Notifies when the Application Context is available.  */
163         fun onContextAvailable(context: Context): SystemUIInitializer
164     }
165 
166     /**
167      * Interface for classes that can be constructed by the system before a context is available.
168      *
169      * This is intended for [Application] and [ContentProvider] implementations that
170      * either may not have a Context until some point after construction or are themselves
171      * a [Context].
172      *
173      * Implementers will be passed a [ContextAvailableCallback] that they should call as soon
174      * as an Application Context is ready.
175      */
176     interface ContextInitializer {
177         /**
178          * Called to supply the [ContextAvailableCallback] that should be called when an
179          * Application [Context] is available.
180          */
181         fun setContextAvailableCallback(callback: ContextAvailableCallback)
182     }
183 }
184