CarBox Launcher logo

CarBox Widget SDK

Build native widgets for Creative Mode.

Create real Android mini-apps with Android Studio, Java or Kotlin, XML layouts and custom Views. CarBoxLauncher hosts them safely inside movable and resizable panels.

Native Android Java + Kotlin XML + custom Views Isolated process
SpeedWidgetActivity.java
public final class SpeedWidgetActivity
    extends CarBoxWidgetActivity {

  @Override
  protected void onCreate(Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.speed_widget);
  }

  @Override
  protected void onWidgetVisible() {
    speedSensor.start();
  }

  @Override
  protected void onWidgetHidden() {
    speedSensor.stop();
  }
}

Starter downloads

Open Android Studio and start building.

Both packages are standalone projects with the Widget SDK, Gradle Wrapper and complete development manual included.

EMPTY

Start here

Empty widget template

The minimal project to duplicate and rename before creating your own interface and behavior.

Widget SDK included Ready-to-use Gradle project Complete manual included
Download empty template ZIP · 67 KB
DEMO

Working example

Analog clock source

A complete native widget built with Java, an XML layout and a custom Canvas View.

Java Activity XML + custom View .cbwidget packaging task
Download example source ZIP · 69 KB

Architecture

A widget is a native Android mini-app.

Each widget is compiled as its own Android package, has no launcher icon and runs with its own UID. CarBoxLauncher discovers its Activity through a public manifest contract and renders it inside a VirtualDisplay panel.

01

No visual limits

Use any Android View, Canvas drawing, animations, databases, sensors and compatible Gradle libraries.

02

Process isolation

Widget code never runs inside the privileged CarBoxLauncher process. A broken widget cannot inherit launcher permissions.

03

Store ready

Package the development output as a .cbwidget for distribution and updates through the CarBox Store.

Requirements

Prepare Android Studio.

Use Android Studio with SDK Platform 34 and its bundled JDK 21. The current SDK supports boxes from Android 5.0 onward.

  • Android Studio and Android SDK 34
  • JDK 21 for Gradle, Java 17 source compatibility
  • CarBoxLauncher with native widget support
  • ADB connection for installation and logcat

Quick start

Start from the empty widget.

Open the Widget project from the CarBoxLauncher source tree and duplicate empty-widget. Give the copy a permanent application ID before writing your interface.

1. Duplicate

Copy empty-widget and register the new folder as a Gradle module.

2. Identify

Change namespace, applicationId, Java package, widget name and carbox-widget.json ID.

3. Design

Build the screen with XML and implement behavior in your Activity or custom Views.

4. Package

Compile the APK, test it on a box and generate the .cbwidget container.

settings.gradle
include ':my-widget'
project(':my-widget').projectDir = file('my-widget')
build.gradle
android {
  namespace 'com.example.mywidget'
  compileSdk 34

  defaultConfig {
    applicationId 'com.example.mywidget'
    minSdk 21
    targetSdk 34
    versionCode 1
    versionName '1.0.0'
  }
}

dependencies {
  implementation project(':carbox-widget-sdk')
}

Discovery contract

Declare one invisible widget Activity.

Never add the LAUNCHER category. Export only the CarBox widget action and declare API version plus initial panel dimensions.

AndroidManifest.xml
<activity
  android:name=".MyWidgetActivity"
  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density"
  android:excludeFromRecents="true"
  android:exported="true"
  android:resizeableActivity="true">

  <intent-filter>
    <action android:name="com.ferra.carboxlauncher.action.CARBOX_WIDGET" />
    <category android:name="com.ferra.carboxlauncher.category.CARBOX_WIDGET" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>

  <meta-data
    android:name="com.ferra.carboxlauncher.widget.API_VERSION"
    android:value="1" />
  <meta-data
    android:name="com.ferra.carboxlauncher.widget.DEFAULT_WIDTH_DP"
    android:value="320" />
  <meta-data
    android:name="com.ferra.carboxlauncher.widget.DEFAULT_HEIGHT_DP"
    android:value="200" />
</activity>

Per-instance settings

Give each widget its own settings.

Add a SECOND exported Activity with the settings action. In edit mode CarBox shows a gear on the panel's bottom-left corner that opens it — external to the widget. CarBox passes a stable per-instance id: namespace your preferences with it so two copies of the same widget (e.g. one digital clock, one analog) keep independent settings. When the settings close, CarBox reloads that widget so it re-reads its config.

