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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
73 changes: 43 additions & 30 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 Down Expand Up @@ -33,6 +34,9 @@ jobs:
- '.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'
- '.github/workflows/developer-guide-docs.yml'
workflow:
- '.github/workflows/developer-guide-docs.yml'
Expand All @@ -41,47 +45,52 @@ 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 process-classes

- name: Install ImageMagick for screenshot comparison
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
- name: Verify developer guide images are referenced
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
python3 scripts/developer-guide/find_unused_images.py docs/developer-guide \
--allow-unused img/autocomplete.png \
--allow-unused img/native-theme-menu.png \
--allow-unused img/tabs.png

- name: Compare animation screenshots
id: compare_animation_screenshots
if: github.event_name != 'pull_request' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true'
continue-on-error: true
- 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
ARTIFACT_DIR="build/developer-guide/animation-screenshots"
mkdir -p "${ARTIFACT_DIR}"
.github/scripts/compare-animation-screenshots.sh "${ARTIFACT_DIR}"
scripts/developer-guide/compile-android-demo-sources.sh

- 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: 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: |
echo "Animation demo screenshots differ from developer guide assets." >&2
exit 1
set -euo pipefail
python3 scripts/developer-guide/validate-guide-snippets.py

- name: Determine publication metadata
run: |
Expand Down Expand Up @@ -335,7 +344,11 @@ jobs:
TEXT_REPORT="${REPORT_DIR}/unused-images.txt"
mkdir -p "$REPORT_DIR"
set +e
python3 scripts/developer-guide/find_unused_images.py docs/developer-guide --output "$JSON_REPORT" | tee "$TEXT_REPORT"
python3 scripts/developer-guide/find_unused_images.py docs/developer-guide \
--allow-unused img/autocomplete.png \
--allow-unused img/native-theme-menu.png \
--allow-unused img/tabs.png \
--output "$JSON_REPORT" | tee "$TEXT_REPORT"
STATUS=${PIPESTATUS[0]}
set -e
echo "UNUSED_IMAGES_JSON=$JSON_REPORT" >> "$GITHUB_ENV"
Expand Down Expand Up @@ -407,7 +420,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 +448,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
Expand Up @@ -3,19 +3,16 @@
import android.content.Context;
import com.codename1.impl.android.AndroidNativeUtil;

// tag::mobihelpNativeImplContext[]
class MobihelpNativeImpl {
// tag::mobihelpNativeContext[]
private static Context context() {
return AndroidNativeUtil.getActivity().getApplicationContext();
}
// end::mobihelpNativeContext[]

// tag::mobihelpNativeClearUserData[]
public void clearUserData() {
com.freshdesk.mobihelp.Mobihelp.clearUserData(context());
}
// end::mobihelpNativeClearUserData[]

private static AndroidActivityWrapper activity() {
return new AndroidActivityWrapper();
Expand Down Expand Up @@ -47,7 +44,6 @@ public void onResult(com.freshdesk.mobihelp.MobihelpCallbackStatus status, Integ
}
// end::mobihelpNativeGetUnreadCountAsync[]
}
// end::mobihelpNativeImplContext[]

// Helper wrapper to simulate Android Activity runOnUiThread for compilation
class AndroidActivityWrapper {
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[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.example.wallet;

import com.codename1.ui.Display;

/**
* Compilable launch-handling snippets for Android wallet handoff examples.
*/
public class WalletLaunchHandlerSnippet {
// tag::walletLaunchStart[]
public void start() {
Display d = Display.getInstance();
String action = d.getProperty("android.intent.action", null);
if ("com.example.wallet.ACTION_VERIFY".equals(action)) {
boolean callerVerified = "true".equals(d.getProperty("android.intent.caller.verified", "false"));
String payload = d.getProperty("android.intent.extra.wallet_payload", d.getProperty("AppArg", null));
String caller = d.getProperty("android.intent.caller", "");

if (!callerVerified || !isAllowedCaller(caller)) {
failClosed(); // return declined/canceled via bridge completion API
return;
}

verifyWalletPayload(payload, caller);
return;
}
showMainForm();
}
// end::walletLaunchStart[]

private boolean isAllowedCaller(String caller) {
return caller != null && caller.length() > 0;
}

private void failClosed() {
// Complete the native bridge with a declined/canceled result.
}

private void verifyWalletPayload(String payload, String caller) {
// Run authentication + back end verification, then call native bridge completion API.
}

private void showMainForm() {
// Show the normal application UI.
}
}
2 changes: 1 addition & 1 deletion docs/demos/common/codenameone_settings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ codename1.android.keystore=
codename1.android.keystoreAlias=
codename1.android.keystorePassword=
codename1.arg.ios.newStorageLocation=true
codename1.arg.java.version=8
codename1.arg.java.version=17
codename1.displayName=DemoCode
codename1.icon=icon.png
codename1.ios.appid=Q5GHSKAL2F.com.codenameone.developerguide
Expand Down
4 changes: 2 additions & 2 deletions docs/demos/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
Expand Down
Loading
Loading