# Technical Documentation: NeoApplications/Neo-Launcher

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/NeoApplications/Neo-Launcher)

### Overview (README.md)

<h1 align="center">
  <img src="fastlane/metadata/android/en-US/images/icon.png" alt="Neo Launcher's icon" width="192" height="192"/>
  <br>
  Neo Launcher
</h1>

<p align="center"><strong>The modern and feature-rich AOSP-based Launcher!</strong></p>

<p align="center">
  <a href="https://github.com/NeoApplications/Neo-Launcher/releases/latest">
    <img src="https://img.shields.io/github/v/release/NeoApplications/Neo-Launcher?style=flat&labelColor=6644FF&color=928CB7" alt="Latest stable release version"/>
  </a>
  <a href="https://github.com/NeoApplications/Neo-Launcher/releases/">
    <img alt="Github downloads" src="https://img.shields.io/github/downloads/NeoApplications/Neo-Launcher/total.svg?style=flat&labelColor=6644FF&color=928CB7"/>
  </a>
  <a href="https://github.com/NeoApplications/Neo-Launcher/stargazers">
    <img alt="GitHub repo stars" src="https://img.shields.io/github/stars/NeoApplications/Neo-Launcher?style=flat&labelColor=6644FF&color=928CB7"/>
  </a>
  <a href="https://hosted.weblate.org/engage/neolauncher">
    <img src="https://img.shields.io/weblate/progress/neolauncher?style=flat&labelColor=6644FF&color=928CB7" alt="Small translation badge" />
  </a>
  <a href="/LICENSE">
    <img src="https://img.shields.io/github/license/NeoApplications/Neo-Launcher?style=flat&labelColor=6644FF&color=928CB7" alt="Project's License" />
  </a>
</p>

[<img src="get_iod.svg" alt="Get it on IzzyOnDroid" width="24%" align="center">](https://apt.izzysoft.de/fdroid/index/apk/com.saggitt.omega)
[<img src="get_codeberg.svg" alt="Get it on Codeberg" width="24%" align="center">](https://codeberg.org/NeoApplications/Neo-Launcher/releases)
[<img src="get_github.svg" alt="Get it on GitHub" width="24%" align="center">](https://github.com/NeoApplications/Neo-Launcher/releases)

> [!IMPORTANT]
> **This project is not abandoned.** It's just that we (Saul and Antonios) both faced couple of barriers on private life level and technically (on AOSP's side). But the A16-based version is in progress.

[](https://github.com/NeoApplications)

## Community :speech_balloon:

You can join either our [Telegram](https://t.me/neo_launcher) or [Matrix](https://matrix.to/#/#neo-launcher:matrix.org) groups to make suggestions, ask questions, receive news, install test builds, or just chat.

<p align="center">
<a href="https://t.me/neo_launcher"><img src="https://upload.wikimedia.org/wikipedia/commons/8/82/Telegram_logo.svg" alt="Join Telegram Channel" width="11%" align="center"></a>
<a href="https://matrix.to/#/#neo-launcher:matrix.org"><img src="https://docs.cloudron.io/img/element-logo.png" alt="Join Matrix Channel" width="11%" align="center" /></a>
</p>

## Features :abacus:

### **Some of the main features**

- Rich themes support.
- The Dash.
- Custom search engines.
- Gestures support.
- A unique custom-widget.
- Categories or folders in the app drawer.
- Icon shapes and packs support.
- Customize app icons.
- Desktop grid preference.
- Hide apps.
- Vertical Apps Lists.
- Icons' scaling.
- Back up and restore preferences.

There's also a [developer's document](DEVDOC.md), where the maintainer and collaborators communicate
their TODOs and plans.

## Screenshots :framed_picture:

| <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/desktop_icons.png" alt="" width="330" align="center"> | <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/desktop_dash.png" alt="" width="330" align="center"> | <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/settings.png" alt="" width="330" align="center"> |
|:--------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------:|

| <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/profile.png" alt="" width="330" align="center"> | <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/gestures.png" alt="" width="330" align="center"> | <img title="" src="fastlane/metadata/android/en-US/images/phoneScreenshots/dash.png" alt="" width="330" align="center"> |
|:-------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------:|

## Contributions

**If you have knowledge in Java or Kotlin you can work on additional features or bugs, and open pull requests. Make sure the code is formatted before make a pull request.**

**You can also contribute to this project by opening issues for bugs or new features.**

## Support

<a href="https://www.paypal.com/paypalme/saulhdev"><img src="https://img.shields.io/badge/Paypal-Donate-blue?style=for-the-badge&logo=paypal" alt="Donate via PayPal"/></a>

## Translations :left_speech_bubble: [<img align="right" src="https://hosted.weblate.org/widgets/neolauncher/-/287x66-white.png" alt="Translation stats" />](https://hosted.weblate.org/engage/neolauncher/?utm_source=widget)

Contribute your translations to Neo Launcher on [Hosted Weblate](https://hosted.weblate.org/engage/neolauncher/). <br> Adding new languages is always accepted and supported.

[](https://hosted.weblate.org/engage/neolauncher/?utm_source=widget)

## FAQ

Is QuickSwitch available?

No, it's not, and there is no plans to add it.

## Special Thanks :heart:

Thanks to The LawnchairLauncher team for the great work on Lawnchair.

Thanks to <a href="https://github.com/nonaybay">Rafael Venâncio</a> for creating the F-Droid repo.

To [Donno](https://github.com/Donnnno) for the new app icon base.

Thanks to Helena Zhang and Toby Fried for the great [Phosphor icons](https://phosphoricons.com), which we use since 0.9.0.

### Contributors :handshake:

<a href="https://github.com/NeoApplications/Neo-Launcher/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=NeoApplications/Neo-Launcher"  alt="Icons of contributors to Neo Launcher"/>
</a>

## Copylefted Libre License :scroll:

Our work is licensed under the [GPLv3+](/COPYING). \
The original AOSP Launcher3 code is licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).

Copyright © 2025 [Saul Henriquez](https://github.com/saulhdev), [Antonios Hazim](https://github.com/machiav3lli) and [contributors](https://github.com/NeoApplications/Neo-Launcher/graphs/contributors).

---

### Wmshell Src Com Android Wm Shell Docs README (`wmshell/src/com/android/wm/shell/docs/README.md`)

# Window Manager Shell Readme

The following docs present more detail about the implementation of the WMShell library (in no
particular order):

1) [What is the Shell](overview.md)
2) [Integration with SystemUI & Launcher](sysui.md)
3) [Usage of Dagger](dagger.md)
4) [Threading model in the Shell](threading.md)
5) [Making changes in the Shell](changes.md)
6) [Extending the Shell for Products/OEMs](extending.md)
7) [Debugging in the Shell](debugging.md)
8) [Testing in the Shell](testing.md)

