Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 9 additions & 37 deletions docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Here is the current list of supported arguments. Build hints change over time, s
|true/false defaults to true - indicates whether to include the release version in the build

|android.onDeviceDebug
|Boolean true/false defaults to false. When `true`, the generated `AndroidManifest.xml` is marked `android:debuggable="true"`, R8/proguard is disabled, and the build is pinned to debug-only (`android.release` is forced off and `android.debug` is forced on) so a stray hint can't ship a release-signed APK that's `debuggable="true"`. Pair with the `cn1:android-on-device-debugging` Maven goal (or the bundled IntelliJ run configs) to install, launch, forward JDWP, and stream logcat through adb. Has no effect on builds that don't carry it — release builds are unaffected. See the link:#_ondevice_debugging_android[On-Device Debugging (Android) chapter] for the full flow.
|Boolean true/false defaults to false. When `true`, the generated `AndroidManifest.xml` is marked `android:debuggable="true"`, R8/proguard is disabled, and the build is pinned to debug-only (`android.release` is forced off and `android.debug` is forced on) so a stray hint can't ship a release-signed APK that's `debuggable="true"`. Pair with the `cn1:android-on-device-debugging` Maven goal (or the bundled IntelliJ run configs) to install, launch, forward JDWP, and stream logcat through adb. Has no effect on builds that don't carry it — release builds are unaffected. See the <<On-Device Debugging (Android),On-Device Debugging (Android) chapter>> for the full flow.

|android.installLocation
|Maps to android:installLocation manifest entry defaults to auto. Can also be set to internalOnly or preferExternal.
Expand Down Expand Up @@ -452,7 +452,7 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g
|Boolean true/false defaults to false as of 5.0. Enable legacy generation of splash screen images for use when launching the app. These have been replaced now by the new launch storyboards.

|ios.onDeviceDebug
|Boolean true/false defaults to false. When `true`, the iOS build links a small JDWP listener thread (`cn1_debugger`) into the binary and the ParparVM translator emits source-line and locals metadata so a desktop proxy can serve the running app to any JDWP-speaking debugger. Has no effect on release builds. See the link:#_ondevice_debugging_ios[On-Device Debugging chapter] for the full flow.
|Boolean true/false defaults to false. When `true`, the iOS build links a small JDWP listener thread (`cn1_debugger`) into the binary and the ParparVM translator emits source-line and locals metadata so a desktop proxy can serve the running app to any JDWP-speaking debugger. Has no effect on release builds. See the <<On-Device Debugging (iOS),On-Device Debugging (iOS) chapter>> for the full flow.

|ios.onDeviceDebug.proxyHost
|Hostname or IP address the device-side listener dials to reach the desktop proxy. Default `127.0.0.1` (correct for the native iOS simulator). For a physical device, set this to the developer laptop's LAN IP. Has no effect unless `ios.onDeviceDebug=true`.
Expand Down Expand Up @@ -768,42 +768,14 @@ Pass `null` to `AndroidNativeUtil.setPermissionPromptCallback()` to restore the

=== On device debugging

Codename One supports debugging applications on devices by using the natively generated project. All paid subscription levels include the ability to check an #Include Source# flag in the settings that returns a native OS project. You can debug that project in the respective native IDE.

In iOS this is strait forward, open the project with Xcode and run it optionally disabling bitcode. Unzip the.bz2 file and open the `.xcworkspace` file if it's available otherwise open the `.xcodeproj` file inside the `dist` directory.

IMPORTANT: The `.xcworkspace` is no longer exclusive to CocoaPods-based builds. Use it whenever it's generated, whether the project uses CocoaPods, Swift Package Manager, or both.

With Android Studio this is sometimes as easy task as it's possible to actually open the Gradle project in Android Studio and run it. For example, due to the fragile nature of the Gradle project this stopped working for some builds and has been "flaky."

==== Android Studio debugging (easy way)

By default you should be able to open the Gradle project in Android Studio and run it. To get this to work open the Android Studio #Setting# and select Gradle *2.11*.

.Gradle settings UI in Android Studio (notice you need Gradle 2.11 and not 2.8 as pictured here)
image::img/gradle-settings.png[Gradle settings UI in Android Studio,scaledwidth=50%]

If this works for you then you can ignore the section below.

==== Android Studio debugging the hard way

Sometimes the Gradle project might not work or this might fail with a change from Google.

