Customize system bars

To customize system bars, use a combination of XML configurations and Dagger modules to the UI components.

Define system bar behavior

To define a system bar, use the <SystemBar> tag in an XML file to define the appearance and animation. This XML file is part of a runtime resource overlay (RRO).

SystemBar tag attributes

The root element for a system bar configuration is <SystemBar>, which supports these attributes:

Attribute Status Description
id Required Unique resource ID for the system bar. For example, @id/my_custom_status_bar
type Required Specifies the type of system bar, which is either status or navigation
barZOrder Required

Integer to represent the Z-order of the system bar. Higher values indicate the system draws the bar on top of others. Must be a positive integer. These rules apply:

  • If you display a system bar above a heads up notification, then this value must be greater than 10
  • Overlapping system bars can't have the same Z-order
defaultVariant Required ID of the <Variant> that applies by default when the system bar is initialized
displayId Optional ID of the <Variant> that applies by default when the system bar is initialized
hideForKeyboard Optional

Boolean value of true or false to indicate if the system bar automatically hides when the software keyboard is active. This attribute defaults to false.

When this attribute is true, you must provide _System_Show_Panel and _System_Hide_Panel transitions for the system bar.

dragOpenNotification Optional

Boolean value of true or false to indicate if the system bar automatically triggers opening the notification panel. This attribute defaults to false.

dragCloseNotification Optional

Boolean value of true or false to indicate if the system bar automatically triggers closing the notification panel. This attribute defaults to false.

System bar IDs and types

Avoid using TopCarSystemBar, BottomCarSystemBar, LeftCarSystemBar, and RightCarSystemBar as id values. The system reserves these IDs for backward compatibility, and their use might lead to unexpected behavior.

For basic top status bar and bottom navigation bar configurations, use status and nav, respectively, as the type attribute values.

If you use these IDs, you can skip the section entitled Provide a system bar UI with Dagger.

<Variant>
Defines visual states. To learn more, see Use a variant to design a visual state. In the <SystemBar> tag, define one or more <Variant> tags. Each variant represents a distinct visual state and contains properties that control the appearance of the system bar in that state.
<Visibility isVisible="true|false">
Controls the visibility of the system bar. The isVisible boolean signals whether the system bar is visible or not.
<Alpha alpha="float_value">
Controls the transparency of the system bar. The value for alpha floats between 0.0 (fully transparent) and 1.0 (fully opaque).
<Bounds .../>

Defines the position and size of the system bar. For <SystemBar> elements, the bounds must touch at least one edge of the display (left, top, right, or bottom). Attributes are:

  • left, top, right, bottom: Absolute coordinates.
  • width, height: Dimensions.
  • leftOffset, topOffset, rightOffset, bottomOffset: Offsets towards center of the rectangle.
<Corner radius="dimen"/>

Defines the corner radius of the system bar. For radius, enter the dimension of the corner radius.

<Insets .../>

Defines insets for the system bar. Attributes are left, top, right, and bottom. For each attribute, enter a dimension value for the insets.

<Gravity .../>

Defines the gravity of the system bar's content. To learn more, see HunTagXmlParserKt.GRAVITY_TAG in the source code.

  • When you omit a value for gravity, the system calculates it internally.
  • Supported values are combinations of TOP, BOTTOM, LEFT, RIGHT, CENTER, CENTER_HORIZONTAL, CENTER_VERTICAL, and FILL_HORIZONTAL, each separated by a | character.

Dimension units

Specify dimensions with px, dp (or dip), %, or references to dimension, integer, fraction, string, or attribute resources.

Transitions: Animate between variants

To learn more, see Configure a transition. Use the <Transitions> block to define how the system bar animates between different variants:

Item Tag attribute
<Transition> fromVariant, toVariant, onEvent, onEventTokens, animator, duration, delay, interpolator
<Transitions> defaultDuration, defaultInterpolator

Scalable UI supports transitions in system bars only when you define them for non-immersive mode use cases. This means that immersive mode doesn't trigger Scalable UI windowing transitions for system bars when it hides (or shows) a system bar.

Scalable UI continues to send events for hiding (or showing) a system bar so that other panels can respond as needed while transitions for hiding and showing system bars must be provided for proper functioning of the hideForKeyboard attribute. Consider this sample XML structure:

<SystemBar id="@id/my_custom_status_bar" type="status" barZOrder="0" defaultVariant="@id/default_variant" hideForKeyboard="true">
    <Variant id="@+id/default_variant">
        <Bounds top="0px" left="0px" right="100%" height="100px"/>
        <Visibility isVisible="true"/>
    </Variant>
    <Variant id="@+id/hidden_variant" parent="@id/default_variant">
        <Visibility isVisible="false"/>
    </Variant>
    <Transitions>
        <Transition onEvent="_System_Show_Panel" onEventTokens="panelId= my_custom_status_bar" toVariant="@id/default_variant"/>
        <Transition onEvent="_System_Hide_Panel" onEventTokens="panelId= my_custom_status_bar" toVariant="@id/hidden_variant"/>
    </Transitions>
</SystemBar>

Provide a system bar UI with Dagger

After you define the system bar in XML, provide the actual View and Window. To do so, apply an application override to the default Dagger module, CarSystemBarModule.java. For example:

