package com.codename1.surfaces.spi

The platform seam of the external surfaces framework. Application code never touches this package – ports implement SurfaceBridge and return it from CodenameOneImplementation.getSurfaceBridge().

Port implementer notes

  • Persist everything. Surfaces render while the app process is dead. Published timeline JSON and image blobs must be written where the platform renderer can reach them without the VM: the app group container on iOS (timeline.json plus <name>.png files under cn1surfaces/<kindId>/), the files directory on Android, storage on desktop. Writes should be atomic (write-rename) because the renderer may read concurrently.
  • Actions flow through the framework. Decode your platform payload (deep link, intent extras, window click) and call com.codename1.surfaces.Surfaces.dispatchAction(source, actionId, params); it handles EDT marshaling and cold-start queuing. The canonical deep link form on URL-based platforms is cn1surface://a?src=<source>&id=<actionId>&p=<url-encoded JSON params>.
  • State-only updates. updateLiveActivity ships just a state map; re-interpolate the ${key} placeholders of the descriptor persisted at start time and re-render locally.
  • Degrade honestly. Return false from the is...Supported queries rather than presenting a broken surface; the public API turns into a documented no-op.

Updating surfaces from the background

“A background process fetches data and updates the widget without the app UI running” is a first-class flow: the app implements com.codename1.background.BackgroundFetch, registers by calling com.codename1.ui.Display.setPreferredBackgroundFetchInterval(int) and re-publishes inside performBackgroundFetch. To support it, com.codename1.surfaces.Surfaces.publish(String, com.codename1.surfaces.WidgetTimeline) is callable from any thread and no bridge may block on the EDT or the platform UI thread in its publish path – publishing is data-only (serialize, persist, poke the renderer asynchronously). Rendering may still happen on the EDT later; that is a separate, async step.

Per platform:

  • Android: the fetch runs in a background IntentService while no Activity exists; the bridge resolves the service/application context, so publish works from a UI-less process. The widget also pulls the app: when a provider renders an exhausted reload=atEnd timeline (or no timeline at all) it starts the fetch service directly, throttled to once per 15 minutes per kind, and only when the app declares background fetch.
  • iOS: the widget extension cannot wake the app arbitrarily. A reload=atEnd timeline makes WidgetKit re-request entries from the extension, which re-reads the persisted document – so publish timelines with enough future entries to bridge the gap between fetches. Refresh the data itself from performBackgroundFetch (add fetch to the ios.background_modes build hint); the publish path is file IO plus a WidgetCenter.reloadTimelines call, neither of which requires the UIKit main thread.
  • Desktop / simulator: the app process is running, so a publish simply re-renders the surface windows. The simulator additionally simulates background fetch with a timer that fires performBackgroundFetch while the app is paused, and the Widgets preview window re-renders on every publish.

Types

interface SurfaceBridgeThe platform seam of the external surfaces framework, implemented by ports and returned from CodenameOneImplementation.getSurfaceBridge() (null on unsupported ports, making the whole public API an inert no-op).