---
outline: deep
---

# Icon Packs

Kvaesitso has built-in support for the ADW launcher icon pack format (the format that is nowadays
used by virtually all icon packs and supported by all major custom launchers).

## Get started

### AndroidManifest.xml

In order to be recognized as an icon pack, your app must declare an activity with at least one of
the following intent filters in your `AndroidManifest.xml`:

```xml

<activity android:name="[...]" android:exported="true">
    <intent-filter>
        <action android:name="org.adw.ActivityStarter.THEMES" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.novalauncher.THEME" />
    </intent-filter>
    <intent-filter>
        <!-- Themed icons; only add this if your icon pack supports themed icons -->
        <action android:name="app.lawnchair.icons.THEMED_ICON" />
    </intent-filter>
</activity>
```

### appfilter.xml

Icons are mapped to apps using a file called `appfilter.xml`. This file must be located in one of
these locations:

- `res/xml/appfilter.xml` (recommended)
- `res/raw/appfilter.xml`
- `assets/appfilter.xml`

The file has the following structure:

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- icons are added here -->
</resources>
```

### drawable.xml

`drawable.xml` is a file that lists all icons that are included in the icon pack. This file is
required for manual icon picking.
The file lives in `res/xml/drawable.xml`, and looks like this:

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item drawable="messages" />
    <category title="System" />
    <item drawable="settings" />
    <item drawable="settings_alt_1" />
</resources>
```

Every icon that should be available for manual icon picking must be listed in this file. You can
optionally group icons by adding `<category>` elements, but these categories aren't used by
Kvaesitso.

## Static icons

Icons are added to the `appfilter.xml` file using the `<item>` tag:

```xml

<item component="ComponentInfo{com.android.deskclock/com.android.deskclock.DeskClockTabActivity}"
    drawable="clock" />
```

- `component` is the component name of the target activity. The short form is also supported if the
  class' package name starts with the app's package
  name: `ComponentInfo{com.android.deskclock/.DeskClockTabActivity}`
- `drawable` is the name of the icon drawable which must be located in the `res/drawable/` folder
  (or any of its variants, e.g. `res/drawable-hdpi/`)

## Autogenerated icons

The launcher can generate fallback icons for apps that don't have an icon in the icon pack.
Fallback icons consist of four parts:

```xml

<resources>
    <scale factor="1.0" />
    <iconback img="iconback" />
    <iconupon img="iconupon" />
    <iconmask img="iconmask" />
</resources>
```

- `scale`: the original icon is scaled by this `factor`
- `iconback`: this is drawn behind the original icon
- `iconupon`: this is drawn on top of the original icon
- `iconmask`: this is used to mask the original icon

You can provide multiple variants of each part by adding a number to the `img` attribute:

```xml

<iconback img1="iconback" img2="iconback2" />
```

The launcher will then randomly pick one of the variants for each icon.

## Dynamic icons

### Calendar icons

Kvaesitso supports Nova
launcher's [dynamic calendar icon standard](https://github.com/teslacoil/Example_NovaTheme/blob/master/DynamicCalendarIconAPI.md):

To your `appfilter.xml` file, add the following:

```xml

<calendar component="ComponentInfo{com.google.android.calendar/com.android.calendar.LaunchActivity}"
    prefix="calendar_" />
```

`prefix` is the prefix of the icon drawables. You need to provide one drawable for each day of the
month (`calendar_1`, `calendar_2`, etc. up to `calendar_31`).

> [!NOTE]
> Single digit days must not be zero-padded (e.g. `calendar_1` is correct but `calendar_01` is not).
> Make sure that all 31 drawables are present, or the launcher will reject the icon.

### Clock icons

Dynamic clock icons are supported as well but they work a bit differently than calendar icons.
A dynamic clock icon needs at least two entries in your `appfilter.xml` file:

```xml

<resources>
    <item
        component="ComponentInfo{com.google.android.deskclock/com.android.deskclock.DeskClockTabActivity}"
        drawable="clock" />
    <dynamic-clock defaultHour="10" defaultMinute="10" defaultSecond="30" drawable="clock"
        hourLayerIndex="0" minuteLayerIndex="1" secondLayerIndex="2" />
</resources>
```

- The first entry is a normal icon entry.
- The second entry tells the launcher that that specific drawable is a dynamic clock icon. That way
  you can reuse the same clock icon config for multiple apps.

The icon itself must be either a `LayerDrawable`, or an `AdaptiveIconDrawable` with
a `LayerDrawable`
as its foreground layer.

> [!NOTE]
> Some launchers only support `AdaptiveIconDrawable`s, so you should prefer that if possible.

