package com.codename1.surfaces

External surfaces: home-screen widgets and live activities from one declarative API.

Both features answer the same developer question – how do I keep a live source of information outside my app? – so Codename One models them as one concept. A widget is persistent, user-placed and content-driven (weather, next meeting, delivery status on the home screen). A live activity is transient, app-started and progress-driven (the delivery that is out RIGHT NOW, a running timer, a live score on the iOS lock screen / Dynamic Island, an ongoing Android notification, a floating desktop pill). They share the layout model, the serialization, the state mechanism and the action model.

The dead-process rule

Surfaces render while your app process may not be running. Everything you hand this API is therefore turned into plain data at publish time: layouts serialize to a compact JSON descriptor, images are encoded to named PNG blobs, and “callbacks” are string action ids that open the app and are delivered to your SurfaceActionHandler on the EDT (queued across a cold start). Layout text embeds ${key} placeholders resolved from a per-entry state map, so content changes are cheap re-publishes of data, not layouts.

The layout model

A small sealed catalog of nodes every platform can render natively – SurfaceColumn, SurfaceRow, SurfaceBox, SurfaceText, SurfaceDynamicText, SurfaceImage, SurfaceProgress, SurfaceVector, SurfaceSpacer – with shared styling (padding, background, corner radius, alignment, weight, size, action). SurfaceDynamicText is the headline feature: countdowns and elapsed-time text the OS animates on its own clock, so a delivery ETA ticks every second with zero app wakeups. SurfaceVector covers the widgets the template catalog cannot express – clocks, gauges, dials – with a retained list of vector drawing ops (fills, strokes, arcs, text, rotation groups) replayed natively by every renderer.

The lowest common denominator contract

Android app widgets (RemoteViews) are the constrained platform; the catalog is designed to its floor and degrades as follows:

NodeiOS (SwiftUI)Android (RemoteViews)Caveat
Column/RowVStack/HStackLinearLayoutweight maps to layout_weight
BoxZStackFrameLayoutchild alignment via 9-way enum
TextTextTextViewAndroid renders light/regular/medium as regular, semibold/bold as bold
DynamicText timerText(date, style:)Chronometernative on both
DynamicText timeText(date, style: .time)TextClocknative on both
DynamicText date/relativenativestatic textAndroid refreshes only on next update
ImageImageImageViewnamed PNG blobs, content-hash dedup
Progress linearProgressViewProgressBarvalue 0..1 or state key
Progress circularGaugefalls back to linearAndroid widgets lack determinate circular
Progress date intervalnative animationfrozen at refreshiOS-only nicety
VectorSwiftUI Canvasbitmap rendered in-processdesktop: CN1 Graphics; Android renders vec nodes as raster, so very large vec nodes cost bitmap budget
SpacerSpacerweighted View
corner radiusclipShapebackground drawablemay render square below Android 12
node actionLink / widgetURLsetOnClickPendingIntentsmall iOS widgets honor only the root action

Descriptors are limited to 8 nesting levels; keep payloads (JSON + images) comfortably under 200kb – the iOS widget extension runs in about 30mb of memory and Android parcels rendered widgets over a 1mb binder transaction.

Example: an analog clock widget

A SurfaceVector face plus per-minute timeline entries driving the hand angles. Angles use the clock convention (degrees, 0 = 12 o’clock, clockwise positive); the OS flips the entries on schedule, so the clock stays correct for an hour with zero app wakeups:

SurfaceVector face = new SurfaceVector(200, 200)
        .fillEllipse(100, 100, 96, 96, SurfaceColor.BACKGROUND)
        .beginRotation("hourAngle", 100, 100)
            .line(100, 100, 100, 52, 8, SurfaceColor.LABEL).endRotation()
        .beginRotation("minuteAngle", 100, 100)
            .line(100, 100, 100, 24, 5, SurfaceColor.ACCENT).endRotation();
