| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.systemui |
| |
| import android.app.Activity |
| import android.app.Application |
| import android.app.Service |
| import android.content.BroadcastReceiver |
| import android.content.ContentProvider |
| import android.content.Context |
| import android.content.Intent |
| import android.util.Log |
| import androidx.core.app.AppComponentFactory |
| import com.android.systemui.dagger.ContextComponentHelper |
| import java.lang.reflect.InvocationTargetException |
| import java.util.concurrent.ExecutionException |
| import javax.inject.Inject |
| |
| /** |
| * Implementation of AppComponentFactory that injects into constructors. |
| * |
| * This class sets up dependency injection when creating our application. |
| * |
| * Activities, Services, and BroadcastReceivers support dependency injection into |
| * their constructors. |
| * |
| * ContentProviders support injection into member variables - _not_ constructors. |
| */ |
| abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() { |
| companion object { |
| private const val TAG = "AppComponentFactory" |
| // Must be static due to http://b/141008541. |
| var systemUIInitializer: SystemUIInitializer? = null |
| } |
| |
| @set:Inject |
| lateinit var componentHelper: ContextComponentHelper |
| |
| /** |
| * Returns a new [SystemUIInitializer]. |
| * |
| * The returned implementation should be specific to your build. |
| */ |
| protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer |
| |
| private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer { |
| return systemUIInitializer ?: run { |
| val initializer = createSystemUIInitializer(context.applicationContext) |
| try { |
| initializer.init(false) |
| } catch (exception: ExecutionException) { |
| throw RuntimeException("Failed to initialize SysUI", exception) |
| } catch (exception: InterruptedException) { |
| throw RuntimeException("Failed to initialize SysUI", exception) |
| } |
| initializer.sysUIComponent.inject( |
| this@SystemUIAppComponentFactoryBase |
| ) |
| |
| systemUIInitializer = initializer |
| return initializer |
| } |
| } |
| |
| override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application { |
| val app = super.instantiateApplicationCompat(cl, className) |
| if (app !is ContextInitializer) { |
| throw RuntimeException("App must implement ContextInitializer") |
| } else { |
| app.setContextAvailableCallback { context -> |
| createSystemUIInitializerInternal(context) |
| } |
| } |
| |
| return app |
| } |
| |
| override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider { |
| val contentProvider = super.instantiateProviderCompat(cl, className) |
| if (contentProvider is ContextInitializer) { |
| contentProvider.setContextAvailableCallback { context -> |
| val initializer = createSystemUIInitializerInternal(context) |
| val rootComponent = initializer.sysUIComponent |
| try { |
| val injectMethod = rootComponent.javaClass |
| .getMethod("inject", contentProvider.javaClass) |
| injectMethod.invoke(rootComponent, contentProvider) |
| } catch (e: NoSuchMethodException) { |
| Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) |
| } catch (e: IllegalAccessException) { |
| Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) |
| } catch (e: InvocationTargetException) { |
| Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) |
| } |
| initializer |
| } |
| } |
| return contentProvider |
| } |
| |
| override fun instantiateActivityCompat( |
| cl: ClassLoader, |
| className: String, |
| intent: Intent? |
| ): Activity { |
| if (!this::componentHelper.isInitialized) { |
| // This shouldn't happen, but is seen on occasion. |
| // Bug filed against framework to take a look: http://b/141008541 |
| systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) |
| } |
| return componentHelper.resolveActivity(className) |
| ?: super.instantiateActivityCompat(cl, className, intent) |
| } |
| |
| override fun instantiateServiceCompat( |
| cl: ClassLoader, |
| className: String, |
| intent: Intent? |
| ): Service { |
| if (!this::componentHelper.isInitialized) { |
| // This shouldn't happen, but does when a device is freshly formatted. |
| // Bug filed against framework to take a look: http://b/141008541 |
| systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) |
| } |
| return componentHelper.resolveService(className) |
| ?: super.instantiateServiceCompat(cl, className, intent) |
| } |
| |
| override fun instantiateReceiverCompat( |
| cl: ClassLoader, |
| className: String, |
| intent: Intent? |
| ): BroadcastReceiver { |
| if (!this::componentHelper.isInitialized) { |
| // This shouldn't happen, but does when a device is freshly formatted. |
| // Bug filed against framework to take a look: http://b/141008541 |
| systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) |
| } |
| return componentHelper.resolveBroadcastReceiver(className) |
| ?: super.instantiateReceiverCompat(cl, className, intent) |
| } |
| |
| /** |
| * An Interface for classes that can be notified when an Application Context becomes available. |
| * |
| * An instance of this will be passed to implementers of [ContextInitializer]. |
| */ |
| fun interface ContextAvailableCallback { |
| /** Notifies when the Application Context is available. */ |
| fun onContextAvailable(context: Context): SystemUIInitializer |
| } |
| |
| /** |
| * Interface for classes that can be constructed by the system before a context is available. |
| * |
| * This is intended for [Application] and [ContentProvider] implementations that |
| * either may not have a Context until some point after construction or are themselves |
| * a [Context]. |
| * |
| * Implementers will be passed a [ContextAvailableCallback] that they should call as soon |
| * as an Application Context is ready. |
| */ |
| interface ContextInitializer { |
| /** |
| * Called to supply the [ContextAvailableCallback] that should be called when an |
| * Application [Context] is available. |
| */ |
| fun setContextAvailableCallback(callback: ContextAvailableCallback) |
| } |
| } |