Todo

- Per-feature docs
- Feature flagging
- Best practices & patterns

---

### Wmshell Src Com Android Wm Shell Docs Overview (`wmshell/src/com/android/wm/shell/docs/overview.md`)

# What is the WindowManager Shell

---

## Motivation

The primary motivation for the WindowManager Shell (WMShell) library is to effectively scale
WindowManager by making it easy&trade; and safe to create windowing features to fit the needs of
various Android products and form factors.

To achieve this, WindowManager separates the policy of managing windows (WMCore) from the
presentation of surfaces (WMShell) and provides a minimal interface boundary for the two to
communicate.

## Who is using the library?

Currently, the WMShell library is used to drive the windowing experience on handheld
(phones & tablets), TV, Auto, Arc++, and Wear to varying degrees.

## Where does the code live

The core WMShell library code is currently located in
the [frameworks/base/libs/WindowManager/Shell](/libs/WindowManager/Shell)
directory and is included as a part dependency of the host SystemUI apk.

## How do I build the Shell library

The library can be built directly by running:

```shell
m WindowManager-Shell
```

But this is mainly useful for inspecting the contents of the library or verifying it builds. The
various targets can be found in the Shell
library's [Android.bp](/libs/WindowManager/Shell/Android.bp)
file.

Normally, you would build it as a part of the host SystemUI, for example via commandline:

```shell
# Phone SystemUI variant
m SystemUI
adevice update
```

Or preferably, if you are making WMShell/SysUI only changes (no other framework changes), then
building via [go/sysui-studio](http://go/sysui-studio) allows for very quick iteration (one click
build and push of SysUI in < 30s).

If you are making framework changes and are using `aidegen` to set up your platform IDE, make sure
to include the appropriate directories to build, for example:

```shell
# frameworks/base will include base/libs/WindowManager/Shell and base/packages/SystemUI
aidegen frameworks/base \
    vendor/<oem>/packages/SystemUI \
    ...
```

## Other useful links

- [go/o-o-summit-20](go/o-o-summit-20) (Video presentations from the WM team)
- [go/o-o-summit-21](go/o-o-summit-21)

---

### Libs Systemui Utils Kairos Docs Flow To Kairos Cheatsheet (`libs_systemui/utils/kairos/docs/flow-to-kairos-cheatsheet.md`)

# From Flows to Kairos

## Key differences

* Kairos evaluates all events (`TFlow` emissions + observers) in a transaction.

* Kairos splits `Flow` APIs into two distinct types: `TFlow` and `TState`

    * `TFlow` is roughly equivalent to `SharedFlow` w/ a replay cache that
      exists for the duration of the current Kairos transaction and shared with
      `SharingStarted.WhileSubscribed()`

    * `TState` is roughly equivalent to `StateFlow` shared with
      `SharingStarted.Eagerly`, but the current value can only be queried within
      a Kairos transaction, and the value is only updated at the end of the
      transaction

* Kairos further divides `Flow` APIs based on how they internally use state:

  * **FrpTransactionScope:** APIs that internally query some state need to be
    performed within an Kairos transaction

    * this scope is available from the other scopes, and from most lambdas
      passed to other Kairos APIs

  * **FrpStateScope:** APIs that internally accumulate state in reaction to
    events need to be performed within an FRP State scope (akin to a
    `CoroutineScope`)

    * this scope is a side-effect-free subset of FrpBuildScope, and so can be
      used wherever you have an FrpBuildScope

  * **FrpBuildScope:** APIs that perform external side-effects (`Flow.collect`)
    need to be performed within an FRP Build scope (akin to a `CoroutineScope`)

    * this scope is available from `FrpNetwork.activateSpec { … }`

  * All other APIs can be used anywhere

