Merge "Update README.md and dagger.md." into tm-qpr-dev
This commit is contained in:
@@ -5,46 +5,72 @@
|
||||
SystemUI is a persistent process that provides UI for the system but outside
|
||||
of the system_server process.
|
||||
|
||||
The starting point for most of sysui code is a list of services that extend
|
||||
SystemUI that are started up by SystemUIApplication. These services then depend
|
||||
on some custom dependency injection provided by Dependency.
|
||||
|
||||
Inputs directed at sysui (as opposed to general listeners) generally come in
|
||||
through IStatusBar. Outputs from sysui are through a variety of private APIs to
|
||||
the android platform all over.
|
||||
|
||||
## SystemUIApplication
|
||||
|
||||
When SystemUIApplication starts up, it will start up the services listed in
|
||||
config_systemUIServiceComponents or config_systemUIServiceComponentsPerUser.
|
||||
When SystemUIApplication starts up, it instantiates a Dagger graph from which
|
||||
various pieces of the application are built.
|
||||
|
||||
Each of these services extend SystemUI. SystemUI provides them with a Context
|
||||
and gives them callbacks for onConfigurationChanged (this historically was
|
||||
the main path for onConfigurationChanged, now also happens through
|
||||
ConfigurationController). They also receive a callback for onBootCompleted
|
||||
To support customization, SystemUIApplication relies on the AndroidManifest.xml
|
||||
having an `android.app.AppComponentFactory` specified. Specifically, it relies
|
||||
on an `AppComponentFactory` that subclases `SystemUIAppComponentFactoryBase`.
|
||||
Implementations of this abstract base class must override
|
||||
`#createSystemUIInitializer(Context)` which returns a `SystemUIInitializer`.
|
||||
`SystemUIInitializer` primary job in turn is to intialize and return the Dagger
|
||||
root component back to the `SystemUIApplication`.
|
||||
|
||||
Writing a custom `SystemUIAppComponentFactoryBase` and `SystemUIInitializer`,
|
||||
should be enough for most implementations to stand up a customized Dagger
|
||||
graph, and launch a custom version of SystemUI.
|
||||
|
||||
## Dagger / Dependency Injection
|
||||
|
||||
See [dagger.md](docs/dagger.md) and https://dagger.dev/.
|
||||
|
||||
## CoreStartable
|
||||
|
||||
The starting point for most of SystemUI code is a list of classes that
|
||||
implement `CoreStartable` that are started up by SystemUIApplication.
|
||||
CoreStartables are like miniature services. They have their `#start` method
|
||||
called after being instantiated, and a reference to them is stored inside
|
||||
SystemUIApplication. They are in charge of their own behavior beyond this,
|
||||
registering and unregistering with the rest of the system as needed.
|
||||
|
||||
`CoreStartable` also receives a callback for `#onBootCompleted`
|
||||
since these objects may be started before the device has finished booting.
|
||||
|
||||
Each SystemUI service is expected to be a major part of system ui and the
|
||||
goal is to minimize communication between them. So in general they should be
|
||||
relatively silo'd.
|
||||
`CoreStartable` is an ideal place to add new features and functionality
|
||||
that does not belong directly under the umbrella of an existing feature.
|
||||
It is better to define a new `CoreStartable` than to stick unrelated
|
||||
initialization code together in catch-all methods.
|
||||
|
||||
## Dependencies
|
||||
CoreStartables are tied to application startup via Dagger:
|
||||
|
||||
The first SystemUI service that is started should always be Dependency.
|
||||
Dependency provides a static method for getting a hold of dependencies that
|
||||
have a lifecycle that spans sysui. Dependency has code for how to create all
|
||||
dependencies manually added. SystemUIFactory is also capable of
|
||||
adding/replacing these dependencies.
|
||||
```kotlin
|
||||
class FeatureStartable
|
||||
@Inject
|
||||
constructor(
|
||||
/* ... */
|
||||
) : CoreStartable {
|
||||
override fun start() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
Dependencies are lazily initialized, so if a Dependency is never referenced at
|
||||
runtime, it will never be created.
|
||||
@Module
|
||||
abstract class FeatureModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(FeatureStartable::class)
|
||||
abstract fun bind(impl: FeatureStartable): CoreStartable
|
||||
}
|
||||
```
|
||||
|
||||
If an instantiated dependency implements Dumpable it will be included in dumps
|
||||
of sysui (and bug reports), allowing it to include current state information.
|
||||
This is how \*Controllers dump state to bug reports.
|
||||
|
||||
If an instantiated dependency implements ConfigurationChangeReceiver it will
|
||||
receive onConfigurationChange callbacks when the configuration changes.
|
||||
Including `FeatureModule` in the Dagger graph such as this will ensure that
|
||||
`FeatureStartable` gets constructed and that its `#start` method is called.
|
||||
|
||||
## IStatusBar
|
||||
|
||||
@@ -64,12 +90,6 @@ across sysui. Such as when StatusBar calls CommandQueue#recomputeDisableFlags.
|
||||
This is generally used a shortcut to directly trigger CommandQueue rather than
|
||||
calling StatusManager and waiting for the call to come back to IStatusBar.
|
||||
|
||||
## Default SystemUI services list
|
||||
|
||||
### [com.android.systemui.Dependency](/packages/SystemUI/src/com/android/systemui/Dependency.java)
|
||||
|
||||
Provides custom dependency injection.
|
||||
|
||||
### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java)
|
||||
|
||||
Creates/initializes the channels sysui uses when posting notifications.
|
||||
@@ -88,11 +108,11 @@ activity. It provides this cached data to RecentsActivity when it is started.
|
||||
Registers all the callbacks/listeners required to show the Volume dialog when
|
||||
it should be shown.
|
||||
|
||||
### [com.android.systemui.status.phone.StatusBar](/packages/SystemUI/src/com/android/systemui/status/phone/StatusBar.java)
|
||||
### [com.android.systemui.status.phone.CentralSurfaces](/packages/SystemUI/src/com/android/systemui/status/phone/CentralSurfaces.java)
|
||||
|
||||
This shows the UI for the status bar and the notification shade it contains.
|
||||
It also contains a significant amount of other UI that interacts with these
|
||||
surfaces (keyguard, AOD, etc.). StatusBar also contains a notification listener
|
||||
surfaces (keyguard, AOD, etc.). CentralSurfaces also contains a notification listener
|
||||
to receive notification callbacks.
|
||||
|
||||
### [com.android.systemui.usb.StorageNotification](/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java)
|
||||
|
||||
@@ -8,105 +8,110 @@ Go read about Dagger 2.
|
||||
|
||||
- [User's guide](https://google.github.io/dagger/users-guide)
|
||||
|
||||
TODO: Add some links.
|
||||
|
||||
## State of the world
|
||||
|
||||
Dagger 2 has been turned on for SystemUI and a early first pass has been taken
|
||||
for converting everything in [Dependency.java](packages/systemui/src/com/android/systemui/Dependency.java)
|
||||
to use Dagger. Since a lot of SystemUI depends on Dependency, stubs have been added to Dependency
|
||||
to proxy any gets through to the instances provided by dagger, this will allow migration of SystemUI
|
||||
through a number of CLs.
|
||||
Dagger 2 has been turned on for SystemUI and much of
|
||||
[Dependency.java](../src/com/android/systemui/Dependency.java)
|
||||
has been converted to use Dagger. Since a lot of SystemUI depends on Dependency,
|
||||
stubs have been added to Dependency to proxy any gets through to the instances
|
||||
provided by dagger, this will allow migration of SystemUI through a number of CLs.
|
||||
|
||||
### How it works in SystemUI
|
||||
|
||||
There are three high level "scopes" of concern in SystemUI. They all represent
|
||||
singleton scopes, but serve different purposes.
|
||||
|
||||
* `@Singleton` - Instances that are shared everywhere. There isn't a lot of
|
||||
code in this scope. Things like the main thread, and Android Framework
|
||||
provided instances mostly.
|
||||
* `@WMShell` - WindowManager related code in the SystemUI process. We don't
|
||||
want this code relying on the rest of SystemUI, and we don't want the rest
|
||||
of SystemUI peeking into its internals, so it runs in its own Subcomponent.
|
||||
* `@SysUISingleton` - Most of what would be considered "SystemUI". Most feature
|
||||
work by SystemUI developers goes into this scope. Useful interfaces from
|
||||
WindowManager are made available inside this Subcomponent.
|
||||
|
||||
The root dagger graph is created by an instance of `SystemUIInitializer`.
|
||||
See [README.md](../README.md) for more details.
|
||||
For the classes that we're using in Dependency and are switching to dagger, the
|
||||
equivalent dagger version is using `@Singleton` and therefore only has one instance.
|
||||
To have the single instance span all of SystemUI and be easily accessible for
|
||||
other components, there is a single root `@Component` that exists that generates
|
||||
these. The component lives in [SystemUIFactory](packages/systemui/src/com/android/systemui/SystemUIFactory.java)
|
||||
and is called `SystemUIRootComponent`.
|
||||
|
||||
```java
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
|
||||
ContextHolder.class})
|
||||
public interface SystemUIRootComponent {
|
||||
@Singleton
|
||||
Dependency.DependencyInjector createDependency();
|
||||
}
|
||||
```
|
||||
|
||||
The root component is composed of root modules, which in turn provide the global singleton
|
||||
dependencies across all of SystemUI.
|
||||
|
||||
- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
|
||||
variants (like other form factors e.g. Car).
|
||||
|
||||
- `DependencyBinder` creates the mapping from interfaces to implementation classes.
|
||||
|
||||
- `DependencyProvider` provides or binds any remaining depedencies required.
|
||||
|
||||
### Adding injection to a new SystemUI object
|
||||
|
||||
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.
|
||||
these. The component lives in
|
||||
[ReferenceGlobalRootComponent.java](../src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java).
|
||||
|
||||
### Adding a new injectable object
|
||||
|
||||
First tag the constructor with `@Inject`. Also tag it with `@Singleton` if only one
|
||||
instance should be created.
|
||||
First annotate the constructor with `@Inject`. Also annotate it with
|
||||
`@SysUISingleton` if only one instance should be created.
|
||||
|
||||
```java
|
||||
@Singleton
|
||||
public class SomethingController {
|
||||
@Inject
|
||||
public SomethingController(Context context,
|
||||
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
|
||||
// context and mainHandler will be automatically populated.
|
||||
}
|
||||
```kotlin
|
||||
@SysUISingleton
|
||||
class FeatureStartable
|
||||
@Inject
|
||||
constructor(
|
||||
/* ... */
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
If you have an interface class and an implementation class, dagger needs to know
|
||||
how to map it. The simplest way to do this is to add an `@Provides` method to
|
||||
DependencyProvider. The type of the return value tells dagger which dependency it's providing.
|
||||
If you have an interface class and an implementation class, Dagger needs to
|
||||
know how to map it. The simplest way to do this is to add an `@Binds` method
|
||||
in a module. The type of the return value tells dagger which dependency it's
|
||||
providing:
|
||||
|
||||
```java
|
||||
public class DependencyProvider {
|
||||
//...
|
||||
@Singleton
|
||||
@Provides
|
||||
public SomethingController provideSomethingController(Context context,
|
||||
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
|
||||
return new SomethingControllerImpl(context, mainHandler);
|
||||
}
|
||||
```kotlin
|
||||
@Module
|
||||
abstract class FeatureModule {
|
||||
@Binds
|
||||
abstract fun bindsFeature(impl: FeatureImpl): Feature
|
||||
}
|
||||
```
|
||||
|
||||
If you need to access this from Dependency#get, then add an adapter to Dependency
|
||||
that maps to the instance provided by Dagger. The changes should be similar
|
||||
to the following diff.
|
||||
If you have a class that you want to make injectable that has can not
|
||||
be easily constructed by Dagger, write a `@Provides` method for it:
|
||||
|
||||
```java
|
||||
public class Dependency {
|
||||
//...
|
||||
@Inject Lazy<SomethingController> mSomethingController;
|
||||
//...
|
||||
public void start() {
|
||||
//...
|
||||
mProviders.put(SomethingController.class, mSomethingController::get);
|
||||
}
|
||||
```kotlin
|
||||
@Module
|
||||
abstract class FeatureModule {
|
||||
@Module
|
||||
companion object {
|
||||
@Provides
|
||||
fun providesFeature(ctx: Context): Feature {
|
||||
return FeatureImpl.constructFromContext(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Module Organization
|
||||
|
||||
Please define your modules on _at least_ per-package level. If the scope of a
|
||||
package grows to encompass a great number of features, create per-feature
|
||||
modules.
|
||||
|
||||
**Do not create catch-all modules.** Those quickly grow unwieldy and
|
||||
unmaintainable. Any that exist today should be refactored into obsolescence.
|
||||
|
||||
You can then include your module in one of three places:
|
||||
|
||||
1) Within another module that depends on it. Ideally, this creates a clean
|
||||
dependency graph between features and utilities.
|
||||
2) For features that should exist in all versions of SystemUI (AOSP and
|
||||
any variants), include the module in
|
||||
[SystemUIModule.java](../src/com/android/systemui/dagger/SystemUIModule.java).
|
||||
3) For features that should exist only in AOSP, include the module in
|
||||
[ReferenceSystemUIModule.java](../src/com/android/systemui/dagger/ReferenceSystemUIModule.java).
|
||||
Similarly, if you are working on a custom version of SystemUI and have code
|
||||
specific to your version, include it in a module specific to your version.
|
||||
|
||||
### Using injection with Fragments
|
||||
|
||||
Fragments are created as part of the FragmentManager, so they need to be
|
||||
setup so the manager knows how to create them. To do that, add a method
|
||||
to com.android.systemui.fragments.FragmentService$FragmentCreator that
|
||||
returns your fragment class. Thats all thats required, once the method
|
||||
returns your fragment class. That is all that is required, once the method
|
||||
exists, FragmentService will automatically pick it up and use injection
|
||||
whenever your fragment needs to be created.
|
||||
|
||||
@@ -123,48 +128,11 @@ then the FragmentHostManager can do this for you.
|
||||
FragmentHostManager.get(view).create(NavigationBarFragment.class);
|
||||
```
|
||||
|
||||
### Using injection with Views
|
||||
|
||||
DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
|
||||
when that repository is updated.
|
||||
|
||||
*Deprecated:*
|
||||
|
||||
Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
|
||||
into
|
||||
[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
|
||||
|
||||
The following commands should work, substituting in the version that you are looking for:
|
||||
|
||||
````
|
||||
cd prebuilts/tools/common/m2/repository/com/google/dagger/
|
||||
|
||||
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
|
||||
|
||||
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
|
||||
|
||||
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
|
||||
|
||||
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
|
||||
````
|
||||
|
||||
Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
|
||||
|
||||
## TODO List
|
||||
|
||||
- Eliminate usages of Dependency#get
|
||||
- Add links in above TODO
|
||||
- Eliminate usages of Dependency#get: http://b/hotlists/3940788
|
||||
|
||||
Reference in New Issue
Block a user