The entry in the `appfilter.xml` file tells the launcher which layer
corresponds to which clock hand. If your icon does not have all three clock hands, you can omit the
corresponding layer index attribute or set it to `-1`.

Here is an example of a clock icon:

```xml

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/clock_background" />
    <foreground>
        <layer-list>
            <item>
                <rotate android:drawable="@drawable/clock_hour" android:fromDegrees="300.0"
                    android:pivotX="50.0%" android:pivotY="50.0%" android:toDegrees="5300.0" />
            </item>
            <item>
                <rotate android:drawable="@drawable/clock_minute" android:fromDegrees="60.0"
                    android:pivotX="50.0%" android:pivotY="50.0%" android:toDegrees="60060.0" />
            </item>
            <item>
                <rotate android:drawable="@drawable/clock_second" android:fromDegrees="180.0"
                    android:pivotX="50.0%" android:pivotY="50.0%" android:toDegrees="6180.0" />
            </item>
            <item android:drawable="@drawable/clock_top" />
        </layer-list>
    </foreground>
</adaptive-icon>
```

Pay attention to how each of the three clock hand layers is wrapped in a `RotateDrawable` and how
there are
very
specific values set for `android:fromDegrees` and `android:toDegrees`. This is required for
launchers
to be able to animate the clock hands. The values for `android:fromDegrees` and `android:toDegrees`
must follow these rules:

- For the clock hour hand, the difference between `android:fromDegrees` and `android:toDegrees`
  must be `5000`.
- For the clock minute hand, the difference between `android:fromDegrees` and `android:toDegrees`
  must be `60000`.
- For the clock second hand, the difference between `android:fromDegrees` and `android:toDegrees`
  must be `6000`.
- Each hand can have an offset from 0° to set the clock to a specific time (this is useful so that
  launchers that don't support dynamic clock icons don't display the icon as noon). In the example
  above, the hour hand is offset by 300°, the minute hand by 60° and the second hand by 180°. This
  means that the clock shows 10:10:30 in its default state.
- To let the launcher know which time the clock shows in its default state, you can use
  the `defaultHour`, `defaultMinute` and `defaultSecond` attributes in the `appfilter.xml` entry.

> [!NOTE]
>
> `defaultHour`, `defaultMinute` and `defaultSecond` are independent from each other. If you
> set `defaultHour` to 10, then it is expected that the hour hand drawable is rotated by exactly
> 300°,
> regardless of the positions of the minute and second hands even if that means that the clock shows
> an impossible time.

> [!INFO]
>
> **Why these numbers?**
>
> Launchers use the `android:level` attribute to
> animate the clock hands. A drawable's level is a number
> between 0 and 10000 that influences how the drawable is drawn. For `RotateDrawable`s, the level
> attribute is used to set the rotation angle. Each level corresponds
> to 1/10000 of the angle between `android:fromDegrees` and `android:toDegrees`.
>
> For the second hand, it is expected
> that [10 levels are equal to 1 second](https://cs.android.com/android/platform/superproject/+/refs/heads/master:frameworks/libs/systemui/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java;drc=7346c436e5a11ce08f6a80dcfeb8ef941ca30176;l=84).
> which means that 600 levels correspond to a full rotation. But since a drawable has 10000 levels,
> the total angle must be `360/600 * 10000 = 6000` degrees.
>
> For the minute layer, one level is equal to one minute so 60 levels are equal to a full rotation.
> This means that the total angle must be `360/60 * 10000 = 60000` degrees.
>
> For the hour layer, one level is also equal to one minute, so there are `12 * 60 = 720` levels in
> a
> full rotation. `360/720 * 10000 = 5000` degrees.
>
> Technically, you could also use other kinds of drawables that support the `android:level`
> attribute (such as a `LevelListDrawable`), as long as you follow the rules above.

## Themed icons

Themed icons are monochrome icons that are tinted to match the theme color of the launcher.

To declare that your icon pack supports themed icons, add the following intent filter to your
`AndroidManifest.xml`:

```xml

<intent-filter>
    <action android:name="app.lawnchair.icons.THEMED_ICON" />
</intent-filter>
```

The launcher will then show a toggle in the icon pack picker which allows the user to enable themed
icons.

Themed icons can be provided in any of the following ways:

- If the icon is an adaptive icon, and the drawable has
  a `<monochrome>` layer, this icon will be used as the themed icon.
- If the icon is an adaptive icon, and it does not have a `<monochrome>` layer, the foreground layer
  will be used.
- If the icon is not an adaptive icon, the entire icon will be used.

In any case, the icon will be tinted using the theme color and the background will be set to a solid
color.