## emptyFlow()

Use `emptyTFlow`

``` kotlin
// this TFlow emits nothing
val noEvents: TFlow<Int> = emptyTFlow
```

## map { … }

Use `TFlow.map` / `TState.map`

``` kotlin
val anInt: TState<Int> = …
val squared: TState<Int> = anInt.map { it * it }
val messages: TFlow<String> = …
val messageLengths: TFlow<Int> = messages.map { it.size }
```

## filter { … } / mapNotNull { … }

### I have a TFlow

Use `TFlow.filter` / `TFlow.mapNotNull`

``` kotlin
val messages: TFlow<String> = …
val nonEmpty: TFlow<String> = messages.filter { it.isNotEmpty() }
```

### I have a TState

Convert the `TState` to `TFlow` using `TState.stateChanges`, then use
`TFlow.filter` / `TFlow.mapNotNull`

If you need to convert back to `TState`, use `TFlow.hold(initialValue)` on the
result.

``` kotlin
tState.stateChanges.filter { … }.hold(initialValue)
```

Note that `TFlow.hold` is only available within an `FrpStateScope` in order to
track the lifetime of the state accumulation.

## combine(...) { … }

### I have TStates

Use `combine(TStates)`

``` kotlin
val someInt: TState<Int> = …
val someString: TState<String> = …
val model: TState<MyModel> = combine(someInt, someString) { i, s -> MyModel(i, s) }
```

### I have TFlows

Convert the TFlows to TStates using `TFlow.hold(initialValue)`, then use
`combine(TStates)`

If you want the behavior of Flow.combine where nothing is emitted until each
TFlow has emitted at least once, you can use filter:

``` kotlin
// null used as an example, can use a different sentinel if needed
combine(tFlowA.hold(null), tFlowB.hold(null)) { a, b ->
        a?.let { b?.let { … } }
    }
    .filterNotNull()
```

Note that `TFlow.hold` is only available within an `FrpStateScope` in order to
track the lifetime of the state accumulation.

#### Explanation

`Flow.combine` always tracks the last-emitted value of each `Flow` it's
combining. This is a form of state-accumulation; internally, it collects from
each `Flow`, tracks the latest-emitted value, and when anything changes, it
re-runs the lambda to combine the latest values.

An effect of this is that `Flow.combine` doesn't emit until each combined `Flow`
has emitted at least once. This often bites developers. As a workaround,
developers generally append `.onStart { emit(initialValue) }` to the `Flows`
that don't immediately emit.

Kairos avoids this gotcha by forcing usage of `TState` for `combine`, thus
ensuring that there is always a current value to be combined for each input.

## collect { … }

Use `observe { … }`

``` kotlin
val job: Job = tFlow.observe { println("observed: $it") }
```

Note that `observe` is only available within an `FrpBuildScope` in order to
track the lifetime of the observer. `FrpBuildScope` can only come from a
top-level `FrpNetwork.transaction { … }`, or a sub-scope created by using a
`-Latest` operator.

## sample(flow) { … }

### I want to sample a TState

Use `TState.sample()` to get the current value of a `TState`. This can be
invoked anywhere you have access to an `FrpTransactionScope`.

``` kotlin
// the lambda passed to map receives an FrpTransactionScope, so it can invoke
// sample
tFlow.map { tState.sample() }
```

#### Explanation

To keep all state-reads consistent, the current value of a TState can only be
queried within a Kairos transaction, modeled with `FrpTransactionScope`. Note
that both `FrpStateScope` and `FrpBuildScope` extend `FrpTransactionScope`.

### I want to sample a TFlow

Convert to a `TState` by using `TFlow.hold(initialValue)`, then use `sample`.

Note that `hold` is only available within an `FrpStateScope` in order to track
the lifetime of the state accumulation.

## stateIn(scope, sharingStarted, initialValue)

Use `TFlow.hold(initialValue)`. There is no need to supply a sharingStarted
argument; all states are accumulated eagerly.

``` kotlin
val ints: TFlow<Int> = …
val lastSeenInt: TState<Int> = ints.hold(initialValue = 0)
```

Note that `hold` is only available within an `FrpStateScope` in order to track
the lifetime of the state accumulation (akin to the scope parameter of
`Flow.stateIn`). `FrpStateScope` can only come from a top-level
`FrpNetwork.transaction { … }`, or a sub-scope created by using a `-Latest`
operator. Also note that `FrpBuildScope` extends `FrpStateScope`.

## distinctUntilChanged()

Use `distinctUntilChanged` like normal. This is only available for `TFlow`;
`TStates` are already `distinctUntilChanged`.

## merge(...)

### I have TFlows

Use `merge(TFlows) { … }`. The lambda argument is used to disambiguate multiple
simultaneous emissions within the same transaction.

#### Explanation

Under Kairos's rules, a `TFlow` may only emit up to once per transaction. This
means that if we are merging two or more `TFlows` that are emitting at the same
time (within the same transaction), the resulting merged `TFlow` must emit a
single value. The lambda argument allows the developer to decide what to do in
this case.

