Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
176 commits
Select commit Hold shift + click to select a range
f5863b4
Watch build: one entry point, no hints, and a watch app that reaches …
shai-almog Jul 29, 2026
dbb6ae5
Add com.codename1.wearable and make the simulator able to run a paire…
shai-almog Jul 29, 2026
4d070a3
Carry com.codename1.wearable over WatchConnectivity on Apple
shai-almog Jul 29, 2026
aeef641
Wear OS: Data Layer link, rotary input and round-screen safe area
shai-almog Jul 29, 2026
2398d09
Complications as surfaces watch families
shai-almog Jul 29, 2026
2151fc9
Rewrite the wearables guide around the two-app model
shai-almog Jul 29, 2026
ceb7835
Fix two LanguageTool findings in the wearables chapter
shai-almog Jul 29, 2026
3800ecc
Add the GPLv2 + Classpath Exception header to files this branch touches
shai-almog Jul 29, 2026
66234f9
Use HashMap rather than EnumMap in WidgetTimeline
shai-almog Jul 29, 2026
ab400fd
Use Integer/Long/Double.valueOf rather than the boxing constructors
shai-almog Jul 29, 2026
3014bc1
Fix the bugs raised in review
shai-almog Jul 29, 2026
8d1574a
Address the remaining review findings
shai-almog Jul 29, 2026
d4448c6
Advertise a wearable capability, and say when no Wear APK is produced
shai-almog Jul 29, 2026
b8a27f2
Fix the second round of review findings
shai-almog Jul 29, 2026
14ac12c
Keep the push helpers and the capability cache honest
shai-almog Jul 29, 2026
4930d72
Generate the watch skins at build time, and finish the transfer path
shai-almog Jul 29, 2026
b83880e
Pass -force when launching the simulated watch app
shai-almog Jul 29, 2026
86b0ffe
Keep transferred files on their own path, and stop leaking request or…
shai-almog Jul 29, 2026
65f1b1f
Pin the watch simulator slice to arm64
shai-almog Jul 29, 2026
3975b78
Keep the embedded watch app out of the Mac Catalyst variant
shai-almog Jul 29, 2026
64fecf5
Carry per-file compiler flags into the watch target
shai-almog Jul 30, 2026
cc8e8b2
Stop forcing -sdk and project-wide ARCHS on multi-target iOS builds
shai-almog Jul 30, 2026
777ddf6
Merge remote-tracking branch 'origin/master' into watch-apps-product
shai-almog Jul 30, 2026
ea15e13
Wearables: fix 19 review findings across both platforms
shai-almog Jul 30, 2026
15f70da
Wearables: state the cold-start queue's guarantee accurately
shai-almog Jul 30, 2026
146d51e
Wearables: document what the watch app actually runs today
shai-almog Jul 30, 2026
a73910f
Wearables: correct the watch icon comment
shai-almog Jul 30, 2026
9a2d202
Wearables: fix 8 follow-up findings, mostly from the previous round
shai-almog Jul 30, 2026
9c12707
Wearables: don't generate an empty widget bundle; guard two lifecycle…
shai-almog Jul 30, 2026
b1d9b34
Wearables: delimit the transfer namespace, keep multi-watch delivery …
shai-almog Jul 30, 2026
f7c2565
Wearables: fix the final nine review findings
shai-almog Jul 31, 2026
ace780f
Merge remote-tracking branch 'origin/master' into watch-apps-product
shai-almog Aug 1, 2026
a92645b
Wearables: four more review fixes, three in the simulator
shai-almog Aug 1, 2026
5cb1e37
Wearables: close eight more review findings, two security-relevant
shai-almog Aug 1, 2026
5694945
Wearables: correct the watch-main comparison and three more findings
shai-almog Aug 1, 2026
e6c97bf
Wearables: record the three watch distribution gaps in the guide
shai-almog Aug 1, 2026
29c38a2
Wearables: four more findings, including a dependency conflict
shai-almog Aug 1, 2026
5068da3
Wearables: stop the simulator stamp compounding; bound transfer bookk…
shai-almog Aug 1, 2026
48a671c
Wearables: scope the transfer sweep to this device; keep the capabili…
shai-almog Aug 1, 2026
f3088c1
Wearables: close the asset stream when a transfer read fails
shai-almog Aug 1, 2026
840e0fa
Wearables: order writes with a logical clock; address only nodes runn…
shai-almog Aug 1, 2026
97cdc95
Wearables: three consequences of the logical clock
shai-almog Aug 1, 2026
a911324
Wearables: break sequence ties on the publishing node
shai-almog Aug 1, 2026
d5e0a44
Wearables: one resolution path, and keep the stamp when resolution fails
shai-almog Aug 1, 2026
cdd642d
Wearables: record the surviving item's stamp outright after a deletion
shai-almog Aug 1, 2026
b05004e
Wearables: restore valueMap and payloadOf, deleted by the getData ref…
shai-almog Aug 1, 2026
ba71503
Wearables: retry local identity; keep transfer claims out of the valu…
shai-almog Aug 1, 2026
92624c6
Wearables: don't re-announce a value when a shadow replica is deleted
shai-almog Aug 1, 2026
000632e
Wearables: read capability readiness and nodes as one snapshot
shai-almog Aug 1, 2026
81f822a
Wearables: resolve the real winner on the first event after a restart
shai-almog Aug 1, 2026
81609cd
Wearables: read the capability cache atomically; retry winner resolution
shai-almog Aug 1, 2026
32460db
Wearables: version the capability cache; coalesce transfer sweeps
shai-almog Aug 1, 2026
ec6286b
Wearables: generation-guard the remaining two cache paths
shai-almog Aug 1, 2026
f7a5bd4
Wearables: guard the send-time refresh; unpin the watch simulator arch
shai-almog Aug 1, 2026
b69e37c
Wearables: guard the last unguarded cache write, and audit the set
shai-almog Aug 1, 2026
ae85394
Wearables: retry the deletion resolution too
shai-almog Aug 1, 2026
252ae90
Wearables: fix a main-thread block, a bypassed retry, and an unlocked…
shai-almog Aug 1, 2026
c03cd8b
Wearables: guard watch-excluded sources in the source, not in a build…
shai-almog Aug 2, 2026
e3080f2
Wearables: filter capability callbacks by name; fix headers and a sta…
shai-almog Aug 2, 2026
beb8e45
Watch suite: pin the simulator locale and timezone
shai-almog Aug 2, 2026
be5544a
iOS builds: stop passing per-SDK conditional settings on the xcodebui…
shai-almog Aug 2, 2026
10062b8
Merge remote-tracking branch 'origin/master' into watch-apps-product
shai-almog Aug 2, 2026
cf7d432
Wearables: defer winner resolution instead of delivering an unverifie…
shai-almog Aug 2, 2026
7306d51
Android port: restore master's line endings in the two files this bra…
shai-almog Aug 2, 2026
da6ae58
Merge remote-tracking branch 'origin/master' into watch-apps-product
shai-almog Aug 2, 2026
801722c
Health: keep the EDT contract when a listener attaches after completion
shai-almog Aug 2, 2026
a446e4c
Wearables: defer deletion resolution too, instead of going silent
shai-almog Aug 2, 2026
e0c66f9
BrowserComponent test: time out against the clock, and allow a loaded…
shai-almog Aug 2, 2026
9c7731d
Wear hints: standalone must not imply Wear mode; make the release ret…
shai-almog Aug 3, 2026
4df7d3a
Wearables: compare-and-replace the delivery stamp after a blocking re…
shai-almog Aug 3, 2026
f41e48c
Wearables: announce a replicated-path removal once, and never invent one
shai-almog Aug 4, 2026
62f73d9
Merge master; drop the EdtResult fix that master superseded
shai-almog Aug 5, 2026
5c59155
Wearables: don't lose deletion state when coalescing a pending resolu…
shai-almog Aug 5, 2026
7648ef0
iOS wearable: converge on ties, raise the clock from peer stamps, ser…
shai-almog Aug 5, 2026
67e5f25
Wearables: fix the capability resource path, link WatchConnectivity, …
shai-almog Aug 6, 2026
6968932
Wearables: order deliveries with their stamps, anchor the empty delet…
shai-almog Aug 6, 2026
13651b8
Wearables: unlock putData, and only persist a transfer claim once it …
shai-almog Aug 6, 2026
41fa8f4
Wearables: make the ordinary delivery atomic too, and prune claims by…
shai-almog Aug 6, 2026
b61a242
Wearables: stop echoing local writes to peer listeners, and stop bloc…
shai-almog Aug 6, 2026
f82aac1
Wearables: route the first-sight delivery through the atomic helper, …
shai-almog Aug 6, 2026
b7153ea
Wearables: finish the local-echo suppression and keep both snapshots …
shai-almog Aug 6, 2026
a1b0145
JavaSE wearable: escape dots, replace atomically, record the real sta…
shai-almog Aug 6, 2026
4ece7b1
Wearables: a local wildcard delete is local on every authority, and c…
shai-almog Aug 6, 2026
02260a4
Wearables: discard partial decodes, reject empty transfer paths, esca…
shai-almog Aug 6, 2026
df04dd2
Wearables: make the paired simulator actually pair, one watch process…
shai-almog Aug 6, 2026
7ab6948
Wearables: bound transfer retries by retention, end the removal windo…
shai-almog Aug 6, 2026
51a9e89
iOS wearable: deliver only real changes, acknowledge removals, prune …
shai-almog Aug 6, 2026
08b6a2d
Wearables: clear the removal marker on every publication path, consum…
shai-almog Aug 6, 2026
08cef01
Wearables: durability, provenance, bounded queues and the last orderi…
shai-almog Aug 6, 2026
dfe0eca
Wearables: durable received transfers, and accept cold-start events f…
shai-almog Aug 6, 2026
08d9cea
Use /// markdown for the MAX_PENDING doc
shai-almog Aug 6, 2026
881415e
Wearables: close the eight follow-ons from the durability and identit…
shai-almog Aug 6, 2026
2dd30d0
iOS wearable: prune tombstones when publishing, and never report our …
shai-almog Aug 6, 2026
15afd3c
Wearables: retry deletion resolution until it resolves, with no deadline
shai-almog Aug 6, 2026
06e1637
Wearables: confirm iOS inbox delivery, bound deletion retries, unbloc…
shai-almog Aug 6, 2026
420781c
Wearables: make the simulator's author label atomic, gate the socket,…
shai-almog Aug 6, 2026
cbef9ca
Wearables: protect one-shot transfers at the queue cap, reconcile a s…
shai-almog Aug 6, 2026
653c434
Wearables: release evicted transfers, stop double-draining the iOS inbox
shai-almog Aug 6, 2026
bc99964
Wearables: make every evicted transfer replayable, sweep on each item…
shai-almog Aug 6, 2026
5fef7b6
Wearables: replay relinquished iOS entries, put every expiry on its o…
shai-almog Aug 6, 2026
ba7140e
Wearables: drop a leftover bare block in pruneInto
shai-almog Aug 6, 2026
a362448
Wearables: copy payloads in and out of the value cache
shai-almog Aug 6, 2026
5226709
Wearables: reset ordering after a local deletion, retry a failed rete…
shai-almog Aug 6, 2026
80a94a3
Wearables: never confirm a delivery no listener saw; stamp from the s…
shai-almog Aug 6, 2026
25cc620
Wearables: anchor the local-delete reset, upgrade pending resolutions…
shai-almog Aug 6, 2026
db54a0d
Wearables: coalesce inbox replays, expire claims across restarts, fix…
shai-almog Aug 6, 2026
6e9c4d6
Wearables: derive the watch app's versions from ios.plistInject when …
shai-almog Aug 6, 2026
f8e4c3c
Wearables: union the optional-framework lists; fix two simulator sock…
shai-almog Aug 6, 2026
7902583
Wearables: one snapshot per scanned file; populate cold caches in the…
shai-almog Aug 6, 2026
0db4942
Wearables: upgrade pending resolvers unconditionally, retry capabilit…
shai-almog Aug 6, 2026
e502f3d
Wearables: keep the phone's bundle-version fallback; replay unclaimed…
shai-almog Aug 6, 2026
c9a505a
Wearables: keep removal in the empty branch, upgrade resolvers for ev…
shai-almog Aug 6, 2026
8d8bff5
Wearables: never replay without a local identity; real tombstone ages…
shai-almog Aug 6, 2026
837cb5a
Wearables: claims outlive the sender's deletion window; identity befo…
shai-almog Aug 6, 2026
c01705f
Wearables: fix an MRC over-release; keep replay and pruning alive as …
shai-almog Aug 6, 2026
56c3af7
Wearables: release priming markers on completion; keep claims while t…
shai-almog Aug 6, 2026
801e478
Wearables: put the writer's stamp in the frame; unify the claim rule;…
shai-almog Aug 6, 2026
9e50bce
Wearables: remember an authoritative absence
shai-almog Aug 6, 2026
00f49a4
Wearables: keep reading v1 frames; keep the removal marker when a fil…
shai-almog Aug 6, 2026
ef14917
Wearables: anchor the negative cache on the pre-query stamp
shai-almog Aug 6, 2026
09ddb07
Wearables: never persist a lower logical-clock floor
shai-almog Aug 6, 2026
c035f58
Wearables: prune claims only after the replay has examined what is st…
shai-almog Aug 6, 2026
92fc397
Wearables: refresh a transfer's claim before decoding it
shai-almog Aug 6, 2026
79f6028
Wearables: decide the iOS surface lowering while parsing the manifest
shai-almog Aug 6, 2026
fe95ad5
Wearables: give the Wear skins Android's theme, not the one from iPho…
shai-almog Aug 6, 2026
1dc24f8
Wearables: declare a standalone watch app watch-only
shai-almog Aug 6, 2026
d2e3d8f
Wearables: pair by role, coalesce the prune chain, forget evicted births
shai-almog Aug 6, 2026
8887e8e
Wearables: say what isSupported() actually answers
shai-almog Aug 6, 2026
bf0a665
Wearables: never expire a claim without checking the item is gone
shai-almog Aug 6, 2026
70db95c
Wearables: correct the last two isSupported() descriptions
shai-almog Aug 6, 2026
027c336
Wearables: give live messages their own worker thread
shai-almog Aug 6, 2026
8528f7a
Wearables: remove the last age-only claim prune
shai-almog Aug 6, 2026
4a8b43e
Wearables: withdraw the local-removal marker when the delete fails
shai-almog Aug 6, 2026
e7da86d
Wearables: don't read a not-yet-run query as "not paired"; document t…
shai-almog Aug 6, 2026
f6124c3
Wearables: advance the paths generation even before a snapshot exists
shai-almog Aug 6, 2026
de799da
Wearables: stop pretending isPaired() can answer on a cold start
shai-almog Aug 6, 2026
2ce83bc
Wearables: stop re-announcing an announced tombstone; one deadline sweep
shai-almog Aug 6, 2026
488cc63
Wearables: replay durable values at startup too, and bound the negati…
shai-almog Aug 6, 2026
5360832
Wearables: keep the startup value replay off local writes and let it …
shai-almog Aug 6, 2026
e46656a
Wearables: re-decide a tombstone acknowledgement under the lock; drop…
shai-almog Aug 6, 2026
9120938
Wearables: replay removals across a restart, and don't orphan the wat…
shai-almog Aug 6, 2026
8ac177d
Wearables: fix four faults in the tombstone work
shai-almog Aug 6, 2026
58541f7
Wearables: record a suppressed local winner by the same rules as a di…
shai-almog Aug 6, 2026
2261799
Wearables: coalesce parked deliveries by path; give the watch every p…
shai-almog Aug 6, 2026
a21d6aa
Wearables: remove the simulator tombstone before publishing its repla…
shai-almog Aug 6, 2026
09184f4
Wearables: hand a discarded delivery back to the port; two simulator …
shai-almog Aug 6, 2026
d54a385
Wearables: give iOS a recovery handler, bound the dropped set, two to…
shai-almog Aug 6, 2026
7fa88a7
Wearables: actually re-offer a forgotten iOS value; abort a removal t…
shai-almog Aug 6, 2026
22cbe53
Wearables: ask for a rescan when recovery overflows; serialize the iO…
shai-almog Aug 6, 2026
751ca26
Wearables: recover a dropped removal as a removal; translate the loca…
shai-almog Aug 6, 2026
8f3a66b
Wearables: recover a discarded removal in core, where the information…
shai-almog Aug 6, 2026
15c08af
Wearables: forget a tombstone's birth only once the publish succeeds
shai-almog Aug 6, 2026
089bdd5
Wearables: say when a removal passes beyond recovery; keep an older t…
shai-almog Aug 6, 2026
9fcefeb
Wearables: drop only the simulator connection whose write actually fa…
shai-almog Aug 6, 2026
c91066e
Wearables: fix a 16-bit count that erased large messages; mirror inje…
shai-almog Aug 6, 2026
58d446d
Wearables: resolve watch purpose strings once, from both sources
shai-almog Aug 6, 2026
2a046e1
Wearables: decode injected XML entities before re-emitting them
shai-almog Aug 6, 2026
b9ff0ba
Wearables: keep a simulator tombstone until the peer has seen it
shai-almog Aug 6, 2026
fb99556
Wearables: decode numeric XML references, one HealthKit verdict
shai-almog Aug 6, 2026
04dc680
Wearables: one HealthKit expression, honour the false opt-out
shai-almog Aug 6, 2026
4e1cbc3
Wearables: count the base HealthKit entitlement as usage
shai-almog Aug 6, 2026
aa5c3af
Wearables: announce deferred first-sight removals, bound the sentinels
shai-almog Aug 6, 2026
2126715
Wearables: tie a tombstone acknowledgement to the tombstone
shai-almog Aug 6, 2026
ea6883e
Wearables: leave a sentinel after a stamped removal too
shai-almog Aug 6, 2026
ff217aa
Wearables: an empty acknowledgement confirms nothing
shai-almog Aug 6, 2026
a97128b
Wearables: four ordering and retention fixes
shai-almog Aug 6, 2026
b5d01df
Wearables: tombstone housekeeping runs, and retires the eldest
shai-almog Aug 6, 2026
3c0abc3
Wearables: unit-test mode outranks the standalone watch lifecycle
shai-almog Aug 6, 2026
591ee93
Wearables: do not retire a tombstone that has been replaced
shai-almog Aug 6, 2026
0b81727
Wearables: keep the acknowledgement when the tombstone will not delete
shai-almog Aug 6, 2026
0b7cff4
Wearables: one retention deadline, not one per transfer
shai-almog Aug 6, 2026
c9511a8
Wearables: an unreadable acknowledgement is not an unframed tombstone
shai-almog Aug 6, 2026
c36ab95
Wearables: clear two SpotBugs findings the outage kept hidden
shai-almog Aug 6, 2026
e006427
Wearables: clear the PMD gate, repark a delivery with no listener
shai-almog Aug 7, 2026
4bf4cdb
Wearables: a republication cancels its own stale recovery record
shai-almog Aug 7, 2026
08aeb87
Wearables: order deliveries against the backlog, not against timing
shai-almog Aug 7, 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
14 changes: 12 additions & 2 deletions .github/workflows/ios-packaging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,18 @@ jobs:
| sort | xargs shasum -a 256 | shasum -a 256 | awk '{print $1}')
POM_HASH=$(find . -name 'pom.xml' -not -path './scripts/*' 2>/dev/null \
| sort | xargs shasum -a 256 | shasum -a 256 | awk '{print $1}')
# The build INVOCATION belongs in the key, not just the sources. Derived data
# embeds the SDK, destination and per-target product layout an invocation
# produced; restoring a tree built by a different one leaves Xcode's build
# database claiming targets are up to date when their products are absent
# ("Build input file cannot be found ... output of a script phase").
SCRIPT_HASH=$(shasum -a 256 \
scripts/setup-workspace.sh \
scripts/build-ios-port.sh \
scripts/build-native-themes.sh \
scripts/build-ios-app.sh \
scripts/run-ios-device-release-build.sh \
scripts/run-ios-ui-tests.sh \
.github/workflows/_build-ios-port.yml \
| shasum -a 256 | awk '{print $1}')
echo "hash=${SRC_HASH:0:16}-${POM_HASH:0:16}-${SCRIPT_HASH:0:16}" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -165,8 +173,10 @@ jobs:
with:
path: ${{ runner.temp }}/cn1-ios-device-release-derived
key: ${{ runner.os }}-ios-device-release-derived-${{ steps.src_hash.outputs.hash }}
restore-keys: |
${{ runner.os }}-ios-device-release-derived-
# Deliberately no restore-keys prefix. A partial match here restores derived
# data produced by a DIFFERENT build invocation, and a stale Xcode build
# database is worse than a cold compile: it reports success for targets it
# skipped. An exact hit or nothing.