import com.android.systemui.car.systembar.CarSystemBarViewSupplier;
import com.android.systemui.car.systembar.CarSystemBarWindowSupplier;
import com.android.systemui.car.systembar.CarSystemBarWindowSupplierUsingLayout;
import com.example.R;

import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;

@Module
public abstract class MySystemBarModule extends CarSystemBarModule {

    @Provides
    @IntoMap
    @StringKey("my_custom_status_bar") // Matches the <SystemBar> id
    static CarSystemBarViewSupplier bindMyCustomStatusBarViewSupplier() {
        return new CarSystemBarViewSupplierUsingLayout(
            R.layout.my_custom_status_bar, // provisioned layout
            R.layout.my_custom_status_bar_unprovisioned // unprovisioned layout
        );
    }

    @Provides
    @IntoMap
    @StringKey("my_custom_status_bar") // Matches the <SystemBar> id
    static CarSystemBarWindowSupplier bindMyCustomStatusBarWindowSupplier() {
        return new CarSystemBarWindowSupplierUsingLayout(
            R.layout.my_navigation_bar_window, // Can reuse existing window layouts
            R.id.my_custom_bar_window // The ID that will be assigned to the window
        );
    }
}

Create a Dagger module in a SystemUI override

To inflate your custom layout resources, use the CarSystemBarViewSupplierUsingLayout and CarSystemBarWindowSupplierUsingLayout classes.

Create a Dagger module to provide your custom suppliers. The @StringKey must match the id in your <SystemBar> XML tag.

To override CarSystemBarModule, see this code sample:

import com.android.systemui.car.systembar.CarSystemBarViewSupplier;
import com.android.systemui.car.systembar.CarSystemBarWindowSupplier;
import com.android.systemui.car.systembar.CarSystemBarWindowSupplierUsingLayout;
import com.example.R;

import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;

@Module
public abstract class MySystemBarModule extends CarSystemBarModule {

    @Provides
    @IntoMap
    @StringKey("my_custom_status_bar") // Matches the <SystemBar> id
    static CarSystemBarViewSupplier bindMyCustomStatusBarViewSupplier() {
        return new CarSystemBarViewSupplierUsingLayout(
            R.layout.my_custom_status_bar, // provisioned layout
            R.layout.my_custom_status_bar_unprovisioned // unprovisioned layout
        );
    }

    @Provides
    @IntoMap
    @StringKey("my_custom_status_bar") // Matches the <SystemBar> id
    static CarSystemBarWindowSupplier bindMyCustomStatusBarWindowSupplier() {
        return new CarSystemBarWindowSupplierUsingLayout(
            R.layout.my_navigation_bar_window, // Can reuse existing window layouts
            R.id.my_custom_bar_window // The ID that will be assigned to the window
        );
    }
}

Use an RRO to create a system-level configuration

Set several system-level configurations that affect system bars in the res/values/config.xml file in your RRO.

Disable legacy system bars

To prevent conflicts with Scalable UI, disable the legacy system bar configurations by setting the following flags to false:

<resources>
    <bool name="config_enableTopSystemBar">false</bool>
    <bool name="config_enableBottomSystemBar">false</bool>
    <bool name="config_enableLeftSystemBar">false</bool>
    <bool name="config_enableRightSystemBar">false</bool>
</resources>

Privacy indicator location

The config_privacyIndicatorLocation string resource specifies which system bar hosts privacy indicators. The value must be the id name of a <SystemBar>.

<resources>
    <!-- "my_custom_status_bar" corresponds to the android:id name of a SystemBar -->
    <string name="config_privacyIndicatorLocation">my_custom_status_bar</string>
</resources>

Drag event listeners

These configurations specify which system bars listen for drag events (for example, to swipe down to open the notification panel). Starting in Android Automotive OS with Scalable UI, use XML attributes as the default instead of instead of driven discovery resource arrays to define these capabilities.

Recommended: XML-driven discovery (Scalable UI)

Use the dragOpenNotification and dragCloseNotification attributes directly in your <SystemBar> tag inside your overlay XML:

<SystemBar id="@id/my_custom_status_bar" type="status" barZOrder="0" defaultVariant="@id/default_variant" dragOpenNotification="true">
    <Variant id="@+id/default_variant">
        <Bounds top="0px" left="0px" right="100%" height="100px"/>
        <Visibility isVisible="true"/>
    </Variant>
</SystemBar>

Legacy: Resource array (fallback)

If you're maintaining a non-Scalable UI build or need to specify listeners for backward-compatible devices, use the legacy string-array approach in res/values/config.xml in your RRO. These string-array resources specify which system bars listen for drag events. For example, to open the notification panel. Each <item> is the id name of a system bar.

  • config_registerHvacDragCloseListener
  • config_notificationDragOpenListener
  • config_notificationDragCloseListener

For example:

<resources>
    <string-array name="config_notificationDragOpenListener" translatable="false">
        <item>my_custom_status_bar</item>
    </string-array>
</resources>

Build and deploy

To build and deploy a status bar:

  1. Flash the device with your modified SystemUI override application.

  2. Use the Android build system (m) to compile your RRO project.

  3. Deploy the generated RRO APK to your Android Automotive device. Use adb install or flash a full build that includes your RRO.