### I have TStates

If `combine` doesn't satisfy your needs, you can use `TState.stateChanges` to
convert to a `TFlow`, and then `merge`.

## conflatedCallbackFlow { … }

Use `tFlow { … }`.

As a shortcut, if you already have a `conflatedCallbackFlow { … }`, you can
convert it to a TFlow via `Flow.toTFlow()`.

Note that `tFlow` is only available within an `FrpBuildScope` in order to track
the lifetime of the input registration.

## first()

### I have a TState

Use `TState.sample`.

### I have a TFlow

Use `TFlow.nextOnly`, which works exactly like `Flow.first` but instead of
suspending it returns a `TFlow` that emits once.

The naming is intentionally different because `first` implies that it is the
first-ever value emitted from the `Flow` (which makes sense for cold `Flows`),
whereas `nextOnly` indicates that only the next value relative to the current
transaction (the one `nextOnly` is being invoked in) will be emitted.

Note that `nextOnly` is only available within an `FrpStateScope` in order to
track the lifetime of the state accumulation.

## flatMapLatest { … }

If you want to use -Latest to cancel old side-effects, similar to what the Flow
-Latest operators offer for coroutines, see `mapLatest`.

### I have a TState…

#### …and want to switch TStates

Use `TState.flatMap`

``` kotlin
val flattened = tState.flatMap { a -> getTState(a) }
```

#### …and want to switch TFlows

Use `TState<TFlow<T>>.switch()`

``` kotlin
val tFlow = tState.map { a -> getTFlow(a) }.switch()
```

### I have a TFlow…

#### …and want to switch TFlows

Use `hold` to convert to a `TState<TFlow<T>>`, then use `switch` to switch to
the latest `TFlow`.

``` kotlin
val tFlow = tFlowOfFlows.hold(emptyTFlow).switch()
```

#### …and want to switch TStates

Use `hold` to convert to a `TState<TState<T>>`, then use `flatMap` to switch to
the latest `TState`.

``` kotlin
val tState = tFlowOfStates.hold(tStateOf(initialValue)).flatMap { it }
```

## mapLatest { … } / collectLatest { … }

`FrpStateScope` and `FrpBuildScope` both provide `-Latest` operators that
automatically cancel old work when new values are emitted.

``` kotlin
val currentModel: TState<SomeModel> = …
val mapped: TState<...> = currentModel.mapLatestBuild { model ->
    effect { "new model in the house: $model" }
    model.someState.observe { "someState: $it" }
    val someData: TState<SomeInfo> =
        getBroadcasts(model.uri)
            .map { extractInfo(it) }
            .hold(initialInfo)
    …
}
```

## flowOf(...)

### I want a TState

Use `tStateOf(initialValue)`.

### I want a TFlow

Use `now.map { initialValue }`

Note that `now` is only available within an `FrpTransactionScope`.

#### Explanation

`TFlows` are not cold, and so there isn't a notion of "emit this value once
there is a collector" like there is for `Flow`. The closest analog would be
`TState`, since the initial value is retained indefinitely until there is an
observer. However, it is often useful to immediately emit a value within the
current transaction, usually when using a `flatMap` or `switch`. In these cases,
using `now` explicitly models that the emission will occur within the current
transaction.

``` kotlin
fun <T> FrpTransactionScope.tFlowOf(value: T): TFlow<T> = now.map { value }
```

## MutableStateFlow / MutableSharedFlow

Use `MutableTState(frpNetwork, initialValue)` and `MutableTFlow(frpNetwork)`.

---

### Libs Systemui Utils Kairos Docs Semantics (`libs_systemui/utils/kairos/docs/semantics.md`)

# FRP Semantics