Here are steps that should work for everyone:

. Check the include source flag in the IDE and send a build
. Download the `sources.zip` result from the build server
. Launch Android Studio and create a new project
. Make sure to use the same package and app name as you did in the Codename One project, select to not create an activity
. Unzip the `sources.zip` file and copy the `main` directory from its `src` directory to the Android Studio projects `src` directory make sure to overwrite files/directories.
. Copy its `libs` directory on top of the existing libs
. Copy the source Gradle dependencies content to the destination Gradle file
. Connect your device and press the Debug button for the IDE

NOTE: You might need to copy more Gradle file meta-data such as multi-dexing etc.

You might not need to repeat the whole thing with every build. For example: it might be practical to copy the `userSources.jar` from the libs directory to get the latest version of your code. You can copy the `src/main` directory to get the latest up-to-date Android port.
On-device debugging now has two dedicated chapters, one per platform:

* <<On-Device Debugging (iOS)>> covers attaching a standard Java
debugger (IntelliJ, jdb, VS Code) to an iOS app running on a device or in the
native iOS simulator, as well as debugging the generated Xcode project natively.
* <<On-Device Debugging (Android)>> covers the equivalent
adb/JDWP-based flow for Android devices and emulators, including wireless
debugging and debugging the generated Gradle project from Android Studio.

=== Native interfaces

Expand Down
140 changes: 139 additions & 1 deletion docs/developer-guide/Game-Development.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ into 3D:
in a grid; flipping one animates its horizontal scale through zero and swaps the
image at the thin point, and taps are hit-tested against sprite bounds through
`GameInput`. A compact illustration of flat 2D play with no camera tricks at all.
Dissected in <<Case study: The card game (flat 2D)>>.
* *`BoardGameSample`* -- *faux-3D* checkers on an isometric board. Every tile and
piece is still a flat `Sprite`, but laying them out in an isometric (2:1 diamond)
projection and raising the pieces off their tiles with a drop shadow gives a
convincing 3D look without any GPU 3D, perspective camera or models. The screen
pixel-to-board-cell mapping (and its inverse, for picking) is the same math behind
any isometric strategy or tycoon game; it also implements the full move/jump/chain
/crown rules and a small AI so you can play it solo.
/crown rules and a small AI so you can play it solo. Dissected in
<<Case study: Faux-3D checkers (isometric projection)>>.

The action samples are driven by the joystick and buttons in
<<On-screen touch controls>>, so they're equally playable on a touch device and in
Expand Down Expand Up @@ -303,6 +305,142 @@ faces both ways -- no mirrored art needed (sprites are drawn double-sided for
exactly this reason). `play()`, `pause()`, `stop()`, `setLooping(boolean)` and
`setCurrentFrame(int)` control playback.

=== Case study: The card game (flat 2D)

image::img/game-cards.png[The Memory card game mid-flip: two matched pairs and one card face up,240]

`CardGameSample` is Memory (Concentration): sixteen face-down cards in a four-by-four
grid, tap two and they stay up when they match, flip back when they don't. It's
the smallest complete example of turn-based 2D play with `com.codename1.gaming`
-- no joystick, no physics, no camera -- which makes the three techniques it does
use easy to see in isolation.

*One sprite per card.* Each card is a `Sprite` positioned at its grid slot. There
are exactly two images at any moment: the shared card-back image and the card's
own face (all generated at runtime with `Image.createImage` and `Graphics` calls,
so the sample needs no assets). Changing what a card shows is just
`sprite.setImage(...)`.

*A flip you can fake with scale.* A real card flip is a 3D rotation, but
squeezing the sprite's horizontal scale through zero and swapping the image at
the thin point reads exactly the same to the eye:

[source,java]
----
// in the card's per-frame animation; flip runs 0 -> 1 over ~0.4s
flip += dt * 5;
float sx = Math.abs(1f - 2f * (float) flip); // 1 -> 0 -> 1
if (flip >= 0.5 && !showingFace) {
showingFace = true;
sprite.setImage(face); // swap sides while the card is edge-on
}
sprite.setScale(Math.max(0.05f, sx), 1f);
----

This is the same negative/positive scale trick the scroller uses to flip its
runner, driven over time to play as an animation.

*Polled taps against sprite bounds.* There are no buttons to wire up;
`update(double)` polls the `GameInput` edge state and hit-tests the tap against
each card's bounding box:

[source,java]
----
protected void update(double dt) {
// ... advance flip animations, count down the mismatch timer ...
if (getInput().wasPointerPressed()) {
int px = getInput().getPointerX();
int py = getInput().getPointerY();
for (int i = 0; i < cards.length; i++) {
Rectangle b = cards[i].sprite.getBounds();
if (b.contains(px, py)) {
flipUp(cards[i]);
break;
}
}
}
}
----

The rest of the sample is a small state machine living in plain fields: the
first and second face-up cards, a `mismatchTimer` that counts down in `update`
before flipping a failed pair back, and a moves counter surfaced through the
form title. Note the timer: in a game loop you don't schedule callbacks, you
subtract `dt` from a countdown field each frame and act when it crosses zero.

=== Case study: Faux-3D checkers (isometric projection)

image::img/game-board.png[Isometric checkers mid-game with a selected piece and a highlighted destination,240]

`BoardGameSample` plays a full game of checkers -- moves, forced captures, chained
jumps, crowning, and a small AI opponent -- on a board that looks convincingly 3D --
yet there is no perspective camera and no `Model` anywhere: every tile and piece
is a flat `Sprite`. The 3D impression comes entirely from *where* the flat art is
placed, a technique (isometric or "2.5D" projection) that powered decades of
strategy and tycoon games and still works just as well on a GPU sprite pipeline.

*The projection.* Board cell `(r, c)` maps to screen pixels by laying the grid out
as 2:1 diamonds -- columns step right-and-down, rows step left-and-down:

[source,java]
----
private float tileCenterX(int r, int c) {
return originX + (c - r) * (tileW / 2f);
}

private float tileCenterY(int r, int c) {
return originY + (c + r) * (tileH / 2f); // tileH = tileW / 2
}
----

*Picking is the inverse.* Because the mapping is linear it inverts with two
divisions, turning a tap back into a board cell -- no per-tile hit testing
required:

[source,java]
----
private int[] pick(int px, int py) {
float a = (px - originX) / (tileW / 2f); // = c - r
float b = (py - originY) / (tileH / 2f); // = c + r
int c = Math.round((a + b) / 2f);
int r = Math.round((b - a) / 2f);
if (r < 0 || r >= N || c < 0 || c >= N) {
return null; // tap missed the board
}
return new int[]{r, c};
}
----

*Depth from z-order.* Cells further down the screen must draw over the cells
behind them, and within one cell the stacking is tile, then shadow, then
selection/move markers, then the piece. Both rules collapse into a single
number -- `(r + c)` strides the cells from back to front, and a small offset
layers the sprites within a cell:

[source,java]
----
// shadow sits flat on the tile, the piece floats above it
addDynamic(shadow, cx, cy + tileH * 0.10f, (r + c) * 4 + 1, 0.5, 0.5);
addDynamic(piece, cx, cy - tileH * 0.30f, (r + c) * 4 + 3, 0.5, 0.62);
----

That pair of lines is also the whole "3D height" illusion: the piece is drawn
raised off its tile while its shadow stays put, and the eye reads the gap as
elevation. The piece art helps -- an elliptical top face over a darker side rim,
again just `fillArc` calls on a runtime-generated image.

*Rebuild, don't mutate.* After every move the sample throws away all its dynamic
sprites (pieces, shadows, markers) and recreates them from the `board[][]` array
in one `syncPieces()` pass. With a few dozen sprites this costs nothing, and it
keeps the scene a pure function of the game state -- there is no way for the
display to drift out of sync with the rules, which eliminates the whole class of
"the board shows X but the game thinks Y" bugs. The game logic itself
(`destinations`, forced captures, chaining, crowning) never touches a sprite; it
reads and writes the `int[][]` board only.

When fake 3D stops being enough -- you want the camera to move through the world
rather than look at it -- the next section covers the real thing.

=== 3D and perspective

