Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0d8ef8a authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Streamline Dagger Initization.

This simplifies the dagger initilization, removing constructs that
provided no value to the process, but did serve to make the code
more complex to analyze.

Update dagger.md while we're here.

Fixes: 161911916
Test: manual
Change-Id: I84ced47d7cd6ce90664b68339acb7d7db765d56f
parent 5067b565
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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();
    }

+7 −2
Original line number Diff line number Diff line
@@ -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();
    }
}
+12 −79
Original line number Diff line number Diff line
@@ -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). 

@@ -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

@@ -147,7 +112,7 @@ whenever your fragment needs to be created.

```java
public interface FragmentCreator {
+   NavigationBarFragment createNavigationBar();
    NavigationBarFragment createNavigationBar();
}
```

@@ -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

+9 −8
Original line number Diff line number Diff line
@@ -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
@@ -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.
@@ -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) {
@@ -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")
@@ -638,9 +644,4 @@ public class Dependency {
            return mDisplayName;
        }
    }

    @Subcomponent
    public interface DependencyInjector {
        void createSystemUI(Dependency dependency);
    }
}
+2 −32
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.
 */
@@ -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();
    }

@@ -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