`Kairos`'s pure API is based off of the following denotational semantics
([wikipedia](https://en.wikipedia.org/wiki/Denotational_semantics)).

The semantics model `Kairos` types as time-varying values; by making `Time` a
first-class value, we can define a referentially-transparent API that allows us
to reason about the behavior of the pure `Kairos` combinators. This is
implementation-agnostic; we can compare the behavior of any implementation with
expected behavior denoted by these semantics to identify bugs.

The semantics are written in pseudo-Kotlin; places where we are deviating from
real Kotlin are noted with comments.

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Wmshell Src Com Android Wm Shell Docs Changes (`wmshell/src/com/android/wm/shell/docs/changes.md`)

# Making changes in the Shell

---

## Code reviews

In addition to the individual reviewers who are most familiar with the changes you are making,
please also add [wm-code-reviewers@google.com](http://g/wm-code-reviewers) to keep other WM folks
in the loop.

## Adding new code

### Internal Shell utility classes

If the new component is used only within the WMShell library, then there are no special
considerations, go ahead and add it (in the `com.android.wm.shell.common` package for example)
and make sure the appropriate [unit tests](testing.md) are added.

### Internal Shell components

If the new component is to be used by other components/features within the Shell library, then
you can create an appropriate package for this component to add your new code. The current
pattern is to have a single `<Component name>Controller` that handles the initialization of the
component.

As mentioned in the [Dagger usage](dagger.md) docs, you need to determine whether it should go into:

- `WMShellBaseModule` for components that other base & product components will depend on
- or `WMShellModule`, `TvWmShellModule`, etc. for product specific components that no base
  components depend on

### SysUI accessible components

In addition to doing the above, you will also need to provide an interface for calling to SysUI
from the Shell and vice versa. The current pattern is to have a parallel `Optional<Component name>`
interface that the `<Component name>Controller` implements and handles on the main Shell thread
(see [SysUI/Shell threading](threading.md)).

In addition, because components accessible to SysUI injection are explicitly listed, you'll have to
add an appropriate method in `WMComponent` to get the interface and update the `Builder` in
`SysUIComponent` to take the interface so it can be injected in SysUI code. The binding between
the two is done in `SystemUIFactory#init()` which will need to be updated as well.

Specifically, to support calling into a controller from an external process (like Launcher):

- Create an implementation of the external interface within the controller
- Have all incoming calls post to the main shell thread (inject @ShellMainThread Executor into the
  controller if needed)
- Note that callbacks into SysUI should take an associated executor to call back on

### Launcher accessible components

Because Launcher is not a part of SystemUI and is a separate process, exposing controllers to
Launcher requires a new AIDL interface to be created and implemented by the controller. The
implementation of the stub interface in the controller otherwise behaves similar to the interface
to SysUI where it posts the work to the main Shell thread.

Specifically, to support calling into a controller from an external process (like Launcher):

- Create an implementation of the interface binder's `Stub` class within the controller, have it
  extend `ExternalInterfaceBinder` and implement `invalidate()` to ensure it doesn't hold long
  references to the outer controller
- Make the controller implement `RemoteCallable<T>`, and have all incoming calls use one of
  the `executeRemoteCallWithTaskPermission()` calls to verify the caller's identity and ensure the
  call happens on the main shell thread and not the binder thread
- Inject `ShellController` and add the instance of the implementation as external interface
- In Launcher, update `TouchInteractionService` to pass the interface to `SystemUIProxy`, and then
  call the SystemUIProxy method as needed in that code

### Component initialization

To initialize the component:

- On the Shell side, you potentially need to do two things to initialize the component:
    - Inject `ShellInit` into your component and add an init callback
    - Ensure that your component is a part of the dagger dependency graph, either by:
        - Making this component a dependency of an existing component already exposed to SystemUI
        - Explicitly add this component to the WMShellBaseModule @ShellCreateTrigger provider or
          the @ShellCreateTriggerOverride provider for your product module to expose it explicitly
          if it is a completely independent component
- On the SysUI side, update `WMShell` to setup any bindings for the component that depend on
  SysUI code

To verify that your component is being initialized at startup, you can enable the `WM_SHELL_INIT`
protolog group and restart the SysUI process:

```shell
adb shell wm logging enable-text WM_SHELL_INIT
adb shell kill `pid com.android.systemui`
adb logcat *:S WindowManagerShell
```

### General Do's & Dont's

Do:

- Add unit tests for all new components
- Keep controllers simple and break them down as needed
- Any SysUI callbacks should also take an associated executor to run the callback on

Don't:

- **Don't** do initialization in the constructor, only do initialization in the init callbacks.
  Otherwise it complicates the building of the dependency graph.
- **Don't** create dependencies from base-module components on specific features (the base module
  is intended for use with all products)
    - Try adding a mechanism to register and listen for changes from the base module component
      instead
- **Don't** add blocking synchronous calls in the SysUI interface between Shell & SysUI
    - Try adding a push-mechanism to share data, or an async callback to request data

### Exposing shared code for use in Launcher

Launcher doesn't currently build against the Shell library, but needs to have access to some shared
AIDL interfaces and constants. Currently, all AIDL files, and classes under the
`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
Launcher uses.

If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the
`wm_shell_util-sources` filegroup.

---

### Wmshell Src Com Android Wm Shell Docs Dagger (`wmshell/src/com/android/wm/shell/docs/dagger.md`)

# Usage of Dagger in the Shell library

---

## Dependencies

Dagger is not required to use the Shell library, but it has a lot of obvious benefits:

- Not having to worry about how to instantiate all the dependencies of a class, especially as
  dependencies evolve (ie. product controller depends on base controller)
- Can create boundaries within the same app to encourage better code modularity

As such, the Shell also tries to provide some reasonable out-of-the-box modules for use with Dagger.

## Modules

All the Dagger related code in the Shell can be found in the `com.android.wm.shell.dagger` package,
this is intentional as it keeps the "magic" in a single location. The explicit nature of how
components in the shell are provided is as a result a bit more verbose, but it makes it easy for
developers to jump into a few select files and understand how different components are provided
(especially as products override components).

The module dependency tree looks a bit like:

- [WMShellConcurrencyModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java)
  (provides threading-related components)
    - [WMShellBaseModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java)
      (provides components that are likely common to all products, ie. DisplayController,
      Transactions, etc.)
        - [WMShellModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java)
          (phone/tablet specific components only)
        - [TvPipModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java)
          (PIP specific components for TV)
            - [TvWMShellModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java)
              (TV specific components only)
    - etc.

Ideally features could be abstracted out into their own modules and included as needed by each
product.

## Overriding base components

In some rare cases, there are base components that can change behavior depending on which
product it runs on. If there are hooks that can be added to the component, that is the
preferable approach.

The alternative is to use
the [@DynamicOverride](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java)
annotation to allow the product module to provide an implementation that the base module can
reference. This is most useful if the existence of the entire component is controlled by the
product and the override implementation is optional (there is a default implementation). More
details can be found in the class's javadoc.

---

### Wmshell Src Com Android Wm Shell Docs Debugging (`wmshell/src/com/android/wm/shell/docs/debugging.md`)

# Debugging in the Shell

---

## Logging & ProtoLogs

The interactions in the Shell can be pretty complicated, so having good logging is crucial to
debugging problems that arise (especially in dogfood). The Shell uses the same efficient Protolog
mechanism as WM Core, which can be enabled at runtime on debug devices.

**TLDR**&nbsp; Don’t use Logs or Slogs except for error cases, Protologs are much more flexible,
easy to add and easy to use

### Adding a new ProtoLog

Update `ShellProtoLogGroup` to include a new log group (ie. NEW_FEATURE) for the content you want to
log. ProtoLog log calls mirror Log.v/d/e(), and take a format message and arguments:

```java
ProtoLog.v(NEW_FEATURE, "Test log w/ params: %d %s", 1, “a”)
```

This code itself will not compile by itself, but the `protologtool` will preprocess the file when
building to check the log state (is enabled) before printing the print format style log.

**Notes**

- ProtoLogs are only fully supported from soong builds (ie. via make/mp). In SysUI-studio it falls
  back to log via Logcat
- Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with
  traces in Winscope)

### Kotlin

Protolog tool does not yet have support for Kotlin code (
see [b/168581922](https://b.corp.google.com/issues/168581922)).
For logging in Kotlin, use
the [KtProtoLog](/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt)
class which has a similar API to the Java ProtoLog class.

### Enabling ProtoLog command line logging

Run these commands to enable protologs (in logcat) for WM
Core ([list of all core tags](/core/java/com/android/internal/protolog/ProtoLogGroup.java)):

```shell
adb shell wm logging enable-text TAG
adb shell wm logging disable-text TAG
```

And these commands to enable protologs (in logcat) for WM
Shell ([list of all shell tags](/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java)):

```shell
adb shell dumpsys activity service SystemUIService WMShell protolog enable-text TAG
adb shell dumpsys activity service SystemUIService WMShell protolog enable-text TAG
```

## Winscope Tracing

The Winscope tool is extremely useful in determining what is happening on-screen in both
WindowManager and SurfaceFlinger. Follow [go/winscope](http://go/winscope-help) to learn how to
use the tool. This trace will contain all the information about the windows/activities/surfaces on
screen.

## WindowManager/SurfaceFlinger hierarchy dump

A quick way to view the WindowManager hierarchy without a winscope trace is via the wm dumps:

```shell
adb shell dumpsys activity containers
```

Likewise, the SurfaceFlinger hierarchy can be dumped for inspection by running:

```shell
adb shell dumpsys SurfaceFlinger
# Search output for "Layer Hierarchy"
```

## Tracing global SurfaceControl transaction updates

While Winscope traces are very useful, it sometimes doesn't give you enough information about which
part of the code is initiating the transaction updates. In such cases, it can be helpful to get
stack traces when specific surface transaction calls are made, which is possible by enabling the
following system properties for example:

```shell
# Enabling
adb shell setprop persist.wm.debug.sc.tx.log_match_call setAlpha  # matches the name of the SurfaceControlTransaction method
adb shell setprop persist.wm.debug.sc.tx.log_match_name com.android.systemui # matches the name of the surface
adb reboot
adb logcat -s "SurfaceControlRegistry"

# Disabling logging
adb shell setprop persist.wm.debug.sc.tx.log_match_call \"\"
adb shell setprop persist.wm.debug.sc.tx.log_match_name \"\"
adb reboot
```

It is not necessary to set both `log_match_call` and `log_match_name`, but note logs can be quite
noisy if unfiltered.

## Tracing activity starts in the app process

It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
get this trace:

```shell
# Enabling
adb shell setprop persist.wm.debug.start_activity true
adb reboot
adb logcat -s "Instrumentation"

# Disabling
adb shell setprop persist.wm.debug.start_activity \"\"
adb reboot
```

## Dumps

Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
part of dumping the SystemUI service. Dumping the Shell specific data can be done by specifying the
WMShell SysUI service:

```shell
adb shell dumpsys activity service SystemUIService WMShell
```

If information should be added to the dump, either:

- Update `WMShell` if you are dumping SysUI state
- Inject `ShellCommandHandler` into your Shell class, and add a dump callback

## Shell commands

It can be useful to add additional shell commands to drive and test specific interactions.

To add a new command for your feature, inject a `ShellCommandHandler` into your class and add a
shell command handler in your controller.

```shell
# List all available commands
adb shell dumpsys activity service SystemUIService WMShell help

# Run a specific command
adb shell dumpsys activity service SystemUIService WMShell <cmd> <args> ...
```

## Debugging in Android Studio

If you are using the [go/sysui-studio](http://go/sysui-studio) project, then you can debug Shell
code directly from Android Studio like any other app.

---

### Wmshell Src Com Android Wm Shell Docs Extending (`wmshell/src/com/android/wm/shell/docs/extending.md`)

# Extending the Shell for Products/OEMs

---

## General Do's & Dont's

Do:

- &nbsp;

Don't

- **Don't** override classes provided by WMShellBaseModule, it makes it difficult to make
  simple changes to the Shell library base modules which are shared by all products
    - If possible add mechanisms to modify the base class behavior

---

### Wmshell Src Com Android Wm Shell Docs Sysui (`wmshell/src/com/android/wm/shell/docs/sysui.md`)

# Shell & SystemUI

---

## Setup

The SystemUI of various products depend on and build against the WM Shell library. To ensure
that we don't inadvertently build dependencies between the Shell library and one particular
product (ie. handheld SysUI), we deliberately separate the initialization of the WM Shell
component from the SysUI component when set up through Dagger.

**TLDR**&nbsp; Initialize everything as needed in the WM component scope and export only well
defined interfaces to SysUI.

## Initialization

There are more details in the Dagger docs, but the general overview of the SysUI/Shell
initialization flow is such:

1) SysUI Global scope is initialize (see `GlobalModule` and its included modules)
2) WM Shell scope is initialized, for example
    1) On phones: `WMComponent` includes `WMShellModule` which includes `WMShellBaseModule`
       (common to all SysUI)
    2) On TVs: `TvWMComponent` includes `TvWMShellModule` which includes `WMShellBaseModule`
    3) etc.
3) SysUI explicitly passes interfaces provided from the `WMComponent` to `SysUIComponent` via
   the `SysUIComponent#Builder`, then builds the SysUI scoped components
4) `WMShell` is the SystemUI “service” (in the SysUI scope) that initializes with the app after the
   SystemUI part of the dependency graph has been created. It contains the binding code between the
   interfaces provided by the Shell and the rest of SystemUI.
5) SysUI can inject the interfaces into its own components

