Change SysemUIFactory to SystemUIInitializer.
This change allows variants of SystemUI to remove the reflection traditionally used to startup systemui and instead directly stand up the code that they need. Specifically, it introduces two primary pieces: - SystemUIInitializer A rename of the SystemUIFactory along with some modest refactoring. It is now an abstract class that new SystemUI build targets should implement themselves. Implementations use this class to specify a root Dagger component used to stand up the rest of SystemUI. A default SystemUIInitializerImpl is provided that starts up AOSP. - SystemUIAppComponentFactoryBase An abstract implementation of AppComponentFactory that is used to decide what SystemUIInitializer should be loaded. Implementers should return a SystemUIInitializer instance of their choice in #createSystemUIInitializer. The implementation of SystemUIAppComponentFactoryBase should in turn be specified in AndroidManifest.xml's Application.android:appComponentFactory attribute. The default SystemUIAppComponentFactory loads the SystemUIInitializer implementation reflectively. This should be considered deprecated behavior and may be removed in a future release. Please define your own SystemUIAppComponentFactoryBase implementation and return a specific SystemUIInitializer implementation from it. Currently, AOSP, TV, and Pixel specific builds still rely on the reflective loader. Other internal builds have been moved off of the reflective loader. Bug: 235624311 Test: manual Change-Id: I7029fb2960f54fb29e08b96ee49170f150a1d0b6
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
-keep class com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
-keep class com.android.systemui.statusbar.tv.TvStatusBar
|
||||
-keep class com.android.systemui.car.CarSystemUIFactory
|
||||
-keep class com.android.systemui.SystemUIFactory
|
||||
-keep class com.android.systemui.tv.TvSystemUIFactory
|
||||
-keep class com.android.systemui.SystemUIInitializer
|
||||
-keep class com.android.systemui.tv.TvSystemUIInitializer
|
||||
-keep class * extends com.android.systemui.CoreStartable
|
||||
-keep class * implements com.android.systemui.CoreStartable$Injector
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<resources>
|
||||
<!-- SystemUIFactory component -->
|
||||
<string name="config_systemUIFactoryComponent" translatable="false">
|
||||
com.android.systemui.tv.TvSystemUIFactory
|
||||
com.android.systemui.tv.TvSystemUIInitializer
|
||||
</string>
|
||||
|
||||
<!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
<bool name="config_enableFullscreenUserSwitcher">false</bool>
|
||||
|
||||
<!-- SystemUIFactory component -->
|
||||
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
|
||||
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIInitializerImpl</string>
|
||||
|
||||
<!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
|
||||
<dimen name="config_qsTileStrokeWidthActive">-1dp</dimen>
|
||||
|
||||
@@ -16,160 +16,22 @@
|
||||
|
||||
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.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.AppComponentFactory;
|
||||
|
||||
import com.android.systemui.dagger.ContextComponentHelper;
|
||||
import com.android.systemui.dagger.SysUIComponent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Implementation of AppComponentFactory that injects into constructors.
|
||||
* Starts up SystemUI using the AOSP {@link SystemUIInitializerImpl}.
|
||||
*
|
||||
* This class sets up dependency injection when creating our application.
|
||||
* This initializer relies on reflection to start everything up and should be considered deprecated.
|
||||
* Instead, create your own {@link SystemUIAppComponentFactoryBase}, specify it in your
|
||||
* AndroidManifest.xml and construct your own {@link SystemUIInitializer} directly.
|
||||
*
|
||||
* Services support dependency injection into their constructors.
|
||||
*
|
||||
* ContentProviders support injection into member variables - _not_ constructors.
|
||||
* @deprecated Define your own SystemUIAppComponentFactoryBase implementation and use that. This
|
||||
* implementation may be changed or removed in future releases.
|
||||
*/
|
||||
public class SystemUIAppComponentFactory extends AppComponentFactory {
|
||||
|
||||
private static final String TAG = "AppComponentFactory";
|
||||
@Inject
|
||||
public ContextComponentHelper mComponentHelper;
|
||||
|
||||
public SystemUIAppComponentFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Deprecated
|
||||
public class SystemUIAppComponentFactory extends SystemUIAppComponentFactoryBase {
|
||||
@Override
|
||||
public Application instantiateApplicationCompat(
|
||||
@NonNull ClassLoader cl, @NonNull String className)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
Application app = super.instantiateApplicationCompat(cl, className);
|
||||
if (app instanceof ContextInitializer) {
|
||||
((ContextInitializer) app).setContextAvailableCallback(
|
||||
context -> {
|
||||
SystemUIFactory.createFromConfig(context);
|
||||
SystemUIFactory.getInstance().getSysUIComponent().inject(
|
||||
SystemUIAppComponentFactory.this);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ContentProvider instantiateProviderCompat(
|
||||
@NonNull ClassLoader cl, @NonNull String className)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
|
||||
ContentProvider contentProvider = super.instantiateProviderCompat(cl, className);
|
||||
if (contentProvider instanceof ContextInitializer) {
|
||||
((ContextInitializer) contentProvider).setContextAvailableCallback(
|
||||
context -> {
|
||||
SystemUIFactory.createFromConfig(context);
|
||||
SysUIComponent rootComponent =
|
||||
SystemUIFactory.getInstance().getSysUIComponent();
|
||||
try {
|
||||
Method injectMethod = rootComponent.getClass()
|
||||
.getMethod("inject", contentProvider.getClass());
|
||||
injectMethod.invoke(rootComponent, contentProvider);
|
||||
} catch (NoSuchMethodException
|
||||
| IllegalAccessException
|
||||
| InvocationTargetException e) {
|
||||
Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return contentProvider;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
|
||||
@Nullable Intent intent)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
if (mComponentHelper == null) {
|
||||
// This shouldn't happen, but is seen on occasion.
|
||||
// Bug filed against framework to take a look: http://b/141008541
|
||||
SystemUIFactory.getInstance().getSysUIComponent().inject(
|
||||
SystemUIAppComponentFactory.this);
|
||||
}
|
||||
Activity activity = mComponentHelper.resolveActivity(className);
|
||||
if (activity != null) {
|
||||
return activity;
|
||||
}
|
||||
return super.instantiateActivityCompat(cl, className, intent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Service instantiateServiceCompat(
|
||||
@NonNull ClassLoader cl, @NonNull String className, Intent intent)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
if (mComponentHelper == null) {
|
||||
// This shouldn't happen, but does when a device is freshly formatted.
|
||||
// Bug filed against framework to take a look: http://b/141008541
|
||||
SystemUIFactory.getInstance().getSysUIComponent().inject(
|
||||
SystemUIAppComponentFactory.this);
|
||||
}
|
||||
Service service = mComponentHelper.resolveService(className);
|
||||
if (service != null) {
|
||||
return service;
|
||||
}
|
||||
return super.instantiateServiceCompat(cl, className, intent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
|
||||
@NonNull String className, @Nullable Intent intent)
|
||||
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
|
||||
if (mComponentHelper == null) {
|
||||
// This shouldn't happen, but does when a device is freshly formatted.
|
||||
// Bug filed against framework to take a look: http://b/141008541
|
||||
SystemUIFactory.getInstance().getSysUIComponent().inject(
|
||||
SystemUIAppComponentFactory.this);
|
||||
}
|
||||
BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className);
|
||||
if (receiver != null) {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
return super.instantiateReceiverCompat(cl, className, intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback that receives a Context when one is ready.
|
||||
*/
|
||||
public interface ContextAvailableCallback {
|
||||
void onContextAvailable(Context context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented in classes that get started by the system before a context is available.
|
||||
*/
|
||||
public interface ContextInitializer {
|
||||
void setContextAvailableCallback(ContextAvailableCallback callback);
|
||||
protected SystemUIInitializer createSystemUIInitializer(Context context) {
|
||||
return SystemUIInitializerFactory.createWithContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ import android.view.SurfaceControl;
|
||||
import android.view.ThreadedRenderer;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.systemui.dagger.ContextComponentHelper;
|
||||
import com.android.systemui.dagger.GlobalRootComponent;
|
||||
import com.android.systemui.dagger.SysUIComponent;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
@@ -65,7 +64,6 @@ public class SystemUIApplication extends Application implements
|
||||
public static final String TAG = "SystemUIService";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private ContextComponentHelper mComponentHelper;
|
||||
private BootCompleteCacheImpl mBootCompleteCache;
|
||||
private DumpManager mDumpManager;
|
||||
|
||||
@@ -80,8 +78,8 @@ public class SystemUIApplication extends Application implements
|
||||
private CoreStartable[] mServices;
|
||||
private boolean mServicesStarted;
|
||||
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
|
||||
private GlobalRootComponent mRootComponent;
|
||||
private SysUIComponent mSysUIComponent;
|
||||
private SystemUIInitializer mInitializer;
|
||||
|
||||
public SystemUIApplication() {
|
||||
super();
|
||||
@@ -90,6 +88,10 @@ public class SystemUIApplication extends Application implements
|
||||
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
|
||||
}
|
||||
|
||||
protected GlobalRootComponent getRootComponent() {
|
||||
return mInitializer.getRootComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@@ -99,10 +101,8 @@ public class SystemUIApplication extends Application implements
|
||||
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
|
||||
Trace.TRACE_TAG_APP);
|
||||
log.traceBegin("DependencyInjection");
|
||||
mContextAvailableCallback.onContextAvailable(this);
|
||||
mRootComponent = SystemUIFactory.getInstance().getRootComponent();
|
||||
mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
|
||||
mComponentHelper = mSysUIComponent.getContextComponentHelper();
|
||||
mInitializer = mContextAvailableCallback.onContextAvailable(this);
|
||||
mSysUIComponent = mInitializer.getSysUIComponent();
|
||||
mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
|
||||
log.traceEnd();
|
||||
|
||||
@@ -189,15 +189,14 @@ public class SystemUIApplication extends Application implements
|
||||
*/
|
||||
|
||||
public void startServicesIfNeeded() {
|
||||
final String vendorComponent = SystemUIFactory.getInstance()
|
||||
.getVendorComponent(getResources());
|
||||
final String vendorComponent = mInitializer.getVendorComponent(getResources());
|
||||
|
||||
// Sort the startables so that we get a deterministic ordering.
|
||||
// TODO: make #start idempotent and require users of CoreStartable to call it.
|
||||
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
|
||||
Comparator.comparing(Class::getName));
|
||||
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
|
||||
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
|
||||
sortedStartables.putAll(mSysUIComponent.getStartables());
|
||||
sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
|
||||
startServicesIfNeeded(
|
||||
sortedStartables, "StartServices", vendorComponent);
|
||||
}
|
||||
@@ -212,7 +211,7 @@ public class SystemUIApplication extends Application implements
|
||||
// Sort the startables so that we get a deterministic ordering.
|
||||
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
|
||||
Comparator.comparing(Class::getName));
|
||||
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
|
||||
sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
|
||||
startServicesIfNeeded(
|
||||
sortedStartables, "StartSecondaryServices", null);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* Copyright (C) 2022 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.
|
||||
@@ -11,7 +11,7 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui;
|
||||
@@ -22,8 +22,6 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.dagger.DaggerGlobalRootComponent;
|
||||
import com.android.systemui.dagger.GlobalRootComponent;
|
||||
import com.android.systemui.dagger.SysUIComponent;
|
||||
import com.android.systemui.dagger.WMComponent;
|
||||
@@ -31,66 +29,47 @@ import com.android.systemui.util.InitializationChecker;
|
||||
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
|
||||
import com.android.wm.shell.transition.ShellTransitions;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* Class factory to provide customizable SystemUI components.
|
||||
* Initializer that stands up SystemUI.
|
||||
*
|
||||
* Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own
|
||||
* Dagger root component.
|
||||
*/
|
||||
public class SystemUIFactory {
|
||||
public abstract class SystemUIInitializer {
|
||||
private static final String TAG = "SystemUIFactory";
|
||||
|
||||
static SystemUIFactory mFactory;
|
||||
private final Context mContext;
|
||||
|
||||
private GlobalRootComponent mRootComponent;
|
||||
private WMComponent mWMComponent;
|
||||
private SysUIComponent mSysUIComponent;
|
||||
private InitializationChecker mInitializationChecker;
|
||||
|
||||
public static <T extends SystemUIFactory> T getInstance() {
|
||||
return (T) mFactory;
|
||||
public SystemUIInitializer(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static void createFromConfig(Context context) {
|
||||
createFromConfig(context, false);
|
||||
protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
|
||||
|
||||
/**
|
||||
* Prepares the SysUIComponent builder before it is built.
|
||||
* @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
|
||||
* @param wm the built WMComponent from the root component's getWMComponent() method
|
||||
*/
|
||||
protected SysUIComponent.Builder prepareSysUIComponentBuilder(
|
||||
SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
|
||||
return sysUIBuilder;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void createFromConfig(Context context, boolean fromTest) {
|
||||
if (mFactory != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
|
||||
if (clsName == null || clsName.length() == 0) {
|
||||
throw new RuntimeException("No SystemUIFactory component configured");
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> cls = null;
|
||||
cls = context.getClassLoader().loadClass(clsName);
|
||||
mFactory = (SystemUIFactory) cls.newInstance();
|
||||
mFactory.init(context, fromTest);
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void cleanup() {
|
||||
mFactory = null;
|
||||
}
|
||||
|
||||
public SystemUIFactory() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public void init(Context context, boolean fromTest)
|
||||
throws ExecutionException, InterruptedException {
|
||||
/**
|
||||
* Starts the initialization process. This stands up the Dagger graph.
|
||||
*/
|
||||
public void init(boolean fromTest) throws ExecutionException, InterruptedException {
|
||||
mRootComponent = getGlobalRootComponentBuilder()
|
||||
.context(context)
|
||||
.context(mContext)
|
||||
.instrumentationTest(fromTest)
|
||||
.build();
|
||||
|
||||
@@ -98,7 +77,7 @@ public class SystemUIFactory {
|
||||
boolean initializeComponents = mInitializationChecker.initializeComponents();
|
||||
|
||||
// Stand up WMComponent
|
||||
setupWmComponent(context);
|
||||
setupWmComponent(mContext);
|
||||
if (initializeComponents) {
|
||||
// Only initialize when not starting from tests since this currently initializes some
|
||||
// components that shouldn't be run in the test environment
|
||||
@@ -188,20 +167,6 @@ public class SystemUIFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the SysUIComponent builder before it is built.
|
||||
* @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
|
||||
* @param wm the built WMComponent from the root component's getWMComponent() method
|
||||
*/
|
||||
protected SysUIComponent.Builder prepareSysUIComponentBuilder(
|
||||
SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
|
||||
return sysUIBuilder;
|
||||
}
|
||||
|
||||
protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
|
||||
return DaggerGlobalRootComponent.builder();
|
||||
}
|
||||
|
||||
public GlobalRootComponent getRootComponent() {
|
||||
return mRootComponent;
|
||||
}
|
||||
@@ -214,24 +179,10 @@ public class SystemUIFactory {
|
||||
return mSysUIComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@link CoreStartable} components that should be started at startup.
|
||||
*/
|
||||
public Map<Class<?>, Provider<CoreStartable>> getStartableComponents() {
|
||||
return mSysUIComponent.getStartables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of additional system UI components that should be started.
|
||||
*/
|
||||
public String getVendorComponent(Resources resources) {
|
||||
return resources.getString(R.string.config_systemUIVendorServiceComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@link CoreStartable} components that should be started per user.
|
||||
*/
|
||||
public Map<Class<?>, Provider<CoreStartable>> getStartableComponentsPerUser() {
|
||||
return mSysUIComponent.getPerUserStartables();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.systemui.util.Assert
|
||||
|
||||
/**
|
||||
* Factory to reflectively lookup a [SystemUIInitializer] to start SystemUI with.
|
||||
*/
|
||||
@Deprecated("Provide your own {@link SystemUIAppComponentFactoryBase} that doesn't need this.")
|
||||
object SystemUIInitializerFactory {
|
||||
private const val TAG = "SysUIInitializerFactory"
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private var initializer: SystemUIInitializer? = null
|
||||
|
||||
/**
|
||||
* Instantiate a [SystemUIInitializer] reflectively.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createWithContext(context: Context): SystemUIInitializer {
|
||||
return createFromConfig(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a [SystemUIInitializer] reflectively.
|
||||
*/
|
||||
@JvmStatic
|
||||
private fun createFromConfig(context: Context): SystemUIInitializer {
|
||||
Assert.isMainThread()
|
||||
|
||||
return createFromConfigNoAssert(context)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@VisibleForTesting
|
||||
fun createFromConfigNoAssert(context: Context): SystemUIInitializer {
|
||||
|
||||
return initializer ?: run {
|
||||
val className = context.getString(R.string.config_systemUIFactoryComponent)
|
||||
if (className.isEmpty()) {
|
||||
throw RuntimeException("No SystemUIFactory component configured")
|
||||
}
|
||||
try {
|
||||
val cls = context.classLoader.loadClass(className)
|
||||
val constructor = cls.getConstructor(Context::class.java)
|
||||
(constructor.newInstance(context) as SystemUIInitializer).apply {
|
||||
initializer = this
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Log.w(TAG, "Error creating SystemUIInitializer component: $className", t)
|
||||
throw t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.content.Context
|
||||
import com.android.systemui.dagger.DaggerGlobalRootComponent
|
||||
import com.android.systemui.dagger.GlobalRootComponent
|
||||
|
||||
/**
|
||||
* {@link SystemUIInitializer} that stands up AOSP SystemUI.
|
||||
*/
|
||||
class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
|
||||
override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
|
||||
return DaggerGlobalRootComponent.builder()
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import com.android.systemui.BootCompleteCacheImpl;
|
||||
import com.android.systemui.CoreStartable;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.InitController;
|
||||
import com.android.systemui.SystemUIAppComponentFactory;
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase;
|
||||
import com.android.systemui.dagger.qualifiers.PerUser;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.keyguard.KeyguardSliceProvider;
|
||||
@@ -241,7 +241,7 @@ public interface SysUIComponent {
|
||||
/**
|
||||
* Member injection into the supplied argument.
|
||||
*/
|
||||
void inject(SystemUIAppComponentFactory factory);
|
||||
void inject(SystemUIAppComponentFactoryBase factory);
|
||||
|
||||
/**
|
||||
* Member injection into the supplied argument.
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.android.keyguard.clock.ClockModule;
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.BootCompleteCache;
|
||||
import com.android.systemui.BootCompleteCacheImpl;
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
import com.android.systemui.appops.dagger.AppOpsModule;
|
||||
import com.android.systemui.assist.AssistModule;
|
||||
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
|
||||
@@ -198,11 +197,6 @@ public abstract class SystemUIModule {
|
||||
@Binds
|
||||
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
|
||||
|
||||
@Provides
|
||||
static SystemUIFactory getSystemUIFactory() {
|
||||
return SystemUIFactory.getInstance();
|
||||
}
|
||||
|
||||
// TODO: This should provided by the WM component
|
||||
/** Provides Optional of BubbleManager */
|
||||
@SysUISingleton
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.os.HandlerThread;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
import com.android.systemui.SystemUIInitializerFactory;
|
||||
import com.android.systemui.tv.TvWMComponent;
|
||||
import com.android.wm.shell.ShellCommandHandler;
|
||||
import com.android.wm.shell.ShellInit;
|
||||
@@ -52,7 +52,7 @@ import dagger.Subcomponent;
|
||||
/**
|
||||
* Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
|
||||
* from the WM component into the SysUI component (in
|
||||
* {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies
|
||||
* {@link SystemUIInitializerFactory#init(Context, boolean)}), and references the specific dependencies
|
||||
* provided by its particular device/form-factor SystemUI implementation.
|
||||
*
|
||||
* ie. {@link WMComponent} includes {@link WMShellModule}
|
||||
|
||||
@@ -29,7 +29,8 @@ import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.systemui.SystemUIAppComponentFactory;
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback;
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextInitializer;
|
||||
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
|
||||
import com.android.systemui.shared.system.PeopleProviderUtils;
|
||||
|
||||
@@ -37,11 +38,11 @@ import javax.inject.Inject;
|
||||
|
||||
/** API that returns a People Tile preview. */
|
||||
public class PeopleProvider extends ContentProvider implements
|
||||
SystemUIAppComponentFactory.ContextInitializer {
|
||||
ContextInitializer {
|
||||
private static final String TAG = "PeopleProvider";
|
||||
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
|
||||
private static final String EMPTY_STRING = "";
|
||||
private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
|
||||
private ContextAvailableCallback mCallback;
|
||||
|
||||
@Inject
|
||||
PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
|
||||
@@ -144,7 +145,7 @@ public class PeopleProvider extends ContentProvider implements
|
||||
|
||||
@Override
|
||||
public void setContextAvailableCallback(
|
||||
SystemUIAppComponentFactory.ContextAvailableCallback callback) {
|
||||
ContextAvailableCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,20 @@
|
||||
|
||||
package com.android.systemui.tv;
|
||||
|
||||
import com.android.systemui.SystemUIFactory;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.SystemUIInitializer;
|
||||
import com.android.systemui.dagger.GlobalRootComponent;
|
||||
|
||||
/**
|
||||
* TV variant {@link SystemUIFactory}, that substitutes default {@link GlobalRootComponent} for
|
||||
* TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for
|
||||
* {@link TvGlobalRootComponent}
|
||||
*/
|
||||
public class TvSystemUIFactory extends SystemUIFactory {
|
||||
public class TvSystemUIInitializer extends SystemUIInitializer {
|
||||
public TvSystemUIInitializer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
|
||||
return DaggerTvGlobalRootComponent.builder();
|
||||
@@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@SmallTest
|
||||
public class DependencyTest extends SysuiTestCase {
|
||||
|
||||
@@ -44,10 +46,12 @@ public class DependencyTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitDependency() {
|
||||
public void testInitDependency() throws ExecutionException, InterruptedException {
|
||||
Dependency.clearDependencies();
|
||||
Dependency dependency =
|
||||
SystemUIFactory.getInstance().getSysUIComponent().createDependency();
|
||||
SystemUIInitializer initializer =
|
||||
SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
|
||||
initializer.init(true);
|
||||
Dependency dependency = initializer.getSysUIComponent().createDependency();
|
||||
dependency.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
|
||||
|
||||
public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES;
|
||||
@@ -54,10 +56,11 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void SysuiSetup() {
|
||||
SystemUIFactory.createFromConfig(mContext, true);
|
||||
mDependency = new TestableDependency(
|
||||
SystemUIFactory.getInstance().getSysUIComponent().createDependency());
|
||||
public void sysuiSetup() throws ExecutionException, InterruptedException {
|
||||
SystemUIInitializer initializer =
|
||||
SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
|
||||
initializer.init(true);
|
||||
mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
|
||||
Dependency.setInstance(mDependency);
|
||||
|
||||
// TODO: Figure out another way to give reference to a SysuiTestableContext.
|
||||
@@ -77,7 +80,6 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
|
||||
public void SysuiTeardown() {
|
||||
InstrumentationRegistry.registerInstance(mRealInstrumentation,
|
||||
InstrumentationRegistry.getArguments());
|
||||
SystemUIFactory.cleanup();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
||||
@@ -76,9 +76,10 @@ public abstract class SysuiTestCase {
|
||||
|
||||
@Before
|
||||
public void SysuiSetup() throws Exception {
|
||||
SystemUIFactory.createFromConfig(mContext, true);
|
||||
mDependency = new TestableDependency(
|
||||
SystemUIFactory.getInstance().getSysUIComponent().createDependency());
|
||||
SystemUIInitializer initializer =
|
||||
SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
|
||||
initializer.init(true);
|
||||
mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
|
||||
Dependency.setInstance(mDependency);
|
||||
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
|
||||
mock(Executor.class), mock(DumpManager.class),
|
||||
|
||||
@@ -45,6 +45,7 @@ import androidx.slice.core.SliceQuery;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.SystemUIInitializerImpl;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
@@ -100,7 +101,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mIsZenMode = false;
|
||||
mProvider = new TestableKeyguardSliceProvider();
|
||||
mProvider.setContextAvailableCallback(context -> { });
|
||||
mProvider.setContextAvailableCallback(context -> new SystemUIInitializerImpl(mContext));
|
||||
mProvider.attachInfo(getContext(), null);
|
||||
reset(mContentResolver);
|
||||
SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.systemui.SysuiBaseFragmentTest;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.log.LogBuffer;
|
||||
import com.android.systemui.log.LogcatEchoTracker;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.DisableFlagsLogger;
|
||||
@@ -112,6 +113,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
|
||||
mDependency.injectMockDependency(DarkIconDispatcher.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user