Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a84a640
Modernize developer guide demos and snippets
shai-almog Jul 1, 2026
af43c11
Fix developer guide CI snapshot setup
shai-almog Jul 1, 2026
b0c3ef7
Speed up developer guide CI bootstrap
shai-almog Jul 1, 2026
d9af0e9
Address developer guide PR feedback
shai-almog Jul 1, 2026
a47d940
Remove obsolete CSharp native snippets
shai-almog Jul 1, 2026
3b79fe1
Fix developer guide quality gate findings
shai-almog Jul 1, 2026
d876b01
Fix advanced topic guide snippets
shai-almog Jul 2, 2026
325df1e
Fix Android demo source compile check
shai-almog Jul 2, 2026
45bfd74
Use staged Android jars for guide compile check
shai-almog Jul 2, 2026
c185d74
Address wallet snippet quality findings
shai-almog Jul 2, 2026
ac48cef
Stop treating guide snippet bundles as Java sources
shai-almog Jul 2, 2026
5af8230
Include Android module classes in guide compile check
shai-almog Jul 2, 2026
5aab3f8
Stub missing Android helpers for guide source check
shai-almog Jul 2, 2026
2a44f97
Update developer guide snippets after master merge
shai-almog Jul 2, 2026
2e11719
Fix media guide anchor collision
shai-almog Jul 2, 2026
bb0852d
Move platform native guide snippets out of common
shai-almog Jul 2, 2026
2c49d81
Move Android native stub out of common demos
shai-almog Jul 2, 2026
8c28211
Move JavaScript guide snippets out of common demos
shai-almog Jul 2, 2026
ef67bf2
Compile developer guide Java snippets in demos
shai-almog Jul 2, 2026
81820bc
Fix developer guide callouts and screenshots
shai-almog Jul 2, 2026
d1567ad
Restore CI guide screenshot baselines
shai-almog Jul 2, 2026
de8b17d
Validate guide CSS and restore OpenAPI snippet
shai-almog Jul 2, 2026
bf53e33
Prove developer guide screenshot capture in CI
shai-almog Jul 2, 2026
19de6fa
Expand developer guide screenshot coverage
shai-almog Jul 3, 2026
efabba9
Avoid ripgrep in guide screenshot manifest check
shai-almog Jul 3, 2026
8f9a8ea
Render transition screenshot frames directly
shai-almog Jul 3, 2026
d3c4ff3
Update generated guide screenshot baselines
shai-almog Jul 3, 2026
47b92fc
Stabilize guide animation screenshots
shai-almog Jul 3, 2026
3cdd4c6
Merge remote-tracking branch 'origin/master' into codex/modernize-dev…
shai-almog Jul 3, 2026
71c1b94
Move VideoIO guide snippets into demos
shai-almog Jul 3, 2026
92d5a1d
Render guide screenshots from real frames
shai-almog Jul 3, 2026
3ce866e
Keep website redirect update green on PRs
shai-almog Jul 3, 2026
5278ab2
Expand developer guide screenshot coverage
shai-almog Jul 3, 2026
b660065
Update guide screenshot baselines from CI
shai-almog Jul 3, 2026
641aa58
Stabilize interaction dialog guide screenshot
shai-almog Jul 3, 2026
47b292c
Refine interaction dialog guide screenshot
shai-almog Jul 3, 2026
365fd6b
Prevent interaction dialog guide screenshot clipping
shai-almog Jul 3, 2026
316572e
Widen interaction dialog guide example
shai-almog Jul 3, 2026
597e696
Keep interaction dialog host controls visible
shai-almog Jul 3, 2026
e3faf66
Clean interaction dialog screenshot host label
shai-almog Jul 3, 2026
89003e8
Update interaction dialog screenshot baseline
shai-almog Jul 3, 2026
ff83077
Refresh developer guide screenshot demos
shai-almog Jul 3, 2026
b562d9d
Tune developer guide screenshot baselines
shai-almog Jul 3, 2026
7f9814d
Tighten developer guide screenshot crops
shai-almog Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
92 changes: 2 additions & 90 deletions .github/scripts/compare-animation-screenshots.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,6 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BASELINE_DIR="${BASELINE_DIR:-${PROJECT_ROOT}/docs/developer-guide/img}"
STORAGE_DIR="${CN1_STORAGE_DIR:-${HOME}/.cn1}"
ARTIFACT_DIR="${1:-${PROJECT_ROOT}/docs/demos/animation-screenshot-artifacts}"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"