More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).

## Interfaces from SysUI to Shell components

Within the same process, the WM Shell components can be running on a different thread than the main
SysUI thread (disabled on certain products). This introduces challenges where we have to be
careful about how SysUI calls into the Shell and vice versa.

As a result, we enforce explicit interfaces between SysUI and Shell components, and the
implementations of the interfaces on each side need to post to the right thread before it calls
into other code.

For example, you might have:

1) (Shell) ShellFeature interface to be used from SysUI
2) (Shell) ShellFeatureController handles logic, implements ShellFeature interface and posts to
   main Shell thread
3) SysUI application init injects Optional<ShellFeature> as an interface to SysUI to call
4) (SysUI) SysUIFeature depends on ShellFeature interface
5) (SysUI) SysUIFeature injects Optional<ShellFeature>, and sets up a callback for the Shell to
   call, and the callback posts to the main SysUI thread

Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently
necessary to maintain proper threading and logic isolation.

## Listening for Configuration changes & other SysUI events

Aside from direct calls into Shell controllers for exposed features, the Shell also receives
common event callbacks from SysUI via the `ShellController`. This includes things like:

- Configuration changes
- Keyguard events
- Shell init
- Shell dumps & commands

For other events which are specific to the Shell feature, then you can add callback methods on
the Shell feature interface. Any such calls should <u>**never**</u> be synchronous calls as
they will need to post to the Shell main thread to run.

