Package com.codename1.surfaces.spi


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.
  • Interfaces
    Class
    Description
    The 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).