Transparent widget background — the RUNTIME theme must be translucent
// Without windowIsTranslucent the widget window is opaque and the
// VirtualDisplay composites it over black → a "transparent" background
// shows BLACK instead of the launcher wallpaper. Required on Theme.CarBoxWidget:
<style name="Theme.CarBoxWidget" parent="android:style/Theme.Material.NoActionBar">
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowBackground">@android:color/transparent</item>
</style>
AndroidManifest.xml
<activity
  android:name=".SettingsActivity"
  android:exported="true"
  android:theme="@style/Theme.CarBoxWidgetSettings">
  <intent-filter>
    <action android:name="com.ferra.carboxlauncher.action.CARBOX_WIDGET_SETTINGS" />
    <category android:name="com.ferra.carboxlauncher.category.CARBOX_WIDGET" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>
res/values/styles.xml — dialog look, launcher visible behind, no system bars
<style name="Theme.CarBoxWidgetSettings" parent="android:style/Theme.Material.NoActionBar">
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowNoTitle">true</item>
  <item name="android:statusBarColor">@android:color/transparent</item>
  <item name="android:navigationBarColor">@android:color/transparent</item>
</style>
SettingsActivity.java
// per-instance config
String id = getIntent().getStringExtra(
    CarBoxWidget.EXTRA_WIDGET_INSTANCE_ID);
SharedPreferences prefs = getSharedPreferences(
    CarBoxWidget.configName(id), MODE_PRIVATE);
prefs.edit().putString("mode", "analog").apply();

// hide status + navigation bars
getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    | View.SYSTEM_UI_FLAG_FULLSCREEN
    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

Runtime

Respect visibility and resize events.

Extend CarBoxWidgetActivity. Start timers, sensors and listeners only while visible, stop them when hidden, and adapt the UI whenever the panel size changes.

MyWidgetActivity.java
@Override
protected void onWidgetVisible() {
  handler.post(updateTask);
}

@Override
protected void onWidgetHidden() {
  handler.removeCallbacks(updateTask);
}

@Override
protected void onWidgetResize(int width, int height) {
  gauge.setCompact(width < 320);
}

Build

Compile and install on the box.

Run the Gradle task from the Widget root. Install the debug APK during development, then add it from Creative Mode.

PowerShell
$env:JAVA_HOME = 'C:\Program Files\Android\Android Studio\jbr'
.\gradlew.bat :my-widget:packageCarBoxWidget