if ! command -v compare >/dev/null 2>&1; then
echo "ImageMagick 'compare' command is required." >&2
exit 2
fi

mkdir -p "${ARTIFACT_DIR}"
find "${ARTIFACT_DIR}" -mindepth 1 -delete

if [[ ! -d "${STORAGE_DIR}" ]]; then
echo "Storage directory ${STORAGE_DIR} does not exist; nothing to compare."
exit 0
fi

shopt -s nullglob
mapfile -d '' SCREENSHOTS < <(find "${STORAGE_DIR}" -type f -name '*.png' -print0)
shopt -u nullglob

if [[ ${#SCREENSHOTS[@]} -eq 0 ]]; then
echo "No screenshots found under ${STORAGE_DIR}; nothing to compare."
exit 0
fi

mismatch_count=0

for screenshot in "${SCREENSHOTS[@]}"; do
filename="$(basename "${screenshot}")"
base="${filename%.png}"

baseline=""
baseline_ext=""
for ext in png PNG jpg JPG jpeg JPEG; do
candidate="${BASELINE_DIR}/${base}.${ext}"
if [[ -f "${candidate}" ]]; then
baseline="${candidate}"
baseline_ext="${ext}"
break
fi
done

if [[ -z "${baseline}" ]]; then
echo "No baseline found for ${filename}; treating as mismatch." >&2
mismatch_count=$((mismatch_count + 1))
cp "${screenshot}" "${ARTIFACT_DIR}/${filename}"
rm -f "${screenshot}"
continue
fi

metric_file="$(mktemp)"
diff_image="${ARTIFACT_DIR}/${base}.diff.png"

set +e
compare -metric AE "${screenshot}" "${baseline}" "${diff_image}" 2>"${metric_file}"
status=$?
set -e

if [[ ${status} -eq 0 ]]; then
rm -f "${screenshot}" "${diff_image}" "${metric_file}"
echo "${filename} matches baseline; capture discarded."
continue
fi

if [[ ${status} -ne 1 ]]; then
cat "${metric_file}" >&2
rm -f "${metric_file}" "${diff_image}"
echo "ImageMagick comparison failed for ${filename}." >&2
exit ${status}
fi

mismatch_count=$((mismatch_count + 1))
metric_value="$(cat "${metric_file}")"
rm -f "${metric_file}"

cp "${screenshot}" "${ARTIFACT_DIR}/${filename}"
cp "${baseline}" "${ARTIFACT_DIR}/${base}.baseline.${baseline_ext}"
echo "${metric_value}" > "${ARTIFACT_DIR}/${base}.metric.txt"
rm -f "${screenshot}"
echo "${filename} differs from baseline; artifacts stored in ${ARTIFACT_DIR}." >&2
done

if [[ ${mismatch_count} -eq 0 ]]; then
echo "All screenshots match their baselines."
exit 0
fi

echo "${mismatch_count} screenshot(s) differ from the documented baseline." >&2
exit 1
exec "${PROJECT_ROOT}/scripts/developer-guide/compare-guide-screenshots.sh" "$@"
86 changes: 63 additions & 23 deletions .github/workflows/developer-guide-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
paths:
- 'docs/developer-guide/**'
- 'docs/demos/**'
- 'scripts/developer-guide/**'
- '.github/workflows/developer-guide-docs.yml'
release:
types: [published]
Expand All @@ -30,9 +31,17 @@ jobs:
filters: |
demos:
- 'docs/demos/**'
- 'scripts/developer-guide/compare-guide-screenshots.sh'
- 'scripts/developer-guide/guide-screenshots.txt'
- 'scripts/developer-guide/verify-guide-screenshot-manifest.sh'
- 'scripts/developer-guide/verify-guide-screenshots-captured.sh'
- '.github/workflows/developer-guide-docs.yml'
docs:
- 'docs/developer-guide/**'
- 'docs/demos/common/src/main/snippets/**'
- 'scripts/developer-guide/migrate-inline-guide-snippets.py'
- 'scripts/developer-guide/validate-guide-snippets.py'
- 'scripts/developer-guide/verify-guide-screenshot-manifest.sh'
- '.github/workflows/developer-guide-docs.yml'
workflow:
- '.github/workflows/developer-guide-docs.yml'
Expand All @@ -41,46 +50,77 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'

- name: Install local Codename One Maven artifacts
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
xvfb-run -a mvn -B -ntp -f maven/pom.xml \
-pl core,javase,android,css-compiler,codenameone-maven-plugin \
-am install \
-Plocal-dev-javase \
-DskipTests \
-Dmaven.javadoc.skip=true \
-Dmaven.source.skip=true \
-Dspotbugs.skip=true \
-Dpmd.skip=true \
-Dcheckstyle.skip=true

- name: Build Codename One demos
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
mkdir -p "$HOME/.codenameone"
touch "$HOME/.codenameone/guibuilder.jar"
cp maven/CodeNameOneBuildClient.jar "$HOME/.codenameone/CodeNameOneBuildClient.jar"
xvfb-run -a mvn -B -ntp -Dgenerate-gui-sources-done=true -pl common -am -f docs/demos/pom.xml install
xvfb-run -a mvn -B -ntp -Dgenerate-gui-sources-done=true -Dcodename1.platform=javase -f docs/demos/pom.xml test

- name: Verify guide screenshot manifest
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
scripts/developer-guide/verify-guide-screenshot-manifest.sh

- name: Verify guide screenshots were captured
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
scripts/developer-guide/verify-guide-screenshots-captured.sh

- name: Compile Android demo sources
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
scripts/developer-guide/compile-android-demo-sources.sh

- name: Validate developer guide snippets
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
python3 scripts/developer-guide/validate-guide-snippets.py

- name: Install ImageMagick for screenshot comparison
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends imagemagick

- name: Compare animation screenshots
id: compare_animation_screenshots
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
- name: Compare guide screenshots
id: compare_guide_screenshots
if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
continue-on-error: true
run: |
set -euo pipefail
ARTIFACT_DIR="build/developer-guide/animation-screenshots"
ARTIFACT_DIR="build/developer-guide/guide-screenshots"
mkdir -p "${ARTIFACT_DIR}"
.github/scripts/compare-animation-screenshots.sh "${ARTIFACT_DIR}"
scripts/developer-guide/compare-guide-screenshots.sh "${ARTIFACT_DIR}"

- name: Upload animation screenshot mismatches
if: steps.compare_animation_screenshots.outcome == 'failure'
uses: actions/upload-artifact@v7
with:
name: developer-guide-animation-screenshots
path: build/developer-guide/animation-screenshots
if-no-files-found: warn

- name: Fail when animation screenshots differ
if: steps.compare_animation_screenshots.outcome == 'failure'
- name: Fail when guide screenshots differ
if: steps.compare_guide_screenshots.outcome == 'failure'
run: |
echo "Animation demo screenshots differ from developer guide assets." >&2
echo "Generated guide screenshots differ from developer guide assets." >&2
exit 1

- name: Determine publication metadata
Expand Down Expand Up @@ -407,7 +447,7 @@ jobs:
if-no-files-found: warn

- name: Upload AsciiDoc linter report
if: always()
if: always() && env.ASCII_DOC_LINT_REPORT != ''
uses: actions/upload-artifact@v7
with:
name: developer-guide-asciidoc-lint
Expand Down Expand Up @@ -435,15 +475,15 @@ jobs:
if-no-files-found: warn

- name: Upload paragraph capitalization report
if: always()
if: always() && env.PARAGRAPH_CAP_REPORT != ''
uses: actions/upload-artifact@v7
with:
name: developer-guide-paragraph-capitalization
path: ${{ env.PARAGRAPH_CAP_REPORT }}
if-no-files-found: warn

- name: Upload LanguageTool report
if: always()
if: always() && env.LANGUAGETOOL_REPORT != ''
uses: actions/upload-artifact@v7
with:
name: developer-guide-languagetool
Expand Down
4 changes: 2 additions & 2 deletions docs/demos/android/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<codename1.platform>android</codename1.platform>
<codename1.projectPlatform>android</codename1.projectPlatform>
<codename1.defaultBuildTarget>android-device</codename1.defaultBuildTarget>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.list.MultiButton;
import com.codename1.components.MultiButton;
import com.codename1.impl.android.AndroidNativeUtil;
import com.codename1.impl.android.PermissionPromptCallback;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.example.wallet;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

import java.lang.ref.WeakReference;

// tag::walletBridgeActivity[]
public class WalletBridgeActivity extends Activity {
private static WeakReference<WalletBridgeActivity> active;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
active = new WeakReference<>(this);

Intent in = getIntent();
String caller = getCallingPackage();
String payload = in == null ? null : in.getStringExtra("payload");

Intent launch = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (launch == null) {
finish();
return;
}
launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
launch.putExtra("wallet_payload", payload);
launch.putExtra("wallet_caller", caller == null ? "" : caller);
startActivity(launch);
// Do not finish yet. CN1 will finish this activity via native callback once verification completes.
}

@Override
protected void onDestroy() {
WalletBridgeActivity current = getActive();
if (current == this) {
active = null;
}
super.onDestroy();
}

public static WalletBridgeActivity getActive() {
return active == null ? null : active.get();
}
}
// end::walletBridgeActivity[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.example.wallet;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Set;

/**
* Android-native security helper snippets for wallet bridge examples.
*/
public final class WalletBridgeSecurity {
private WalletBridgeSecurity() {
}

// tag::walletTrustedCallerSignature[]
public static boolean isTrustedCallerSignature(Context ctx, String packageName, Set<String> allowedSha256) {
if (ctx == null || packageName == null || packageName.isEmpty() || allowedSha256 == null || allowedSha256.isEmpty()) {
return false;
}
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo info;
if (Build.VERSION.SDK_INT >= 28) {
info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES);
Signature[] sigs = info.signingInfo != null
? info.signingInfo.getApkContentsSigners()
: null;
return hasAllowedFingerprint(sigs, allowedSha256);
} else {
info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
return hasAllowedFingerprint(info.signatures, allowedSha256);
}
} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException ex) {
return false;
}
}

private static boolean hasAllowedFingerprint(Signature[] sigs, Set<String> allowedSha256)
throws NoSuchAlgorithmException {
if (sigs == null) {
return false;
}
MessageDigest md = MessageDigest.getInstance("SHA-256");
for (Signature s : sigs) {
String fp = toHex(md.digest(s.toByteArray()));
if (allowedSha256.contains(fp)) {
return true;
}
}
return false;
}

private static String toHex(byte[] data) {
char[] digits = "0123456789ABCDEF".toCharArray();
StringBuilder out = new StringBuilder(data.length * 2);
for (byte b : data) {
int value = b & 0xff;
out.append(digits[value >>> 4]);
out.append(digits[value & 0x0f]);
}
return out.toString();
}
// end::walletTrustedCallerSignature[]
}
Loading
Loading