`GameView` renders on the GPU through the <<3D Graphics and Shaders,`com.codename1.gpu`>>
Expand Down
1 change: 1 addition & 0 deletions docs/developer-guide/Index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ public void start() {

<5> The `show()` method places the `Form` on the screen. Only one `Form` can be shown at a time

[[TitleAndLabelImage]]
.Title and Label in the UI
image::img/codenameone-hello-world-title-label.png[Title and Label in the UI,scaledwidth=50%]

Expand Down
24 changes: 24 additions & 0 deletions docs/developer-guide/On-Device-Debugging-Android.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,30 @@ Without a source path, breakpoints in framework classes still trigger
and locals / fields still read — you'll just see "Sources not found"
in the editor when stepping into framework code.

=== Debugging the generated Gradle project from Android Studio

Everything above drives a Codename One-built APK through `adb`. The
alternative is to open the generated Android project itself in Android
Studio and use it like any native Android app — useful when you're
debugging native interface code, working on the Android port, or want
Android Studio's profiler and layout inspector:

* *Local build*: `mvn cn1:buildAndroidGradleProject` writes a complete
Gradle project under `target/...-android-source/`. Open that directory
in Android Studio (current versions open it directly — no Gradle
version fiddling required), connect a device, and press *Debug*.
* *Cloud build*: check the *Include Source* flag in Codename One
Settings before sending the build, then download the sources result
from the build server and open it the same way.

When iterating, regenerate the project rather than hand-merging files —
the generated project is a build artifact, not a source of truth.

For native C/C++ added through the NDK, attach Android Studio's LLDB to
the process; as noted in <<What you can step through>>, the LLDB and
JDWP attaches are independent and can run side by side against one
device.

=== Troubleshooting

==== "No Android device is online"
Expand Down
28 changes: 28 additions & 0 deletions docs/developer-guide/On-Device-Debugging.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,34 @@ of two to three times slower than a release build — this is
normal and matches the overhead of `-g`-style debugging on
other native VMs.

=== Debugging the generated Xcode project natively

The JDWP flow above keeps you at the Java level. For native-level work --
stepping through Objective-C sources, debugging a native interface, chasing a
crash inside the VM, or profiling with Instruments -- debug the generated Xcode
project directly:

* *Local build*: `mvn cn1:buildIosXcodeProject` writes the complete Xcode
project under `target/...-ios-source/`.
* *Cloud build*: check the *Include Source* flag in Codename One Settings before
sending the build, and download the sources result from the build server.

Open the `.xcworkspace` file if one was generated, otherwise the `.xcodeproj`,
and run on a device or the native iOS simulator from Xcode as you would any iOS
app.

IMPORTANT: The `.xcworkspace` is no longer exclusive to CocoaPods-based builds.
Use it whenever it's generated, whether the project uses CocoaPods, Swift
Package Manager, or both.

Inside Xcode you get LLDB and Instruments against the real binary. Your Java
code appears as the C sources ParparVM generated from it -- a Java method like
`com.mycompany.MyClass.doStuff(int)` becomes a C function named along the lines
of `com_mycompany_MyClass_doStuff___int`, so symbolic breakpoints on the
translated code work too. The native-IDE path and the JDWP path are
complementary: use Xcode for native frames and profiling, the JDWP proxy for
source-level Java debugging.

=== Troubleshooting

==== The proxy reports "device disconnected" before the breakpoint fires
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[[working-with-codename-one-sources]]
== Working with Codename One sources

The Codename One SDK is published as a Maven multi-module project. Building the
Expand Down
Binary file added docs/developer-guide/img/game-board.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/developer-guide/img/game-cards.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/developer-guide/img/gradle-settings.png
Binary file not shown.
4 changes: 2 additions & 2 deletions docs/developer-guide/io.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ Notice that a static version of the method is used! The callback object is an in
===== Writing JSON — `JSONWriter`

For typed DTO serialization use the `@Mapped` annotation framework
(see `<<binding-framework>>`) plus `Mappers.toJson(...)`. For ad-hoc
(see `<<annotation-mapping-section,JSON / XML Object Mapping>>`) plus `Mappers.toJson(...)`. For ad-hoc
maps / lists where a build-time `Mapper` would be overkill, the
`com.codename1.io.JSONWriter` class is the complement of `JSONParser`:

Expand Down Expand Up @@ -1415,7 +1415,7 @@ branch on `data.get("root") instanceof List`.

`Rest.fetchAsJsonMap` returns a generic `Map<String, Object>` that the caller
casts and inspects key by key. Once your DTOs are `@Mapped`-annotated
(see `<<binding-framework>>`), `fetchAsMapped` returns the typed object
(see `<<annotation-mapping-section,JSON / XML Object Mapping>>`), `fetchAsMapped` returns the typed object
directly:

[source,java]
Expand Down
Loading