adb -s BOX_IP:5555 install -r `
  my-widget\build\outputs\apk\debug\my-widget-debug.apk
In CarBoxLauncher: Creative Mode → Edit → + → CarBox Widgets → choose your widget.
Uninstall safely Long-press a widget icon in the CarBox Widgets tab. CarBoxLauncher removes the package and every instance placed across all six Creative Mode pages.

Distribution

Inside a .cbwidget package.

The container is a ZIP with a compiled plugin.apk and manifest.json. The CarBox server validates production packages before they are offered for installation.

my-widget.cbwidget
APKplugin.apk
JSONmanifest.json

Before publishing

Release checklist

  • Unique and permanent applicationId
  • No LAUNCHER Activity
  • Matching IDs and versions in Gradle and manifest.json
  • Responsive layout at multiple panel sizes
  • Timers and listeners stop when hidden
  • No unnecessary Android permissions
  • Tested installation, update and uninstall
  • Icon, screenshots and offline behavior verified

Talk to the launcher

Use the CarBox Launcher API.

Building the entire home screen? Panel placement, animated overlays and native panel-handle visibility are protected Theme APIs, not Widget APIs. Open the Theme API guide

A widget runs inside a VirtualDisplay in its own process, so it cannot call the launcher directly — and calling startActivity() from a widget would open the app INSIDE the widget panel. The SDK exposes two channels: fire-and-forget commands (CarBoxWidgetCommands) and read-only state queries (CarBoxWidgetState). Import both from the Widget SDK.

Commands — do something in the launcher
// launch an app fullscreen (outside the panel)
CarBoxWidgetCommands.launchAppFullscreen(context, "com.spotify.music");
CarBoxWidgetCommands.launchFavorite(context, 1);   // favorite 1..5
CarBoxWidgetCommands.openAppDrawer(context);
CarBoxWidgetCommands.openAssistant(context);       // voice assistant
CarBoxWidgetCommands.speak(context, "Hello");
CarBoxWidgetCommands.openPhone(context);
CarBoxWidgetCommands.openContacts(context);
CarBoxWidgetCommands.call(context, "+391234567");  // or a contact name
CarBoxWidgetCommands.openNotifications(context);
CarBoxWidgetCommands.goToPage(context, 3);
CarBoxWidgetCommands.nextPage(context);
CarBoxWidgetCommands.previousPage(context);
CarBoxWidgetCommands.goHome(context);
CarBoxWidgetCommands.toggleEditMode(context);
CarBoxWidgetCommands.setEditMode(context, true);
CarBoxWidgetCommands.reloadPanels(context);
CarBoxWidgetCommands.setMode(context, "creative"); // or "dual"
CarBoxWidgetCommands.toggleMode(context);
CarBoxWidgetCommands.openSystemSettings(context, "wifi"); // wifi|bluetooth|data
CarBoxWidgetCommands.restartLauncher(context);
Queries — read launcher state
// follow the language selected in the launcher
String lang = CarBoxWidgetState.getLanguage(context); // "it" / "en"
applyLanguage(lang);

String mode = CarBoxWidgetState.getMode(context); // "creative" / "dual"
boolean creative = CarBoxWidgetState.isCreativeMode(context);
boolean editing = CarBoxWidgetState.isEditMode(context);
int page  = CarBoxWidgetState.getCurrentPage(context);
int pages = CarBoxWidgetState.getPageCount(context);

State is exposed by a read-only ContentProvider. Every SDK query is defensive and returns the documented fallback when the launcher or provider is unavailable.

Query Provider column Fallback
getLanguagelanguage"en"
getModemode"dual"
isCreativeModemodefalse
isEditModeedit_modefalse
getCurrentPagecurrent_page0
getPageCountpage_count0

Read-only provider URI: content://com.ferra.carboxlauncher.state/config

Command Parameter What it does
launchAppFullscreenpackageOpen an app fullscreen on the main display
launchFavorite1–5Open launcher favorite N
openAppDrawerOpen the app drawer
openAssistant / speak— / textVoice assistant / text-to-speech
openPhone / openContacts / call— / — / numberPhone dialer, contacts, place a call via the paired phone
openNotificationsLeft-handle notifications panel
goToPage / nextPage / previousPage / goHomeN / —Navigate creative-mode pages
toggleEditMode / setEditMode / reloadPanelsboolean for setEditModeToggle or explicitly set widget edit mode / reload panels
setMode / toggleModecreative|dualSwitch between creative and dual mode
openSystemSettingswifi|bluetooth|dataOpen system settings
restartLauncherRestart the launcher
Advanced command transport Prefer the typed helpers above. Use sendCommand only when integrating a new command added by a newer Launcher API.
Generic command example
Intent command = new Intent()
    .putExtra(CarBoxWidgetCommands.EXTRA_COMMAND, "commandName")
    .putExtra(CarBoxWidgetCommands.EXTRA_TEXT, "value");

CarBoxWidgetCommands.sendCommand(context, command);
SDK constant Wire value Purpose
LAUNCHER_PACKAGEcom.ferra.carboxlauncherExplicit broadcast destination
ACTION_WIDGET_COMMANDcom.ferra.carboxlauncher.action.WIDGET_COMMANDExplicit broadcast action
EXTRA_COMMANDcom.ferra.carboxlauncher.extra.COMMANDRequired command name
EXTRA_PACKAGE_NAMEcom.ferra.carboxlauncher.extra.PACKAGE_NAMEAndroid application package
EXTRA_INTcom.ferra.carboxlauncher.extra.INTInteger parameter
EXTRA_BOOLcom.ferra.carboxlauncher.extra.BOOLBoolean parameter
EXTRA_TEXTcom.ferra.carboxlauncher.extra.TEXTText parameter

Full working example: the CarBox Button widget — a fully configurable button (color, background transparency, image, label) where the settings let you pick any of these actions. It's the reference for using the whole API.

Available now

Publish and update through the CarBox Store.

CarBoxLauncher can discover, install, update and remove validated .cbwidget packages directly from the Widget Store.