Skip to content

Simulator: data-driven menu hooks for cn1libs#4988

Open
shai-almog wants to merge 9 commits into
masterfrom
feat/simulator-menu-hooks
Open

Simulator: data-driven menu hooks for cn1libs#4988
shai-almog wants to merge 9 commits into
masterfrom
feat/simulator-menu-hooks

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Adds SimulatorHookLoader to the JavaSE port: cn1libs ship a META-INF/codenameone/simulator-hooks.properties file and get a menu in the simulator's menu bar, with no Swing types in the contract.
  • JavaSEPort.installMenu consumes the neutral hook list and emits JMenu/JMenuItems today — replaceable when the simulator UX is rewritten without breaking any cn1lib.
  • JUnit tests cover the parser end-to-end.
  • New section in docs/developer-guide/Maven-Creating-CN1Libs.adoc documents the contract with the cn1-bluetooth cn1lib as a worked example.

Contract

# META-INF/codenameone/simulator-hooks.properties
name=Bluetooth

item1.label=Add demo peripheral
item1.action=com.example.bt.sim.Hooks#addDemoPeripheral

Actions are fqcn#staticMethod references to public static void no-arg methods. The loader resolves them via Display.class.getClassLoader() and pre-binds a Runnable that dispatches on the CN1 EDT via Display.callSerially. Multiple cn1libs each contribute one menu; discovery uses ClassLoader.getResources() so they merge cleanly.

The neutral SimulatorHook record (menuName, label, Runnable) is the contract; today's Swing menu rendering is just one consumer.

Test plan

  • mvn test -pl maven/javase — 7 new tests, all pass:
    • parses well-formed file
    • preserves declaration order
    • skips file without name=
    • skips item without matching .action
    • skips unknown class but keeps rest
    • skips non-static method
    • skips malformed action string
  • End-to-end smoke test: running BTDemo in the simulator with the cn1-bluetooth lib on the classpath shows a "Bluetooth" menu with all of the lib's items; clicking each fires the matching static method on the CN1 EDT.
  • Existing simulator-tests CI (mvn cn1:test) still passes — the loader is additive and the menu construction path is unchanged for the existing three menus.

Companion change

The cn1-bluetooth cn1lib at codenameone/bluetoothle-codenameone has a companion PR using this mechanism. The lib's CI builds CN1 from source via a composite action so once this lands the lib's PR can be merged on top.

🤖 Generated with Claude Code

Adds a small framework feature that lets cn1libs contribute items to the
JavaSE simulator's menu bar via a properties file, without referencing
any Swing types. Each cn1lib drops a
META-INF/codenameone/simulator-hooks.properties on its classpath:

    name=Bluetooth
    item1.label=Add demo peripheral
    item1.action=com.example.bt.sim.Hooks#addDemoPeripheral

The new SimulatorHookLoader scans every jar on the simulator classpath
via getResources(), parses the file, resolves each action's static
method against the classloader that loaded Display, and pre-binds a
Runnable that dispatches on the CN1 EDT. JavaSEPort.installMenu groups
the result by menu name and renders one JMenu per group between the
existing menus and the Help menu.

Why data-driven instead of a Java SPI: the simulator UX is going to be
rewritten and we don't want cn1libs to depend on JMenu/JMenuItem (or
have to be recompiled when the UX shape changes). The neutral
SimulatorHook record (menuName, label, Runnable) is the contract; the
UI shell on top is replaceable.

Tests in maven/javase cover well-formed parsing, declaration-order
preservation, and skip-on-error for every malformed case
(missing name, dangling label, unknown class, non-static target,
malformed action string).

Documentation lives in docs/developer-guide/Maven-Creating-CN1Libs.adoc
with the cn1-bluetooth lib as a worked example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 19, 2026

Compared 11 screenshots: 11 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@github-actions
Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 19, 2026

