--- name: codename-one description: Build and modify Codename One cross-platform mobile apps (Java 17, Maven, ParparVM/Android/iOS/JavaScript). Use when the project contains a `common/codenameone_settings.properties`, depends on `com.codenameone:codenameone-core`, edits CSS files under `common/src/main/css/`, calls `cn1:run`, `cn1:test`, `cn1:build`, references `com.codename1.ui.*` / `com.codename1.testing.*`, or when the user asks to build a UI, write screen tests, generate screenshots, or compare to Swing/HTML. metadata: type: skill --- # Codename One — App and UI Authoring Skill This skill teaches you how to write code for a Codename One (CN1) cross-platform mobile project. Codename One compiles Java/Kotlin bytecode to native iOS, Android, desktop and web. It looks like Java AWT/Swing, behaves like a mobile UI toolkit, and styles with a subset of CSS. **Use this skill when**: - A file you are editing imports `com.codename1.ui.*`, `com.codename1.io.*`, `com.codename1.testing.*`, or extends `com.codename1.system.Lifecycle`. - You are editing a file in `common/src/main/css/` (CN1 CSS). - You are running `cn1:run`, `cn1:debug`, `cn1:test`, or `cn1:build` Maven goals. - The user asks for a UI screen, a screenshot test, a responsive layout, or wants to convert a Swing/HTML snippet to CN1. ## How this skill is organized `SKILL.md` (this file) is the top-level cheat sheet. Deeper reference material lives under `references/` — pull the relevant file in **only when you need it**: - `references/build-and-run.md` — Local vs cloud builds, JDK matrix, Maven goals, `codenameone_settings.properties`, running the simulator, building for iOS/Android/Web, automated (Enterprise) cloud builds in CI. - `references/build-hints.md` — Curated index of `codename1.arg.*` build hints (iOS, Android, push, web). - `references/java-api-subset.md` — How to inspect the supported Java API subset, IO (`Storage`, `FileSystemStorage`), networking (`ConnectionRequest`, `Rest`), concurrency, dates, SQLite. **Read this whenever the compliance check fails or when you reach for a `java.*` API.** - `references/ui-components.md` — Form, Toolbar, Container layouts (Border/Box/Flow/Grid/Layered), common components, navigation, dialogs. - `references/css.md` — CSS capabilities and (important) **limitations**. Selectors, supported properties, 9-patch borders, theme constants. - `references/swing-comparison.md` — Mapping Swing concepts and code to Codename One. Read this when porting Swing code. - `references/html-css-cheatsheet.md` — Converting common HTML/CSS snippets to CN1 components + CSS. - `references/android-to-cn1.md` — Porting Android (XML + Kotlin/Java) screens to Codename One. - `references/testing-and-screenshots.md` — `AbstractTest`, `TestUtils`, `screenshotTest`, the `cn1:test` Maven goal, the screenshot tolerance algorithm. - `references/junit-testing.md` — Standard JUnit 5 tests against the simulator via `@CodenameOneTest`. Annotations (`@RunOnEdt`, `@Theme`, `@DarkMode`, `@LargerText`, `@Orientation`, `@RTL`, `@SimulatorProperty`), how it coexists with `cn1:test`, and why a headless CI runner has to be configured with Xvfb (or accepts that JUnit test classes will be skipped). - `references/mobile-adaptability.md` — Density-independent units (mm), `convertToPixels`, `LayeredLayout` for responsive design, `Display.isTablet()`, font scaling. - `references/native-interfaces.md` — Authoring native interfaces for iOS/Android/JavaScript/Desktop with `cn1:generate-native-interfaces` and platform callbacks. - `references/cn1libs.md` — Creating, packaging, and consuming Codename One libraries (Maven and legacy `.cn1lib`). - `references/snapshot-builds.md` — Edge case: compiling against a Codename One SNAPSHOT from git. - `references/debugging.md` — `jdb`-attach workflow for an agent: start the simulator paused, set breakpoints, dump locals, drive the session non-interactively from a script. - `tools/` — runnable Java 17 single-file utilities. `tools/IsApiSupported.java` answers "is this `java.*` class in the CN1 subset?"; `tools/IsCssValid.java` answers "does this `theme.css` compile?". Run with `java tools/.java `. When the user's task hits any one of those topics, **read the matching reference before generating code**. Do not paste large snippets without checking. ## Project layout (multi-module Maven) A CN1 project generated by the initializr has these modules: ``` my-app/ ├── pom.xml # Aggregator. cn1.plugin.version + cn1.version pinned here. ├── common/ # Cross-platform Java/Kotlin source. THIS IS WHERE THE APP LIVES. │ ├── pom.xml # 17 17 by default │ ├── codenameone_settings.properties │ └── src/main/ │ ├── java//.java │ ├── css/theme.css # CN1 CSS (NOT regular web CSS - see references/css.md) │ ├── l10n/ # i18n bundles (NOT src/main/resources!) │ └── guibuilder/ # Optional GUI builder XML ├── javase/ # Desktop simulator port ├── android/ # Android wrapper (built via build server or local Gradle) ├── ios/ # iOS wrapper (ParparVM) └── javascript/ # TeaVM-based web port ``` **Only edit `common/`**. The platform modules are thin wrappers — touching them is almost always wrong unless you are intentionally writing a native interface. ## Java version and language features This project targets **Java 17** (`17` / `17` in `common/pom.xml`, plus `codename1.arg.java.version=17` in `codenameone_settings.properties`). Use: - `var` for local variable type inference - Text blocks (`"""..."""`) - Records - Pattern matching for `instanceof` - `switch` expressions - Lambdas, method references, `Stream`s **Caveat — the build server cross-compiles to bytecode that ParparVM/TeaVM can consume.** Codename One ships a curated subset of the JDK, **not** the full `java.*` namespace. The `cn1:compliance-check` Maven goal runs on every compile and fails the build if you call an unsupported API. The most common gotchas: - No `java.nio.file.*` — use `com.codename1.io.FileSystemStorage` and `Storage`. - No `java.net.http.*` / `java.net.URLConnection` — use `com.codename1.io.rest.Rest` (preferred) or `ConnectionRequest`. - No `java.util.concurrent.locks.*` beyond simple `synchronized` — use `Display.getInstance().callSerially(...)` or `Display.startThread(...)`. - No `java.awt.*` / `javax.swing.*` — CN1 has its own UI stack. See `references/swing-comparison.md`. - No `java.lang.reflect.*` on production builds — works in the simulator only. - No threads spawned with `new Thread(...).start()` for UI work — always go through `Display.callSerially` or `Display.startThread(...)`. For the authoritative subset list and IO/networking patterns, read `references/java-api-subset.md` (which also shows how to grep the `java-runtime` jar to verify any specific class/method). ## The Event Dispatch Thread (EDT) CN1 has a single EDT, exactly like Swing. All UI mutation **must** happen on it. - Inside event listeners and lifecycle callbacks (`start`, `stop`, `init`) you are already on the EDT. - From a background thread, hop back with `Display.getInstance().callSerially(() -> { ... })` (or `callSeriallyAndWait` if you need to block). - Use `Display.getInstance().startThread(runnable, "name").start()` instead of `new Thread(...)` so cleanup happens correctly across platforms. `references/swing-comparison.md` contains a Swing→CN1 EDT idiom table. ## The Lifecycle main class Every CN1 app extends `com.codename1.system.Lifecycle` (or `com.codename1.ui.util.Lifecycle` in older code). The four methods you may override: ```java public class MyAppName extends Lifecycle { @Override public void init(Object context) { // Called once on the EDT. The Lifecycle base class already installs // the theme; reach for the cached global resources instance from // here on (Resources.getGlobalResources() returns the in-RAM copy, // no disk re-read). } @Override public void runApp() { // Build and show the first form. Form f = new Form("Hello", new BorderLayout()); f.add(BorderLayout.CENTER, new Label("Welcome")); f.show(); } @Override public void stop() { /* App backgrounded */ } @Override public void destroy() { /* App killed */ } } ``` ## Minimal "first screen" pattern ```java import static com.codename1.ui.CN.*; // Convenience statics: callSerially, etc. import com.codename1.ui.*; import com.codename1.ui.layouts.*; Form f = new Form("Profile", BoxLayout.y()); f.getToolbar().addCommandToRightBar("Save", null, e -> save()); f.add(new Label("Name")) .add(new TextField()) .add(new Button("Submit")); f.show(); ``` `BoxLayout.y()` (vertical) and `BoxLayout.x()` (horizontal) are the most common layouts. Wrap a `Form` content pane in `BorderLayout` when you want a header/footer/center split. See `references/ui-components.md` for the full layout matrix. ## CSS in Codename One CN1 ships with a CSS compiler that bakes `common/src/main/css/theme.css` into the binary theme resource (`theme.res`). It supports a deliberate **subset** of web CSS: ```css Form { background-color: #0f172a; /* hex, rgb(), or named colors */ padding: 2mm; /* mm is the recommended unit */ } Button { background-color: #1d4ed8; color: #ffffff; border: 1px solid #1d4ed8; border-radius: 3mm; padding: 2mm 4mm; } Button.pressed { /* state pseudo-class baked as UIID */ background-color: #1e3a8a; } #Constants { useLargerTextScaleBool: true; /* theme constants, not standard CSS */ } ``` **Key differences from web CSS** (read `references/css.md` before authoring more): - Selectors target **UIIDs** (Codename One component style names), not arbitrary HTML elements. `Button`, `Form`, `Label`, `Toolbar`, `Title` are the most common. - No descendant combinator, no `:hover`, no media queries. State variants are baked: `.pressed`, `.disabled`, `.selected`. - Units: prefer `mm` (millimeters) over `px`. CN1 converts `mm` to device pixels via `Display.convertToPixels`. `1mm` ≈ 6-9 px depending on density. - `border-radius` works but is rasterized at compile time — animating it at runtime requires programmatic styling. - No `transform`, no `flex`, no `grid`. Use CN1 Java layouts for arrangement; CSS is only for *styling*. - Bundled named colors are limited: `pink`, `orange`, `purple`, `yellow`, `gray`/`grey` are translated to hex by the initializr, anything else you must specify as hex. `references/html-css-cheatsheet.md` shows how to map "I want a flexbox row" / "I want a hero section" / "I want a card" to CN1 idioms. ## Adaptability and responsive design Mobile screens vary wildly. CN1 gives you: - **Density-independent units**: `1mm` always renders ~1mm tall regardless of pixel density. Always size in `mm`, not `px`. - `Display.getInstance().convertToPixels(2.5f)` — convert millimeters to current device pixels programmatically. - `Display.getInstance().isTablet()`, `Display.getInstance().isPortrait()`, `Display.getInstance().getDisplayWidth/Height()` — branch on form factor. - `LayeredLayout` with `LayeredLayoutConstraint` for precise responsive positioning (percent-based insets). - `Toolbar` automatically reshapes to platform conventions (Android side menu / iOS tab bar). See `references/mobile-adaptability.md` for patterns: phone-vs-tablet master-detail, orientation listeners, dynamic font scaling. ## Testing CN1 supports two compatible test styles in the same project: 1. **Legacy `AbstractTest` + `cn1:test`.** Required for tests that must also run on a device (`mvn cn1:test -Dtarget=ios`). Compiles under the device subset (no reflection, no JavaSE APIs). See `references/testing-and-screenshots.md`. 2. **Standard JUnit 5 + `@CodenameOneTest`.** Runs only in the simulator JVM via Surefire, so you get reflection, Mockito, AssertJ, IDE green-bar integration, `-Dtest=Foo#bar` filtering. Faster startup. See `references/junit-testing.md`. Both runners coexist — `cn1:test` discovers `UnitTest` implementers, Surefire discovers `@Test` methods, they don't trip over each other. Pick per test class. ```java // Legacy AbstractTest -- compiles under the device subset, runs via `cn1:test`. public class LoginFormTest extends AbstractTest { @Override public boolean shouldExecuteOnEDT() { return true; } @Override public boolean runTest() throws Exception { new MyAppName().runApp(); TestUtils.waitForFormTitle("Login"); TestUtils.setText("usernameField", "alice"); TestUtils.clickButtonByLabel("Sign In"); TestUtils.waitForFormTitle("Home"); return screenshotTest("home-screen-baseline"); } } // JUnit 5 -- simulator-only, runs via `mvn test` / Surefire. @CodenameOneTest class GreetingFormTest { @Test @RunOnEdt void formShowsExpectedTitle() { new Form("Hello").show(); assertEquals("Hello", Display.getInstance().getCurrent().getTitle()); } } ``` Run with `mvn -pl common cn1:test` (cn1:test runner only) or `mvn test` (both runners). The cn1app archetype already wires up Surefire + JUnit Jupiter in the generated POMs. `screenshotTest(name)` captures the current form, compares against a stored baseline under `Storage`, and returns `true` if within tolerance. First run records the baseline. See `references/testing-and-screenshots.md` for the tolerance algorithm and how to validate UI you just wrote. > Important: a "screenshot matches baseline" only proves consistency, **not** correctness. If you just generated the baseline yourself, you have not validated the screen — visually inspect at least once before treating that baseline as ground truth. > Headless caveat: any simulator-driven test (both flavors) needs an X server / Xvfb to construct the simulator's `JFrame`. The `@CodenameOneTest` extension auto-aborts the class on a headless JVM so you get "skipped" instead of "errored"; the `cn1:test` runner needs you to skip with `-DskipTests` or run under `xvfb-run`. ## Build and run commands From the project root: ```bash # Run in the desktop simulator (requires JDK 11–25 at runtime; build still uses JDK 17 source level) mvn -pl common cn1:run # Run with breakpoints mvn -pl common cn1:debug # Execute the CN1 test runner mvn -pl common cn1:test # Cloud build for Android/iOS/JS (requires CN1 build server creds) mvn -pl android package -Dcodename1.platform=android -Dcodename1.buildTarget=android-device mvn -pl ios package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-device mvn -pl javascript package -Dcodename1.platform=javascript -Dcodename1.buildTarget=javascript ``` See `references/build-and-run.md` for the local-vs-cloud matrix, automated-build mode (Enterprise), iOS local-build prerequisites, and the complete goal list. The full `codename1.arg.*` index lives in `references/build-hints.md`. ## What NOT to do - Don't use `java.awt.Color` / `java.awt.Font` / `javax.swing.*` — CN1 has its own `Color` constants (just `int` ARGB), `Font.createTrueTypeFont`, and `Component` hierarchy. - Don't add CSS that references web-only properties (`display`, `flex`, `position`, `transform`, `@media`) — the CN1 CSS compiler will silently ignore them or fail. - Don't put localization bundles under `common/src/main/resources/`. The CN1 plugin scans `common/src/main/l10n/` (or `common/src/main/i18n/`); bundles placed anywhere else are NOT baked into `theme.res` and `Resources.getL10N("messages", lang)` returns `null` at runtime. - Don't spin up `new Thread(...)` for UI work — use `Display.getInstance().callSerially(...)` or `Display.startThread(...)`. - Don't mutate UI off the EDT. Symptoms: random repaint glitches, native crashes on iOS. - Don't write screenshot tests where the baseline was just generated by the same code you are validating — that proves nothing. ## Sanity-check loop before reporting "done" For any UI-altering change: 1. Run `mvn -pl common cn1:run` in the simulator and click through the changed flow. 2. Inspect at least one screenshot (capture via the simulator menu → Save Screenshot, or generate via a test). 3. Resize the simulator window or toggle a different skin to confirm the layout doesn't break on a different form factor. 4. If you wrote a `screenshotTest`, delete the auto-generated baseline once if the screen has changed, then re-run twice — the second run should pass with `true`. If you cannot run the simulator (e.g. headless environment), **say so explicitly in the response** rather than claiming the UI works. ## Reference quick-look index | If the user asks for... | Open this reference | | --- | --- | | "Add a screen with a list / form / dialog" | `references/ui-components.md` | | "Make this look like X" / CSS tweaks | `references/css.md` | | "Port this from Swing" / Swing idioms | `references/swing-comparison.md` | | "I have HTML/CSS, convert it" | `references/html-css-cheatsheet.md` | | "I have Android XML/Kotlin/Java, convert it" | `references/android-to-cn1.md` | | "Write a test for this screen" / "Compare to a baseline" | `references/testing-and-screenshots.md` | | "Make it look right on tablet/landscape" | `references/mobile-adaptability.md` | | "How do I run/build/deploy" | `references/build-and-run.md` | | "What's the right `codename1.arg.*` for X" / native config | `references/build-hints.md` | | "Why does the compliance check fail" / Java/IO/networking | `references/java-api-subset.md` | | "I need to call a native iOS/Android/JS/desktop API" | `references/native-interfaces.md` | | "How do I create / consume a cn1lib" | `references/cn1libs.md` | | "Build against a Codename One SNAPSHOT from git" | `references/snapshot-builds.md` | | "Debug a faulty screen — attach `jdb` to the simulator" | `references/debugging.md` | | Quick yes/no check: "is this `java.*` class supported", "does my `theme.css` compile" | `tools/` directory — `java tools/IsApiSupported.java ` / `java tools/IsCssValid.java ` |