## Shell commands & Dumps

Since the Shell library is a part of the SysUI process, it relies on SysUI to trigger commands
on individual Shell components, or to dump individual shell components.

```shell
# Dump everything
adb shell dumpsys activity service SystemUIService WMShell

# Run a specific command
adb shell dumpsys activity service SystemUIService WMShell help
adb shell dumpsys activity service SystemUIService WMShell <cmd> <args> ...
```

More detail can be found in [Debugging in the Shell](debugging.md) section.

---

### Wmshell Src Com Android Wm Shell Docs Testing (`wmshell/src/com/android/wm/shell/docs/testing.md`)

# Testing

---

## Unit tests

New WM Shell unit tests can be added to the
[Shell/tests/unittest](/libs/WindowManager/Shell/tests/unittest) directory, and can
be run via command line using `atest`:

```shell
atest WMShellUnitTests
```

If you use the SysUI Studio project, you can run and debug tests directly in the source files
(click on the little arrows next to the test class or test method).

These unit tests are run as a part of WindowManager presubmit, and the dashboards for these unit
tests tests can be found at [go/wm-tests](http://go/wm-tests).

This [GCL file](http://go/wm-unit-tests-gcl) configures the tests being run on the server.

## Flicker tests

Flicker tests are tests that perform actions and make assertions on the state in Window Manager
and SurfaceFlinger traces captured during the run.

New WM Shell Flicker tests can be added to the
[Shell/tests/flicker](/libs/WindowManager/Shell/tests/flicker) directory, and can be run via command
line using `atest`:

```shell
# Bubbles
atest WMShellFlickerTestsBubbles

# PIP
atest WMShellFlickerTestsPip1
atest WMShellFlickerTestsPip2
atest WMShellFlickerTestsPip3
atest WMShellFlickerTestsPipApps
atest WMShellFlickerTestsPipAppsCSuite

# Splitscreen
atest WMShellFlickerTestsSplitScreenGroup1
atest WMShellFlickerTestsSplitScreenGroup2

# Other
atest WMShellFlickerTestsOther
```

**Note**: Currently Flicker tests can only be run from the commandline and not via SysUI Studio

A subset of the flicker tests tests are run as a part of WindowManager presubmit, and the
dashboards for these tests tests can be found at [go/wm-tests-flicker](http://go/wm-tests-flicker).

## CTS tests

Some windowing features also have CTS tests to ensure consistent behavior across OEMs. For example:

- Picture-in-Picture:
  [PinnedStackTests](cts/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java)
- etc.

These can also be run via commandline only using `atest`, for example:

```shell
atest PinnedStackTests
```

---

### Wmshell Src Com Android Wm Shell Docs Threading (`wmshell/src/com/android/wm/shell/docs/threading.md`)

# Threading

---

## Boundaries

```text
                                    Thread boundary
                                          |
                                WM Shell  | SystemUI
                                          |
                                          |
FeatureController <-> FeatureInterface <--|--> WMShell <-> SysUI
       |          (^post to shell thread) |            (^post to main thread)
      ...                                 |
       |                                  |
 OtherControllers                         |
```

## Threads

We currently have multiple threads in use in the Shell library depending on the configuration by
the product.

- SysUI main thread (standard main thread)
- `ShellMainThread` (only used if the resource `config_enableShellMainThread` is set true
  (ie. phones))
    - This falls back to the SysUI main thread otherwise
    - **Note**:
        - This thread runs with `THREAD_PRIORITY_DISPLAY` priority since so many windowing-critical
          components depend on it
        - This is also the UI thread for almost all UI created by the Shell
        - The Shell main thread Handler (and the Executor that wraps it) is async, so
          messages/runnables used via this Handler are handled immediately if there is no sync
          messages prior to it in the queue.
- `ShellBackgroundThread` (for longer running tasks where we don't want to block the shell main
  thread)
    - This is always another thread even if config_enableShellMainThread is not set true
    - **Note**:
        - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
  animations could be offloaded here)
- `ShellSplashScreenThread` (only for use with splashscreens)

## Dagger setup

The threading-related components are provided by
the [WMShellConcurrencyModule](/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java),
for example, the Executors and Handlers for the various threads that are used. You can request
an executor of the necessary type by using the appropriate annotation for each of the threads (ie.
`@ShellMainThread Executor`) when injecting into your Shell component.

To get the SysUI main thread, you can use the `@Main` annotation.

## Best practices

### Components

- Don't do initialization in the Shell component constructors
    - If the host SysUI is not careful, it may construct the WMComponent dependencies on the main
      thread, and this reduces the likelihood that components will intiailize on the wrong thread
      in such cases
- Be careful of using CountDownLatch and other blocking synchronization mechanisms in Shell code
    - If the Shell main thread is not a separate thread, this will cause a deadlock
- Callbacks, Observers, Listeners to any non-shell component should post onto main Shell thread
    - This includes Binder calls, SysUI calls, BroadcastReceivers, etc. Basically any API that
      takes a runnable should either be registered with the right Executor/Handler or posted to
      the main Shell thread manually
- Since everything in the Shell runs on the main Shell thread, you do **not** need to explicitly
  `synchronize` your code (unless you are trying to prevent reentrantcy, but that can also be
  done in other ways)

### Handlers/Executors

- You generally **never** need to create Handlers explicitly, instead inject `@ShellMainThread
  ShellExecutor` instead
    - This is a common pattern to defer logic in UI code, but the Handler created wraps the Looper
      that is currently running, which can be wrong (see above for initialization vs construction)
- That said, sometimes Handlers are necessary because Framework API only takes Handlers or you
  want to dedupe multiple messages
    - In such cases inject `@ShellMainThread Handler` or use view.getHandler() which should be OK
      assuming that the view root was initialized on the main Shell thread
- <u>**Never</u> use Looper.getMainLooper()**
    - It's likely going to be wrong, you can inject `@Main ShellExecutor` to get the SysUI main
      thread

### Testing

- You can use a `TestShellExecutor` to control the processing of messages

---

### Wmshell Src Com Android Wm Shell Docs Patterns TEMPLATE (`wmshell/src/com/android/wm/shell/docs/patterns/TEMPLATE.md`)

# Pattern (one line description)

### What this pattern solves

Give detailed information about the problem that this pattern addresses, include when it
should/should not be used.

### How it works

Give a high level overview of how this pattern works technically with sufficient links for the
relevant dependencies for folks to read more.

### Code examples

Explain how this pattern is used in code.

File.kt:

```kotlin
fun someFunction() {
    // Use this code
}
```

### Relevant links

Add relevant links to other files that implement this pattern, design docs, or other important
info.

Link 1: [More info](some_example_link) \
Link 2: [More info](some_example_link)

---

--- METRICS ---
- Files Extracted: 13
- Estimated Token Budget: ~11574 tokens
- Canonical Reference: https://codewiki.google/github.com/NeoApplications/Neo-Launcher
