- 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/<Name>.java <args>`.
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 # <source>17</source> <target>17</target> by default
│ ├── codenameone_settings.properties
│ └── src/main/
│ ├── java/<pkg>/<MainClass>.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** (`<source>17</source>` / `<target>17</target>` 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.
View on GitHub