- name: Build Release iOS device app without signing
env:
Expand Down
12 changes: 12 additions & 0 deletions CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java
Original file line number Diff line number Diff line change
Expand Up @@ -6000,6 +6000,18 @@ public boolean isCarConnected() {
return b != null && b.isConnected();
}

/// Returns the platform bridge that carries the `com.codename1.wearable` phone-to-watch API over
/// the native transport (Apple's `WCSession` / Google's Wearable Data Layer), or null when this
/// device has no wearable counterpart (the base implementation). When null, the
/// `com.codename1.wearable` API degrades to a harmless no-op.
///
/// #### Returns
///
/// the wearable bridge, or null when unsupported
public com.codename1.wearable.spi.WearableBridge getWearableBridge() {
return null;
}

/// 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.
Expand Down
1 change: 1 addition & 0 deletions CodenameOne/src/com/codename1/impl/health/EdtResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,5 @@ void superComplete(T value) {
void superError(Throwable t) {
super.error(t);
}

}
18 changes: 13 additions & 5 deletions CodenameOne/src/com/codename1/surfaces/SurfaceSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ public final class SurfaceSerializer {
private SurfaceSerializer() {
}

/// True when the timeline carries a layout for at least one size family. Iterating the enum
/// rather than naming families keeps this correct as the catalog grows -- the watch
/// complication families joined it without touching this method.
private static boolean hasAnyExplicitContent(WidgetTimeline timeline) {
for (WidgetSize size : WidgetSize.values()) {
if (timeline.getExplicitContent(size) != null) {
return true;
}
}
return false;
}

/// Serializes a widget timeline.
///
/// #### Parameters
Expand All @@ -63,11 +75,7 @@ private SurfaceSerializer() {
/// the timeline JSON
public static String serializeTimeline(String kindId, WidgetTimeline timeline,
Map<String, byte[]> imagesOut) {
if (timeline.getDefaultContent() == null
&& timeline.getContent(WidgetSize.SMALL) == null
&& timeline.getContent(WidgetSize.MEDIUM) == null
&& timeline.getContent(WidgetSize.LARGE) == null
&& timeline.getContent(WidgetSize.LOCKSCREEN) == null) {
if (timeline.getDefaultContent() == null && !hasAnyExplicitContent(timeline)) {
throw new IllegalArgumentException("A widget timeline needs content: call "
+ "setContent(...) before publishing");
}
Expand Down
65 changes: 60 additions & 5 deletions CodenameOne/src/com/codename1/surfaces/WidgetSize.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,41 @@
*/
package com.codename1.surfaces;

/// The size families a widget kind supports. iOS maps these to the WidgetKit families
/// (`systemSmall` / `systemMedium` / `systemLarge` and `accessoryRectangular` for `LOCKSCREEN`);
/// Android and desktop treat them as size hints. `LOCKSCREEN` is ignored on Android in this
/// version.
/// The size families a widget kind supports.
///
/// The first four are the phone families: iOS maps them to the WidgetKit families
/// (`systemSmall` / `systemMedium` / `systemLarge`, and `accessoryRectangular` for `LOCKSCREEN`);
/// Android and desktop treat them as size hints, and `LOCKSCREEN` is ignored on Android.
///
/// The `WATCH_*` families are **complications** -- the small live readouts on a watch face. They
/// live here rather than in an API of their own because they are the same concept as a widget:
/// content-driven, rendered while your app is not running, and fed by the same [WidgetTimeline]. On
/// Apple a complication is literally a WidgetKit widget in an accessory family; on Wear OS the
/// simple families become complication data and the richer ones become a Tile.
///
/// Design them for a glance. A complication is a few dozen pixels someone reads in under a second,
/// so a `SurfaceVector` gauge or a single number beats any layout that has to be read.
public enum WidgetSize {
/// Small square home-screen widget. iOS `systemSmall`.
SMALL("small"),
/// Medium home-screen widget. iOS `systemMedium`.
MEDIUM("medium"),
/// Large home-screen widget. iOS `systemLarge`.
LARGE("large"),
LOCKSCREEN("lockscreen");
/// Lock-screen widget. iOS `accessoryRectangular`.
LOCKSCREEN("lockscreen"),
/// Round complication -- the corner or centre slots of a watch face. iOS `accessoryCircular`;
/// Wear OS `RANGED_VALUE` or `MONOCHROMATIC_IMAGE`. Room for a gauge or one glyph.
WATCH_CIRCULAR("watchCircular"),
Comment thread
shai-almog marked this conversation as resolved.
/// Wide complication, a band across the watch face. iOS `accessoryRectangular`; Wear OS
/// `LONG_TEXT`, or a Tile when the layout is richer than text. The roomiest family.
WATCH_RECTANGULAR("watchRectangular"),
/// One line of text alongside the time. iOS `accessoryInline`; Wear OS `SHORT_TEXT`. Text only --
/// anything else is dropped.
WATCH_INLINE("watchInline"),
/// Curved complication hugging the bezel of a round face. iOS `accessoryCorner`; renders as the
/// circular family on Wear OS, which has no corner slot.
WATCH_CORNER("watchCorner");

private final String jsonName;

Expand All @@ -42,4 +68,33 @@ public enum WidgetSize {
public String getJsonName() {
return jsonName;
}

/// True for the watch complication families, which are published to a watch face rather than to
/// a home or lock screen.
///
/// #### Returns
///
/// true if this is a complication family
public boolean isWatchFamily() {
Comment thread
shai-almog marked this conversation as resolved.
return this == WATCH_CIRCULAR || this == WATCH_RECTANGULAR
|| this == WATCH_INLINE || this == WATCH_CORNER;
}

/// Resolves a wire-format name back to its family.
///
/// #### Parameters
///
/// - `jsonName`: the name produced by [#getJsonName()]
///
/// #### Returns
///
/// the matching family, or null when the name is unknown
public static WidgetSize fromJsonName(String jsonName) {
for (WidgetSize s : values()) {
if (s.jsonName.equals(jsonName)) {
return s;
}
}
return null;
}
}
62 changes: 14 additions & 48 deletions CodenameOne/src/com/codename1/surfaces/WidgetTimeline.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ public Map<String, Object> getState() {
}

private SurfaceNode defaultContent;
private SurfaceNode smallContent;
private SurfaceNode mediumContent;
private SurfaceNode largeContent;
private SurfaceNode lockscreenContent;
/// Per-family layout overrides. A map rather than a field per family: the catalog grows (the
/// watch accessory families joined the phone ones) and a switch per accessor did not. A plain
/// HashMap rather than an EnumMap -- the Codename One runtime has no EnumMap, and lookups here
/// are by key so the ordering an EnumMap would give buys nothing.
private final Map<WidgetSize, SurfaceNode> overrides =
new java.util.HashMap<WidgetSize, SurfaceNode>();
private final List<Entry> entries = new ArrayList<Entry>();
private int reloadPolicy = RELOAD_AT_END;

Expand Down Expand Up @@ -105,21 +107,12 @@ public WidgetTimeline setContent(SurfaceNode root) {
///
/// this timeline, for chaining
public WidgetTimeline setContent(WidgetSize size, SurfaceNode root) {
switch (size) {
case SMALL:
smallContent = root;
break;
case MEDIUM:
mediumContent = root;
break;
case LARGE:
largeContent = root;
break;
case LOCKSCREEN:
lockscreenContent = root;
break;
default:
break;
if (size != null) {
if (root == null) {
overrides.remove(size);
} else {
overrides.put(size, root);
}
}
return this;
}
Expand Down Expand Up @@ -172,41 +165,14 @@ public WidgetTimeline setReloadPolicy(int policy) {
///
/// the layout root, or null when neither an override nor a default was set
public SurfaceNode getContent(WidgetSize size) {
SurfaceNode override = null;
switch (size) {
case SMALL:
override = smallContent;
break;
case MEDIUM:
override = mediumContent;
break;
case LARGE:
override = largeContent;
break;
case LOCKSCREEN:
override = lockscreenContent;
break;
default:
break;
}
SurfaceNode override = size == null ? null : overrides.get(size);
return override != null ? override : defaultContent;
}

/// Returns the explicit per-size override, or null when the size family falls back to the
/// default content. Used by the serializer so only real overrides are emitted per size.
SurfaceNode getExplicitContent(WidgetSize size) {
switch (size) {
case SMALL:
return smallContent;
case MEDIUM:
return mediumContent;
case LARGE:
return largeContent;
case LOCKSCREEN:
return lockscreenContent;
default:
return null;
}
return size == null ? null : overrides.get(size);
}

/// Returns the layout used for size families without an explicit override, or null.
Expand Down
12 changes: 12 additions & 0 deletions CodenameOne/src/com/codename1/ui/Display.java
Original file line number Diff line number Diff line change
Expand Up @@ -4713,6 +4713,18 @@ public com.codename1.car.spi.CarBridge getCarBridge() {
return impl.getCarBridge();
}

/// Returns the platform bridge used by the `com.codename1.wearable` API to talk to the
/// counterpart watch or phone app, or null when this device has no wearable counterpart.
/// Internal -- application code uses the `com.codename1.wearable` API rather than this bridge
/// directly.
///
/// #### Returns
///
/// the wearable bridge, or null
public com.codename1.wearable.spi.WearableBridge getWearableBridge() {
return impl.getWearableBridge();
}

/// Returns the platform bridge used by the `com.codename1.surfaces` API to render external
/// surfaces (home-screen widgets and live activities), or null when unsupported on this port.
/// Internal -- application code uses the `com.codename1.surfaces` API rather than this bridge
Expand Down
Loading
Loading