WidgetTimeline t = new WidgetTimeline().setContent(face);
for (int m = 0; m < 60; m++) {
    int totalMinutes = hourOfDay * 60 + minute + m;
    Map<String, Object> state = new HashMap<String, Object>();
    state.put("minuteAngle", totalMinutes % 60 * 6f);
    state.put("hourAngle", totalMinutes % 720 * 0.5f);
    t.addEntry(new Date(minuteStart + m * 60000L), state);
}
Surfaces.publish("analog_clock", t);

Build-time manifest: surfaces.json

Platform widget galleries are compiled into the native app, so widget kinds must be known at build time. Keep a surfaces.json in your project resources (next to your icons) mirroring your runtime WidgetKind registrations:

{
  "liveActivities": true,
  "kinds": [
    { "id": "delivery_status", "name": "Delivery", "description": "Track your order",
      "iosFamilies": ["systemSmall", "systemMedium"] }
  ]
}

Refresh and updates

This version updates surfaces from the running app: publish a WidgetTimeline of dated entries (the OS flips entries on schedule without waking you), implement com.codename1.background.BackgroundFetch to re-publish periodically, and push live activity state with LiveActivity.update(...). Push-driven updates (server-sent widget content and ActivityKit push tokens) are planned; the wire format already accommodates them.

Zero cost when unused

Referencing this package makes the build inject the native plumbing (WidgetKit extension and app group on iOS, widget receivers on Android). Apps that never touch it get none of it. On unsupported ports every entry point is an inert no-op.

Types

class LiveActivityA running live activity: an ongoing-state surface (delivery, timer, ride, score) presented on the iOS lock screen and Dynamic Island, as an ongoing Android notification, or as a floating pill window on desktop.
class LiveActivityDescriptorDescribes a live activity: an ongoing-state surface (delivery, timer, ride, score) that stays visible outside the app while it is in progress.
class SurfaceActionEventA user interaction with an external surface: the user tapped a node that carries an action id.
interface SurfaceActionHandlerReceives surface action events.
enum SurfaceAlignmentNine-way alignment of a node within its parent.
class SurfaceBoxA container that stacks its children on top of each other.
class SurfaceColorA color used on an external surface.
class SurfaceColumnA container that stacks its children vertically.
class SurfaceContainerThe base class of surface nodes that hold children: SurfaceColumn, SurfaceRow and SurfaceBox.
class SurfaceDynamicTextA date-driven text node the operating system animates natively – the headline feature for timers, delivery ETAs and elapsed-time displays: a countdown keeps ticking every second on the widget or Dynamic Island even though the app process is not running.
enum SurfaceFontWeightFont weight of a surface text node.
class SurfaceImageAn image node.
class SurfaceNodeThe base class of all surface layout nodes.
class SurfaceProgressA progress indicator node.
class SurfaceRasterizerA shared software renderer for desktop ports (the JavaSE simulator, the native Windows and Linux ports): draws a parsed surface descriptor node into a mutable Codename One Image using only Graphics primitives – no Container/Label components and no theme dependence, so the output looks the same on every desktop surface.
class SurfaceRowA container that lays its children out horizontally.
class SurfaceSerializerSerializes surface descriptors to the canonical wire format shared by every port: a compact JSON document plus PNG blobs named by content hash.
class SurfaceSpacerA flexible spacer that absorbs the leftover space of its parent row or column, pushing its siblings apart.
class SurfaceTextA static text node.
class SurfaceVectorA retained vector drawing node: a small catalog of fill/stroke/text operations recorded in paint order and replayed natively by every platform renderer (SwiftUI Canvas on iOS, an in-process bitmap on Android, Codename One Graphics on desktop).
class SurfacesThe static entry point for external surfaces: home-screen widgets and live activities – the two faces of one concept, a live source of information that resides outside your app.
class WidgetKindDeclares one kind of home-screen widget the app offers (an app may offer several).
enum WidgetSizeThe size families a widget kind supports.
class WidgetTimelineThe content of a widget kind: a layout descriptor plus a timeline of dated state snapshots.