Compared 20 screenshots: 20 matched.
✅ JavaScript-port screenshot tests passed.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Image references: No unused images detected (report)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 19, 2026

Compared 110 screenshots: 110 matched.

Native Android coverage

  • 📊 Line coverage: 11.81% (6573/55655 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.52% (33008/346569), branch 4.12% (1355/32925), complexity 5.17% (1632/31558), method 9.00% (1328/14749), class 15.07% (301/1998)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.81% (6573/55655 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.52% (33008/346569), branch 4.12% (1355/32925), complexity 5.17% (1632/31558), method 9.00% (1328/14749), class 15.07% (301/1998)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 907.000 ms
Base64 CN1 encode 200.000 ms
Base64 encode ratio (CN1/native) 0.221x (77.9% faster)
Base64 native decode 745.000 ms
Base64 CN1 decode 203.000 ms
Base64 decode ratio (CN1/native) 0.272x (72.8% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 19, 2026

Compared 110 screenshots: 110 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 365 seconds

Build and Run Timing

Metric Duration
Simulator Boot 88000 ms
Simulator Boot (Run) 1000 ms
App Install 14000 ms
App Launch 9000 ms
Test Execution 297000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 776.000 ms
Base64 CN1 encode 2030.000 ms
Base64 encode ratio (CN1/native) 2.616x (161.6% slower)
Base64 native decode 375.000 ms
Base64 CN1 decode 2116.000 ms
Base64 decode ratio (CN1/native) 5.643x (464.3% slower)
Base64 SIMD encode 821.000 ms
Base64 encode ratio (SIMD/native) 1.058x (5.8% slower)
Base64 encode ratio (SIMD/CN1) 0.404x (59.6% faster)
Base64 SIMD decode 591.000 ms
Base64 decode ratio (SIMD/native) 1.576x (57.6% slower)
Base64 decode ratio (SIMD/CN1) 0.279x (72.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 85.000 ms
Image createMask (SIMD on) 14.000 ms
Image createMask ratio (SIMD on/off) 0.165x (83.5% faster)
Image applyMask (SIMD off) 317.000 ms
Image applyMask (SIMD on) 81.000 ms
Image applyMask ratio (SIMD on/off) 0.256x (74.4% faster)
Image modifyAlpha (SIMD off) 170.000 ms
Image modifyAlpha (SIMD on) 77.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.453x (54.7% faster)
Image modifyAlpha removeColor (SIMD off) 201.000 ms
Image modifyAlpha removeColor (SIMD on) 122.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.607x (39.3% faster)
Image PNG encode (SIMD off) 1561.000 ms
Image PNG encode (SIMD on) 1283.000 ms
Image PNG encode ratio (SIMD on/off) 0.822x (17.8% faster)
Image JPEG encode 819.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 19, 2026

Compared 109 screenshots: 109 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 203 seconds

Build and Run Timing

Metric Duration
Simulator Boot 67000 ms
Simulator Boot (Run) 2000 ms
App Install 17000 ms
App Launch 8000 ms
Test Execution 331000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1340.000 ms
Base64 CN1 encode 2002.000 ms
Base64 encode ratio (CN1/native) 1.494x (49.4% slower)
Base64 native decode 548.000 ms
Base64 CN1 decode 1428.000 ms
Base64 decode ratio (CN1/native) 2.606x (160.6% slower)
Base64 SIMD encode 587.000 ms
Base64 encode ratio (SIMD/native) 0.438x (56.2% faster)
Base64 encode ratio (SIMD/CN1) 0.293x (70.7% faster)
Base64 SIMD decode 556.000 ms
Base64 decode ratio (SIMD/native) 1.015x (1.5% slower)
Base64 decode ratio (SIMD/CN1) 0.389x (61.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 239.000 ms
Image createMask (SIMD on) 15.000 ms
Image createMask ratio (SIMD on/off) 0.063x (93.7% faster)
Image applyMask (SIMD off) 309.000 ms
Image applyMask (SIMD on) 239.000 ms
Image applyMask ratio (SIMD on/off) 0.773x (22.7% faster)
Image modifyAlpha (SIMD off) 404.000 ms
Image modifyAlpha (SIMD on) 61.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.151x (84.9% faster)
Image modifyAlpha removeColor (SIMD off) 397.000 ms
Image modifyAlpha removeColor (SIMD on) 363.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.914x (8.6% faster)
Image PNG encode (SIMD off) 1148.000 ms
Image PNG encode (SIMD on) 991.000 ms
Image PNG encode ratio (SIMD on/off) 0.863x (13.7% faster)
Image JPEG encode 890.000 ms

CI's vale gate flagged 10 prose issues in the simulator-menu-hooks
section: "backend" hits Microsoft.Avoid (treated as a noise token
since it's our standard term for a swappable implementation),
"e.g.", a missing Oxford-style comma inside quoted enumerations, a
stray "freely" adverb, heading punctuation, and "do not" instead
of "don't". Adds "[Bb]ackend" to the project vocabulary and rewrites
the affected sentences. No semantic change to the section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog added a commit to codenameone/bluetoothle-codenameone that referenced this pull request May 19, 2026
…latform plumbing

Every cn1lib CI job failed on its first run with a mix of issues that
all map to the same shape: the OS-activated native-helper Maven
profiles tried to do too much, and the framework setup composite
action didn't actually work cross-OS.

Concretely:

- The OS-activated profiles (linux-native-ble-helper, etc.) fired
  whenever a build ran on the matching OS, so the simulator-tests,
  ios-native-tests, and android-native-tests jobs all tried to
  compile the Rust helper too. Those runners don't have cargo (or
  libdbus-1-dev on Linux) installed, and they don't need the helper.
  Each profile now also requires `!skipNativeBleHelper` to activate,
  and every workflow / script that builds the cn1lib but doesn't
  need the helper passes -DskipNativeBleHelper=true.

- Composite action used a hard-coded /tmp path. Windows runners
  fail with "directory name is invalid" because Git Bash there
  doesn't see a real Unix /tmp. Swapped to $RUNNER_TEMP throughout.

- Composite action requested JDK 8 from Temurin, which doesn't ship
  Apple-Silicon JDK 8 binaries; macos-14 jobs failed with "Could
  not find satisfied version for SemVer '8'". Switched to Zulu.

- device-test.yml never ran the framework setup; it tried to fetch
  codenameone-maven-plugin 8.0-SNAPSHOT from Central. Wired in the
  composite action and the skip flag.

- TEMPORARY: composite's default cn1-ref is feat/simulator-menu-hooks
  (the framework PR's branch). Once codenameone/CodenameOne#4988
  merges, flip back to 'master'. Comment in the action calls this
  out so the followup isn't forgotten.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

shai-almog and others added 2 commits May 20, 2026 06:42
Original feedback: CN1 tests live in the cross-platform common/
project and can't import JavaSE-port classes (no reflection, no
JavaSE-only types). The previous design forced tests to either
reflect on SimulatorHookLoader or directly instantiate cn1lib
internals, both of which break on iOS/Android/JavaScript.

This commit adds the missing piece: every hook gets a stable
`namespace:id` identifier and is registered with a new core class,
SimulatorHookExecutor. CN.executeHook(String hookId) delegates to
that executor and returns false on platforms where the registry is
empty (i.e., every non-simulator target). Tests in common/ can
drive simulator-only behavior with one cross-platform call:

    CN.executeHook("bluetooth:addDemoPeripheral");

Also lifts the menu-label restriction: hooks may now declare an
id and action without a label, in which case they are registered
with the executor (callable from tests) but hidden from the
simulator's menu. Useful for test fixture scaffolding ("seed N
peripherals", "prime next-call failure") that would clutter the
menu UX.

Properties-file grammar additions:

  namespace=<token>     # defaults to slugified `name`
  itemN.id=<token>      # defaults to the property key (item1, item2)
  itemN.label=...       # NOW OPTIONAL; absent = API-only hook

Documentation in Maven-Creating-CN1Libs.adoc updated with the new
shape and a CN.executeHook test example.

12 JUnit tests on the framework parser pass; existing menu rendering
is unchanged for any hook that ships a label.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The action wrapper used Display.callSerially, which is fire-and-forget.
That broke CN.executeHook callers running off the EDT (every CN1
UnitTest's runTest()) — they'd assert state changes before the EDT
had run the action and false-fail.

Switched to Display.callSeriallyAndWait. From the EDT the body runs
inline (CN1's existing semantics); from any other thread the call
blocks until the action completes. CN.executeHook now returns true
only after the hook has actually executed, so tests can immediately
assert on the side effects.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog added a commit to codenameone/bluetoothle-codenameone that referenced this pull request May 20, 2026
Rewrite BluetoothSimulatorHooksTest to call hooks by id through
CN.executeHook("bluetooth:<id>") instead of importing
com.codename1.impl.javase.simulator.SimulatorHook{,Loader} directly.
The new pattern works unchanged in cn1lib-using apps where the test
lives in the cross-platform common/ project — no JavaSE-port
imports, no reflection.

simulator-hooks.properties:
  - explicit namespace=bluetooth
  - explicit item.id for every hook (matches the executor key shape)
  - new item8 = primeReadFailure: a label-less hook the test uses
    to script a one-shot read failure. Demonstrates the API-only
    branch (no menu entry, still callable from tests).

BluetoothSimulatorHooks gains primeReadFailure() which delegates to
BluetoothSimulator.failNext("read", ...). Other existing hooks are
unchanged; the only state asserted from BluetoothSimulator is via
its public testing API (isEnabled, registeredPeripheralCount,
isPeripheralRegistered), which has always been part of the lib.

Depends on codenameone/CodenameOne#4988 latest commit (the
callSeriallyAndWait fix that makes CN.executeHook synchronous from
off-EDT callers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog and others added 5 commits May 20, 2026 07:12
The framework's docs-style gate (build-test JDK 8/17/21) rejects
classic /** Javadoc markers in core/CLDC classes — CN1 standardized
on Java 25 /// markdown comments instead. Convert SimulatorHookExecutor
to the project style. No semantic change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous design added CN.executeHook + nested itemN.id/.label/.action
keys. Both miss the mark:

- We already have CN/Display.execute(String url) for native dispatch
  on every platform; the JavaSE port can intercept hook urls before
  handing the rest to the OS browser. Adding executeHook duplicates
  that surface and forces app code to learn a second method.

- Items are positional: item1 is the first menu entry, item2 the
  second. The previous design treated itemN as an opaque pairing
  token (any string worked) and allowed reordering — that's wrong;
  the simulator UX renders in numeric order and the loader should
  too. The loop now stops at the first missing itemN, matching
  what the user-facing menu would do.

- The compound key syntax (itemN.label, itemN.action, itemN.id) is
  redundant when the namespace is already in the file's `name`/
  `namespace` field. Replaced with parallel arrays: itemN holds
  the action, labelN holds the label.

Final shape:

    name=Bluetooth
    namespace=bluetooth          # optional; defaults to slugified `name`

    item1=fqcn#method            # required; the Nth menu item's action
    label1=Toggle adapter        # optional; if absent, hook is API-only

    item2=fqcn#method2
    label2=Add demo peripheral

    # No labelN -> registered with the executor but not in the menu
    item3=fqcn#primeReadFailure

JavaSEPort.execute intercepts urls matching a registered hook (key
shape: "namespace:itemN") and routes them through the existing
SimulatorHookExecutor; non-matching urls fall through to the
browser launcher. JavaSEPort.canExecute reports TRUE for registered
hook urls so tests can guard cross-platform.

SimulatorHookExecutor stays in core. Tests use CN.execute(...) plus
CN.canExecute(...) as a "are we in a simulator?" gate; no
JavaSE-only imports needed. 12 JUnit tests pin the positional loop,
slugify rules, gap-stops behavior, executor registration, and the
API-only branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Android port's javac uses ASCII encoding (see feedback memory),
so any non-ASCII character in a .java file under CodenameOne/ or
Ports/JavaSE/ trips "unmappable character" errors in build-test
(8/17). The redesign sneaked em-dashes ('--'), arrows ('->') and a
right single quote into a handful of new/edited files. Sweep them
out: every modified .java is now pure ASCII.

Also drops a stray "e.g." from the dev guide section that Vale's
Microsoft.Foreign rule flags; replaced with "for example".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PMD's AvoidUsingVolatile rule (enforced by the build-test JDK 8
"Generate static analysis HTML summaries" step) flagged the
`private static volatile Map<String, Runnable> hooks` field. The
semantics we want -- safe replacement of the registry from the
JavaSE port while readers see either the old or new map atomically
-- map cleanly to AtomicReference, which avoids the volatile keyword
and keeps PMD happy. No observable behavior change.

12 framework JUnit tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rence)

The volatile-vs-AtomicReference choice was a false binary:

- volatile: trips PMD's AvoidUsingVolatile gate on the JDK 8 PR CI run.
- AtomicReference: triggers "package java.util.concurrent.atomic does
  not exist" in the JavaSE port's javase-simulator-tests Ant step,
  which compiles the framework core against the CLDC subset. CLDC
  doesn't ship j.u.c.atomic.

Both gates fail on the new class. The portable third option is plain
synchronized accessors guarding the registry field with a private
lock object -- same memory-visibility guarantee, compiles under
every supported target, no PMD warning. Hook actions still run
OUTSIDE the lock so a long-running hook can't deadlock a concurrent
register() call. 12 framework JUnit tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shai-almog added a commit to codenameone/bluetoothle-codenameone that referenced this pull request May 20, 2026
Previous version of this PR depended on CN1 8.0-SNAPSHOT to pick up
the simulator hook intercept added in codenameone/CodenameOne#4988.
That's wrong for a cn1lib: the framework feature isn't released yet,
and tying this PR to an in-flight master commit means everything
downstream (CI, anyone reviewing) has to also build CN1 from source.

Reverting the cn1lib to the current release (7.0.243):

- pom.xml: cn1.version / cn1.plugin.version = 7.0.243.
- Drop the local-snapshots repository entry and the
  setup-cn1-framework composite action that cloned + built CN1
  master on every CI job. The workflows now use the same plain
  curl-the-build-client pattern they had pre-PR.
- ios-native-tests no longer needs continue-on-error: the iOS
  codegen on the released line generates the same dispatch shim
  the bridge has always been targeting (it worked under 7.0.71 and
  carries through 7.0.243), so the lib's ObjC selector naming
  (param1/param2/...) compiles cleanly. The 8.0-SNAPSHOT clash was
  upstream churn, not a bridge bug.
- cn1-framework-pin.txt removed.

The new hook surface ships dormant on 7.0.243:

- simulator-hooks.properties and BluetoothSimulatorHooks are still
  there; on 7.0.243 the simulator simply doesn't read them, the
  menu items don't render, and CN.canExecute("bluetooth:item1")
  returns false.
- BluetoothSimulatorHooksTest now opens with that exact canExecute
  guard. On 7.0.243 it returns true (test "passes" without doing
  anything); on a future CN1 release that intercepts the URLs
  (whenever #4988 ships), every existing assertion fires.

Local verification:
- mvn install of every module succeeds against 7.0.243.
- mvn cn1:test from BTDemo passes 8/8 (hook test no-ops cleanly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant