Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3d53d0c
Add com.codename1.surfaces: unified widgets + live activities core API
shai-almog Jul 11, 2026
b0d2ac8
Add SurfaceRasterizer + JavaSE surfaces: simulator preview and deskto…
shai-almog Jul 11, 2026
436c059
Android surfaces lowering: RemoteViews renderer, widget providers, on…
shai-almog Jul 11, 2026
6298234
iOS surfaces lowering: WidgetKit extension, ActivityKit/Dynamic Islan…
shai-almog Jul 11, 2026
a13fd6a
Windows surfaces: layered floating widgets + Widgets Board MSIX provider
shai-almog Jul 11, 2026
24acc9e
Linux surfaces: frameless GTK applet windows
shai-almog Jul 11, 2026
fdb56da
SurfacesSample delivery tracker + Catalyst guard + iOS family aliases
shai-almog Jul 11, 2026
c6804b6
Fix surfaces CI failures: CLDC hex, UTF-8 charset, MSVC STL, Alpine g…
shai-almog Jul 11, 2026
ba1a0e2
Address review: iOS widget-support gate, Android state replace, alpha…
shai-almog Jul 11, 2026
5b5b801
SurfaceVector: retained vector drawing for widgets (clocks, gauges, d…
shai-almog Jul 11, 2026
7653495
Background-driven widget refresh: publish-from-anywhere + Android wid…
shai-almog Jul 11, 2026
b3c4919
cn1ss suite: surfaces validation on every platform leg
shai-almog Jul 11, 2026
12c964f
Developer guide: External Surfaces chapter with simulator screenshots
shai-almog Jul 11, 2026
95c7fc0
Fix Checkstyle indentation in the vector line op
shai-almog Jul 11, 2026
4de567c
Fix widget extension deployment target stomp + deployment-independent…
shai-almog Jul 11, 2026
872f50e
Seed SurfacesRasterizer goldens (Android/Linux/Windows) + park surfac…
shai-almog Jul 11, 2026
6141dd2
iOS bridge: FileSystemStorage instead of java.io.File (unimplemented …
shai-almog Jul 11, 2026
901e2f0
Catalyst ActivityKit guards, iOS golden seeds, StringBuilder in mkdirs
shai-almog Jul 12, 2026
ae5476a
Seed the Mac Catalyst SurfacesRasterizer golden
shai-almog Jul 12, 2026
b1c02d9
Fix JS runtime StringBuilder.append(Object): [object Object] corrupte…
shai-almog Jul 12, 2026
167617c
Implement android.surfaces.exactAlarms + RemoteViews end-to-end suite…
shai-almog Jul 12, 2026
55bc9b5
Compile-verify the Widgets Board provider in CI + WinAppSDK layout fixes
shai-almog Jul 12, 2026
465ef56
Wayland layer-shell applets via dlopen (gtk-layer-shell)
shai-almog Jul 12, 2026
d24d069
Exercise live activities on every CI leg
shai-almog Jul 12, 2026
5108422
Fix JavaSE desktop-mode widget bugs found by live verification
shai-almog Jul 12, 2026
8925e8f
Fix compile breaks: stray mic usage description, widgetboard C++20 + …
shai-almog Jul 12, 2026
19b2167
Certificate wizard: turnkey App Groups + widget-extension signing
shai-almog Jul 12, 2026
234f94a
Address surfaces review: antialias rasterizer, implicit fetch, drop s…
shai-almog Jul 13, 2026
41d9046
Address surfaces review: cold-start action race, iOS child alignment,…
shai-almog Jul 13, 2026
31f6838
Fix CN1Widgets app-target compile: resolve activities dir inline in t…
shai-almog Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/parparvm-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -668,3 +668,88 @@ jobs:
echo "[windows-native-port arm64] FATAL: $me screenshot(s) streamed with no stored golden (missing_expected) -- add them to $REF_DIR."; fail=1
fi
if [ "$fail" -ne 0 ]; then echo "Windows arm64 screenshot gate failed."; exit 1; fi

# Widgets Board provider compile check: compiles the one nativeSources TU no
# other CI leg ever compiles -- cn1_windows_widgetboard.cpp, which is gated
# behind CN1_WIDGETBOARD (the default CMake build never defines it and the
# Linux xwin cross leg has no Windows App SDK) -- against the real Windows App
# SDK winmds + cppwinrt-generated C++/WinRT projections on a Windows host.
# This mirrors exactly what WindowsNativeBuilder does for windows.msix=true
# builds: clang-cl (the port is clang-cl-only by design -- cn1_globals.h uses
# `_Atomic` and `__thread`, which MSVC cl.exe rejects in C++ -- and the
# builder sets CMAKE_CXX_COMPILER to clang-cl on Windows hosts and cross
# builds alike), /std:c++17 (the translator-emitted CMAKE_CXX_STANDARD),
# /EHsc (C++/WinRT requires exceptions) and /DCN1_WIDGETBOARD=1 plus the
# WinAppSDK include dir. The same step also re-compiles
# cn1_windows_widgets.cpp (the plain-exe floating-widget TU) with the
# Windows-host toolchain as a second real-compiler check beyond the Linux
# xwin leg. Compile-only (/c): any compile error fails the job.
widgetboard-compile-check:
name: widgetboard-compile-check (x64)
runs-on: windows-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@v6

# clang-cl uses the MSVC STL/CRT headers and the Windows SDK, which reach
# the compiler through the INCLUDE/LIB environment vcvarsall sets up.
- name: Set up MSVC environment
uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64

# The Microsoft.WindowsAppSDK nupkg ships winmd metadata + MddBootstrap.h
# but NO prebuilt C++/WinRT headers, so the projection (winrt/base.h, the
# referenced Windows.* namespaces from the OS metadata, and
# winrt/Microsoft.Windows.Widgets[.Providers].h) is generated with
# cppwinrt.exe. Versions are pinned for reproducibility: the 1.5 servicing
# release matches the 1.5 series the provider bootstraps at runtime
# (CN1_WINAPPSDK_MAJORMINOR 0x00010005 in the TU). nuget.exe is
# preinstalled on the windows runners.
- name: Fetch Windows App SDK + generate C++/WinRT projections
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$wasdkVer = '1.5.250108004'
$cppwinrtVer = '2.0.240405.15'
nuget install Microsoft.WindowsAppSDK -Version $wasdkVer -OutputDirectory nupkgs -NonInteractive
if ($LASTEXITCODE -ne 0) { throw "nuget install Microsoft.WindowsAppSDK failed" }
nuget install Microsoft.Windows.CppWinRT -Version $cppwinrtVer -OutputDirectory nupkgs -NonInteractive
if ($LASTEXITCODE -ne 0) { throw "nuget install Microsoft.Windows.CppWinRT failed" }
$wasdk = "nupkgs/Microsoft.WindowsAppSDK.$wasdkVer"
$cppwinrt = "nupkgs/Microsoft.Windows.CppWinRT.$cppwinrtVer/bin/cppwinrt.exe"
# -in local merges the OS metadata (C:\Windows\System32\WinMetadata)
# so the Windows.Foundation types the Widgets winmd references are
# projected by the same cppwinrt version (mixing SDK-shipped headers
# with a different generator version trips base.h's version check).
& $cppwinrt -in "$wasdk/lib/uap10.0/Microsoft.Windows.Widgets.winmd" -in local -output winrt-gen
if ($LASTEXITCODE -ne 0) { throw "cppwinrt projection generation failed" }
if (!(Test-Path 'winrt-gen/winrt/Microsoft.Windows.Widgets.Providers.h')) {
throw "cppwinrt did not produce winrt/Microsoft.Windows.Widgets.Providers.h"
}
"CN1_WASDK_DIR=$wasdk" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8

- name: Widgets Board provider compile check (clang-cl)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
clang-cl --version
# The widgetboard TU deliberately avoids the cn1 headers (cn1_globals.h's
# C11 stdatomic conflicts with the C++ <atomic> the winrt headers pull) and
# compiles at C++20: C++/WinRT at C++17 falls back to
# <experimental/coroutine>, which the modern MSVC STL rejects under clang
# (STL1009). This mirrors the -DCMAKE_CXX_STANDARD=20 WindowsNativeBuilder
# passes for windows.msix=true builds.
clang-cl /c /std:c++20 /EHsc /DCN1_WIDGETBOARD=1 /Iwinrt-gen "/I$env:CN1_WASDK_DIR/include" `
Ports/WindowsPort/nativeSources/cn1_windows_widgetboard.cpp /Fowidgetboard.obj
if ($LASTEXITCODE -ne 0) { throw "cn1_windows_widgetboard.cpp failed to compile with CN1_WIDGETBOARD=1" }
# A translated app supplies the generated cn1_class_method_index.h; the
# floating-widget TU includes cn1_windows.h -> cn1_globals.h, which only
# needs this one constant from it here.
New-Item -ItemType Directory -Force stub-index | Out-Null
"#define cn1_array_start_offset 1000" | Set-Content stub-index/cn1_class_method_index.h -Encoding ascii
clang-cl /c /std:c++17 /EHsc /Istub-index /Ivm/ByteCodeTranslator/src `
Ports/WindowsPort/nativeSources/cn1_windows_widgets.cpp /Fowidgets.obj
if ($LASTEXITCODE -ne 0) { throw "cn1_windows_widgets.cpp failed to compile" }
Write-Host "Widgets Board provider + floating-widget TUs compiled clean."
12 changes: 12 additions & 0 deletions CodenameOne/src/com/codename1/background/BackgroundFetch.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
/// Currently background fetch is supported on iOS, Android, and in the Simulator (simulated using timers when the app is paused). You should
/// use the `com.codename1.ui.Display#isBackgroundFetchSupported()` method to find out if the current platform supports it.
///
/// Background Fetch and External Surfaces
///
/// Background fetch is the refresh engine of home-screen widgets: call
/// `com.codename1.surfaces.Surfaces#publish(java.lang.String, com.codename1.surfaces.WidgetTimeline)` from
/// `performBackgroundFetch(long, com.codename1.util.Callback)` to update published widget content while the
/// app UI is not running -- publish is callable from any thread, including this callback. On Android a
/// widget whose `atEnd` timeline ran out will itself trigger this callback (throttled) to pull fresh
/// content. See `com.codename1.surfaces.Surfaces` and the `com.codename1.surfaces.spi` package
/// documentation for the full background update story.
///
/// Examples
///
/// ```java
Expand Down Expand Up @@ -203,6 +213,8 @@ public interface BackgroundFetch {
/// - com.codename1.ui.Display.getPreferredBackgroundFetchInterval()
///
/// - com.codename1.ui.Display.isBackgroundFetchSupported()
///
/// - com.codename1.surfaces.Surfaces (publishing widget content from this callback)
void performBackgroundFetch(long deadline, Callback<Boolean> onComplete); // PMD Fix: UnnecessaryModifier removed

}
11 changes: 11 additions & 0 deletions CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java
Original file line number Diff line number Diff line change
Expand Up @@ -5846,6 +5846,17 @@ public boolean isCarConnected() {
return b != null && b.isConnected();
}

/// Returns the platform bridge used by the `com.codename1.surfaces` API to render external
/// surfaces (home-screen widgets and live activities). Ports supporting surfaces override
/// this; the base implementation returns null which renders the whole API an inert no-op.
///
/// #### Returns
///
/// the surface bridge, or null when unsupported
public com.codename1.surfaces.spi.SurfaceBridge getSurfaceBridge() {
return null;
}

/// True if the device is a foldable or dual screen device. False on the base implementation.
public boolean isFoldable() {
return false;
Expand Down
141 changes: 141 additions & 0 deletions CodenameOne/src/com/codename1/surfaces/LiveActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.surfaces;

import com.codename1.surfaces.spi.SurfaceBridge;

import java.util.LinkedHashMap;
import java.util.Map;

/// 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. Start it with a descriptor and an initial state, then push fresh state
/// maps as the situation evolves -- updates ship only the state, the layout is re-interpolated on
/// the surface:
///
/// ```java
/// LiveActivity delivery = LiveActivity.start(descriptor, initialState);
/// ...
/// delivery.update(stateMap("Arriving now", eta, 1.0f));
/// delivery.end(null);
/// ```
///
/// On platforms without live activity support `start(...)` returns an inert handle whose methods
/// are safe no-ops ([#isActive()] returns false), so app code needs no platform checks.
public final class LiveActivity {
private final String id;
private boolean active;

private LiveActivity(String id) {
this.id = id;
this.active = id != null;
}

/// Returns true when this platform can present live activities.
///
/// #### Returns
///
/// true when live activities are supported
public static boolean isSupported() {
SurfaceBridge b = Surfaces.bridgeInternal();
return b != null && b.isLiveActivitySupported();
}

/// Starts a live activity. On unsupported platforms (or when the platform refuses, e.g. the
/// user disabled live activities) this returns an inert handle rather than throwing.
///
/// #### Parameters
///
/// - `descriptor`: the activity layout and regions
/// - `initialState`: the initial state map, may be null
///
/// #### Returns
///
/// a handle to the running activity; check [#isActive()] to know whether it is live
public static LiveActivity start(LiveActivityDescriptor descriptor,
Map<String, Object> initialState) {
SurfaceBridge b = Surfaces.bridgeInternal();
if (b == null || !b.isLiveActivitySupported()) {
return new LiveActivity(null);
}
Map<String, byte[]> images = new LinkedHashMap<String, byte[]>();
String json = SurfaceSerializer.serializeLiveActivity(descriptor, initialState, images);
return new LiveActivity(b.startLiveActivity(json, images));
}

/// Pushes a fresh state map to the running activity. A no-op on an inert or ended handle.
///
/// #### Parameters
///
/// - `state`: the new state map
public void update(Map<String, Object> state) {
if (!active) {
return;
}
SurfaceBridge b = Surfaces.bridgeInternal();
if (b != null) {
b.updateLiveActivity(id, SurfaceSerializer.serializeState(state));
}
}

/// Ends the activity, optionally showing a final state before the platform dismisses the
/// surface. A no-op on an inert or already-ended handle.
///
/// #### Parameters
///
/// - `finalState`: the final state to show, or null to keep the last state
public void end(Map<String, Object> finalState) {
end(finalState, false);
}

/// Ends the activity.
///
/// #### Parameters
///
/// - `finalState`: the final state to show, or null to keep the last state
/// - `dismissImmediately`: true to remove the surface right away instead of letting the
/// platform linger on the final state
public void end(Map<String, Object> finalState, boolean dismissImmediately) {
if (!active) {
return;
}
active = false;
SurfaceBridge b = Surfaces.bridgeInternal();
if (b != null) {
b.endLiveActivity(id,
finalState == null ? null : SurfaceSerializer.serializeState(finalState),
dismissImmediately);
}
}

/// Returns true while the activity is running (false for inert handles and after `end`).
public boolean isActive() {
return active;
}

/// Returns the platform id of the activity, or null for inert handles. Action events from
/// this activity carry the descriptor's activity type as their source.
public String getId() {
return id;
}
}
Loading
Loading