Package com.codename1.surfaces


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:

Node iOS (SwiftUI) Android (RemoteViews) Caveat
Column/Row VStack/HStack LinearLayout weight maps to layout_weight
Box ZStack FrameLayout child alignment via 9-way enum
Text Text TextView Android renders light/regular/medium as regular, semibold/bold as bold
DynamicText timer Text(date, style:) Chronometer native on both
DynamicText time Text(date, style: .time) TextClock native on both
DynamicText date/relative native static text Android refreshes only on next update
Image Image ImageView named PNG blobs, content-hash dedup
Progress linear ProgressView ProgressBar value 0..1 or state key
Progress circular Gauge falls back to linear Android widgets lack determinate circular
Progress date interval native animation frozen at refresh iOS-only nicety
Vector SwiftUI Canvas bitmap rendered in-process desktop: CN1 Graphics; Android renders vec nodes as raster, so very large vec nodes cost bitmap budget
Spacer Spacer weighted View
corner radius clipShape background drawable may render square below Android 12
node action Link / widgetURL setOnClickPendingIntent small 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.

  • Class
    Description
    A 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.
    Describes a live activity: an ongoing-state surface (delivery, timer, ride, score) that stays visible outside the app while it is in progress.
    A user interaction with an external surface: the user tapped a node that carries an action id.
    Receives surface action events.
    Nine-way alignment of a node within its parent.
    A container that stacks its children on top of each other.
    A color used on an external surface.
    A container that stacks its children vertically.
    The base class of surface nodes that hold children: SurfaceColumn, SurfaceRow and SurfaceBox.
    A 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.
    Font weight of a surface text node.
    An image node.
    The base class of all surface layout nodes.
    A progress indicator node.
    A 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.
    The absolute bounds of a node that carries a tap action, in pixels of the rasterized image.
    The output of a rasterization pass: pixels, action hit rectangles and the next re-render deadline.
    A container that lays its children out horizontally.
    The 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.
    Serializes surface descriptors to the canonical wire format shared by every port: a compact JSON document plus PNG blobs named by content hash.
    A flexible spacer that absorbs the leftover space of its parent row or column, pushing its siblings apart.
    A static text node.
    A 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).
    Declares one kind of home-screen widget the app offers (an app may offer several).
    The size families a widget kind supports.
    The content of a widget kind: a layout descriptor plus a timeline of dated state snapshots.
    One dated state snapshot within a timeline.