Loading packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +1 −1 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ public class CarSystemUIFactory extends SystemUIFactory { @Override protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerCarSystemUIRootComponent.builder() .contextHolder(new ContextHolder(context)) .context(context) .build(); } Loading packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +7 −2 Original line number Diff line number Diff line Loading @@ -36,12 +36,17 @@ import dagger.Component; DependencyBinder.class, PipModule.class, OneHandedModule.class, SystemUIFactory.ContextHolder.class, SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, CarSystemUIBinder.class }) public interface CarSystemUIRootComponent extends SystemUIRootComponent { /** * Builder for a CarSystemUIRootComponent. */ @Component.Builder interface Builder extends SystemUIRootComponent.Builder { CarSystemUIRootComponent build(); } } packages/SystemUI/docs/dagger.md +12 −79 Original line number Diff line number Diff line Loading @@ -41,8 +41,6 @@ public interface SystemUIRootComponent { The root component is composed of root modules, which in turn provide the global singleton dependencies across all of SystemUI. - `ContextHolder` is just a wrapper that provides a context. - `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI variants (like other form factors e.g. Car). Loading @@ -52,41 +50,8 @@ variants (like other form factors e.g. Car). ### Adding injection to a new SystemUI object Anything that depends on any `@Singleton` provider from SystemUIRootComponent should be declared as a `@Subcomponent` of the root component. This requires declaring your own interface for generating your own modules or just the object you need injected. The subcomponent also needs to be added to SystemUIRootComponent in SystemUIFactory so it can be acquired. ```java public interface SystemUIRootComponent { + @Singleton + Dependency.DependencyInjector createDependency(); } public class Dependency extends SystemUI { //... + @Subcomponent + public interface DependencyInjector { + Dependency createSystemUI(); + } } ``` For objects which extend SystemUI and require injection, you can define an injector that creates the injected object for you. This other class should be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml). ```java public static class DependencyCreator implements Injector { @Override public SystemUI apply(Context context) { return SystemUIFactory.getInstance().getRootComponent() .createDependency() .createSystemUI(); } } ``` SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses information in that file to locate and construct an instance of the requested SystemUI class. ### Adding a new injectable object Loading Loading @@ -147,7 +112,7 @@ whenever your fragment needs to be created. ```java public interface FragmentCreator { + NavigationBarFragment createNavigationBar(); NavigationBarFragment createNavigationBar(); } ``` Loading @@ -160,49 +125,17 @@ FragmentHostManager.get(view).create(NavigationBarFragment.class); ### Using injection with Views Generally, you shouldn't need to inject for a view, as the view should be relatively self contained and logic that requires injection should be moved to a higher level construct such as a Fragment or a top-level SystemUI component, see above for how to do injection for both of which. DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED. Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a lot of code that could benefit from injection and will need to be migrated off from Dependency#get uses. Similar to how fragments are injected, the view needs to be added to the interface com.android.systemui.util.InjectionInflationController$ViewInstanceCreator. Needing to inject objects into your View's constructor generally implies you are doing more work in your presentation layer than is advisable. Instead, create an injected controller for you view, inject into the controller, and then attach the view to the controller after inflation. ```java public interface ViewInstanceCreator { + QuickStatusBarHeader createQsHeader(); } ``` Presumably you need to inflate that view from XML (otherwise why do you need anything special? see earlier sections about generic injection). To obtain an inflater that supports injected objects, call InjectionInflationController#injectable, which will wrap the inflater it is passed in one that can create injected objects when needed. ```java @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false); } ``` There is one other important thing to note about injecting with views. SysUI already has a Context in its global dagger component, so if you simply inject a Context, you will not get the one that the view should have with proper theming. Because of this, always ensure to tag views that have @Inject with the @Named view context. ```java public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs, OtherCustomDependency something) { //... } ``` View injection generally causes headaches while testing, as inflating a view (which may in turn inflate other views) implicitly causes a Dagger graph to be stood up, which may or may not contain the appropriately faked/mocked/stubbed objects. It is a hard to control process. ## Updating Dagger2 Loading packages/SystemUI/src/com/android/systemui/Dependency.java +9 −8 Original line number Diff line number Diff line Loading @@ -131,9 +131,9 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; import dagger.Subcomponent; /** * Class to handle ugly dependencies throughout sysui until we determine the Loading @@ -150,6 +150,7 @@ import dagger.Subcomponent; * they have no clients they should not have any registered resources like bound * services, registered receivers, etc. */ @Singleton public class Dependency { /** * Key for getting a the main looper. Loading Loading @@ -522,7 +523,12 @@ public class Dependency { mProviders.put(RecordingController.class, mRecordingController::get); mProviders.put(Divider.class, mDivider::get); sDependency = this; Dependency.setInstance(this); } @VisibleForTesting public static void setInstance(Dependency dependency) { sDependency = dependency; } protected final <T> T getDependency(Class<T> cls) { Loading @@ -549,7 +555,7 @@ public class Dependency { } @VisibleForTesting protected <T> T createDependency(Object cls) { public <T> T createDependency(Object cls) { Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>); @SuppressWarnings("unchecked") Loading Loading @@ -638,9 +644,4 @@ public class Dependency { return mDisplayName; } } @Subcomponent public interface DependencyInjector { void createSystemUI(Dependency dependency); } } packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +2 −32 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.os.Handler; Loading @@ -30,7 +29,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.DaggerSystemUIRootComponent; import com.android.systemui.dagger.DependencyProvider; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; Loading @@ -48,9 +46,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.concurrent.Executor; import dagger.Module; import dagger.Provides; /** * Class factory to provide customizable SystemUI components. */ Loading Loading @@ -97,24 +92,13 @@ public class SystemUIFactory { // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. Dependency dependency = new Dependency(); mRootComponent.createDependency().createSystemUI(dependency); Dependency dependency = mRootComponent.createDependency(); dependency.start(); } protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) { if (mRootComponent != null) { throw new RuntimeException("Root component can be set only once."); } mRootComponent = rootComponent; } protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerSystemUIRootComponent.builder() .dependencyProvider(new DependencyProvider()) .contextHolder(new ContextHolder(context)) .context(context) .build(); } Loading Loading @@ -168,18 +152,4 @@ public class SystemUIFactory { Dependency.get(DozeParameters.class), Dependency.get(BubbleController.class)); } @Module public static class ContextHolder { private Context mContext; public ContextHolder(Context context) { mContext = context; } @Provides public Context provideContext() { return mContext; } } } Loading
packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +1 −1 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ public class CarSystemUIFactory extends SystemUIFactory { @Override protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerCarSystemUIRootComponent.builder() .contextHolder(new ContextHolder(context)) .context(context) .build(); } Loading
packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +7 −2 Original line number Diff line number Diff line Loading @@ -36,12 +36,17 @@ import dagger.Component; DependencyBinder.class, PipModule.class, OneHandedModule.class, SystemUIFactory.ContextHolder.class, SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, CarSystemUIBinder.class }) public interface CarSystemUIRootComponent extends SystemUIRootComponent { /** * Builder for a CarSystemUIRootComponent. */ @Component.Builder interface Builder extends SystemUIRootComponent.Builder { CarSystemUIRootComponent build(); } }
packages/SystemUI/docs/dagger.md +12 −79 Original line number Diff line number Diff line Loading @@ -41,8 +41,6 @@ public interface SystemUIRootComponent { The root component is composed of root modules, which in turn provide the global singleton dependencies across all of SystemUI. - `ContextHolder` is just a wrapper that provides a context. - `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI variants (like other form factors e.g. Car). Loading @@ -52,41 +50,8 @@ variants (like other form factors e.g. Car). ### Adding injection to a new SystemUI object Anything that depends on any `@Singleton` provider from SystemUIRootComponent should be declared as a `@Subcomponent` of the root component. This requires declaring your own interface for generating your own modules or just the object you need injected. The subcomponent also needs to be added to SystemUIRootComponent in SystemUIFactory so it can be acquired. ```java public interface SystemUIRootComponent { + @Singleton + Dependency.DependencyInjector createDependency(); } public class Dependency extends SystemUI { //... + @Subcomponent + public interface DependencyInjector { + Dependency createSystemUI(); + } } ``` For objects which extend SystemUI and require injection, you can define an injector that creates the injected object for you. This other class should be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml). ```java public static class DependencyCreator implements Injector { @Override public SystemUI apply(Context context) { return SystemUIFactory.getInstance().getRootComponent() .createDependency() .createSystemUI(); } } ``` SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses information in that file to locate and construct an instance of the requested SystemUI class. ### Adding a new injectable object Loading Loading @@ -147,7 +112,7 @@ whenever your fragment needs to be created. ```java public interface FragmentCreator { + NavigationBarFragment createNavigationBar(); NavigationBarFragment createNavigationBar(); } ``` Loading @@ -160,49 +125,17 @@ FragmentHostManager.get(view).create(NavigationBarFragment.class); ### Using injection with Views Generally, you shouldn't need to inject for a view, as the view should be relatively self contained and logic that requires injection should be moved to a higher level construct such as a Fragment or a top-level SystemUI component, see above for how to do injection for both of which. DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED. Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a lot of code that could benefit from injection and will need to be migrated off from Dependency#get uses. Similar to how fragments are injected, the view needs to be added to the interface com.android.systemui.util.InjectionInflationController$ViewInstanceCreator. Needing to inject objects into your View's constructor generally implies you are doing more work in your presentation layer than is advisable. Instead, create an injected controller for you view, inject into the controller, and then attach the view to the controller after inflation. ```java public interface ViewInstanceCreator { + QuickStatusBarHeader createQsHeader(); } ``` Presumably you need to inflate that view from XML (otherwise why do you need anything special? see earlier sections about generic injection). To obtain an inflater that supports injected objects, call InjectionInflationController#injectable, which will wrap the inflater it is passed in one that can create injected objects when needed. ```java @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false); } ``` There is one other important thing to note about injecting with views. SysUI already has a Context in its global dagger component, so if you simply inject a Context, you will not get the one that the view should have with proper theming. Because of this, always ensure to tag views that have @Inject with the @Named view context. ```java public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs, OtherCustomDependency something) { //... } ``` View injection generally causes headaches while testing, as inflating a view (which may in turn inflate other views) implicitly causes a Dagger graph to be stood up, which may or may not contain the appropriately faked/mocked/stubbed objects. It is a hard to control process. ## Updating Dagger2 Loading
packages/SystemUI/src/com/android/systemui/Dependency.java +9 −8 Original line number Diff line number Diff line Loading @@ -131,9 +131,9 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; import dagger.Subcomponent; /** * Class to handle ugly dependencies throughout sysui until we determine the Loading @@ -150,6 +150,7 @@ import dagger.Subcomponent; * they have no clients they should not have any registered resources like bound * services, registered receivers, etc. */ @Singleton public class Dependency { /** * Key for getting a the main looper. Loading Loading @@ -522,7 +523,12 @@ public class Dependency { mProviders.put(RecordingController.class, mRecordingController::get); mProviders.put(Divider.class, mDivider::get); sDependency = this; Dependency.setInstance(this); } @VisibleForTesting public static void setInstance(Dependency dependency) { sDependency = dependency; } protected final <T> T getDependency(Class<T> cls) { Loading @@ -549,7 +555,7 @@ public class Dependency { } @VisibleForTesting protected <T> T createDependency(Object cls) { public <T> T createDependency(Object cls) { Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>); @SuppressWarnings("unchecked") Loading Loading @@ -638,9 +644,4 @@ public class Dependency { return mDisplayName; } } @Subcomponent public interface DependencyInjector { void createSystemUI(Dependency dependency); } }
packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +2 −32 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.os.Handler; Loading @@ -30,7 +29,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.DaggerSystemUIRootComponent; import com.android.systemui.dagger.DependencyProvider; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; Loading @@ -48,9 +46,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.concurrent.Executor; import dagger.Module; import dagger.Provides; /** * Class factory to provide customizable SystemUI components. */ Loading Loading @@ -97,24 +92,13 @@ public class SystemUIFactory { // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. Dependency dependency = new Dependency(); mRootComponent.createDependency().createSystemUI(dependency); Dependency dependency = mRootComponent.createDependency(); dependency.start(); } protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) { if (mRootComponent != null) { throw new RuntimeException("Root component can be set only once."); } mRootComponent = rootComponent; } protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerSystemUIRootComponent.builder() .dependencyProvider(new DependencyProvider()) .contextHolder(new ContextHolder(context)) .context(context) .build(); } Loading Loading @@ -168,18 +152,4 @@ public class SystemUIFactory { Dependency.get(DozeParameters.class), Dependency.get(BubbleController.class)); } @Module public static class ContextHolder { private Context mContext; public ContextHolder(Context context) { mContext = context; } @Provides public Context provideContext() { return mContext; } } }