diff --git a/.github/scripts/compare-animation-screenshots.sh b/.github/scripts/compare-animation-screenshots.sh index 729486c4bf4..8e19ac084e8 100755 --- a/.github/scripts/compare-animation-screenshots.sh +++ b/.github/scripts/compare-animation-screenshots.sh @@ -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" "$@" diff --git a/.github/workflows/developer-guide-docs.yml b/.github/workflows/developer-guide-docs.yml index 377be4b8f55..717cdae4015 100644 --- a/.github/workflows/developer-guide-docs.yml +++ b/.github/workflows/developer-guide-docs.yml @@ -5,6 +5,7 @@ on: paths: - 'docs/developer-guide/**' - 'docs/demos/**' + - 'scripts/developer-guide/**' - '.github/workflows/developer-guide-docs.yml' release: types: [published] @@ -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' @@ -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 @@ -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 @@ -435,7 +475,7 @@ 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 @@ -443,7 +483,7 @@ jobs: 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 diff --git a/docs/demos/android/pom.xml b/docs/demos/android/pom.xml index 9dedf095eda..bb46743db0e 100644 --- a/docs/demos/android/pom.xml +++ b/docs/demos/android/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 android android android-device diff --git a/docs/demos/android/src/main/java/com/codenameone/developerguide/advancedtopics/PermissionSnippets.java b/docs/demos/android/src/main/java/com/codenameone/developerguide/advancedtopics/PermissionSnippets.java index 3efd813420a..27fb90b63e5 100644 --- a/docs/demos/android/src/main/java/com/codenameone/developerguide/advancedtopics/PermissionSnippets.java +++ b/docs/demos/android/src/main/java/com/codenameone/developerguide/advancedtopics/PermissionSnippets.java @@ -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; diff --git a/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeActivity.java b/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeActivity.java new file mode 100644 index 00000000000..13c7d032de3 --- /dev/null +++ b/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeActivity.java @@ -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 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[] diff --git a/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeSecurity.java b/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeSecurity.java new file mode 100644 index 00000000000..91f1e90d337 --- /dev/null +++ b/docs/demos/android/src/main/java/com/example/wallet/WalletBridgeSecurity.java @@ -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 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 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[] +} diff --git a/docs/demos/android/src/main/java/com/example/wallet/WalletLaunchHandlerSnippet.java b/docs/demos/android/src/main/java/com/example/wallet/WalletLaunchHandlerSnippet.java new file mode 100644 index 00000000000..45fc40a9258 --- /dev/null +++ b/docs/demos/android/src/main/java/com/example/wallet/WalletLaunchHandlerSnippet.java @@ -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. + } +} diff --git a/docs/demos/common/src/main/java/com/mycompany/myapp/MyNativeImplStub.java b/docs/demos/android/src/main/java/com/mycompany/myapp/MyNativeImplStub.java similarity index 100% rename from docs/demos/common/src/main/java/com/mycompany/myapp/MyNativeImplStub.java rename to docs/demos/android/src/main/java/com/mycompany/myapp/MyNativeImplStub.java diff --git a/docs/demos/common/codenameone_settings.properties b/docs/demos/common/codenameone_settings.properties index d0c00917fc7..8494fded9dd 100644 --- a/docs/demos/common/codenameone_settings.properties +++ b/docs/demos/common/codenameone_settings.properties @@ -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 diff --git a/docs/demos/common/pom.xml b/docs/demos/common/pom.xml index 4d7bf72b2a4..bc93c800614 100644 --- a/docs/demos/common/pom.xml +++ b/docs/demos/common/pom.xml @@ -305,8 +305,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + ${maven.compiler.source} + ${maven.compiler.target} diff --git a/docs/demos/common/src/main/css/developer-guide/css.css b/docs/demos/common/src/main/css/developer-guide/css.css new file mode 100644 index 00000000000..73eceeee0b1 --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/css.css @@ -0,0 +1,682 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::css-css-001[] +Button { + text-align: center; + border: 1pt solid gray; + background-color: transparent; +} + +Button.pressed { + background-color: gray; +} +// end::css-css-001[] + +// tag::css-css-002[] +MyButton { + cn1-derive: Button; + background-color: blue; +} +// end::css-css-002[] + +// tag::css-css-003[] +#Device { + min-resolution: 120dpi; + max-resolution: 480dpi; + resolution: 480dpi; +} +// end::css-css-003[] + +// tag::css-css-004[] +#Constants { + PopupDialogArrowBool: false; + calTitleDayStyleBool: true; + calTransitionVertBool: false; + calendarLeftImage: "calendar-arrow-left.png"; + calendarRightImage: "calendar-arrow-right.png"; + centeredPopupBool: false; + checkBoxCheckDisFocusImage: "Check-Box_Normal.png"; + checkBoxCheckedFocusImage: "Check-Box_Press.png"; + checkBoxCheckedImage: "Check-Box_Press.png"; + checkBoxOppositeSideBool: true; + checkBoxUncheckedFocusImage: "Check-Box_Normal.png"; + checkBoxUncheckedImage: "Check-Box_Normal.png"; + comboImage: "combo.png"; + commandBehavior: "Side"; + dialogTransitionIn: "fade"; + dialogTransitionOut: "fade"; + dlgButtonCommandUIID: "DialogButton"; + dlgCommandGridBool: true; + dlgInvisibleButtons: #1a1a1a; + formTransitionIn: "empty"; + formTransitionOut: "slide"; + includeNativeBool: true; + menuImage: "of_menu.png"; + noTextModeBool: true; + onOffIOSModeBool: true; + otherPopupRendererBool: false; + pureTouchBool: true; + radioSelectedFocusImage: "Radio_btn_Press.png"; + radioSelectedImage: "Radio_btn_Press.png"; + radioUnselectedFocusImage: "Radio_btn_Normal.png"; + radioUnselectedImage: "Radio_btn_Normal.png"; + sideMenuImage: "menu.png"; + switchMaskImage: "switch-mask-3.png"; + switchOffImage: "switch-off-3.png"; + switchOnImage: "switch-on-3.png"; + tabPlacementInt: 0; + backIconImage: "Back-icon.png"; + articleSourceIconImage: "Source-icon.png"; + articleDateIconImage: "Date-icon.png"; + articleArrowRightImage: "Arrow-right.png"; + articleShareIconImage: "Share-icon.png"; + articleBookmarkIconImage: "Bookmark-icon.png"; + articleTextIconImage: "Text-icon.png"; + articleCommentsIconImage: "Comments-icon.png"; + newsIconImage: "News-icon.png"; + channelsIconImage: "Channels-icon.png"; + bookmarksIconImage: "Bookmarks-icon.png"; + overviewIconImage: "Overview-icon.png"; + calendarIconImage: "Calendar-icon.png"; + timelineIconImage: "Timeline-icon.png"; + profileIconImage: "Profile-icon.png"; + widgetsIconImage: "Widgets-icon.png"; + settingsIconImage: "Settings-icon.png"; + SubmitIconImage: "Submit-icon.png"; + SubmitIconDarkImage: "SubmitButtonLight-icon.png"; + defaultFontSizeInt: 18; + defaultDesktopFontSizeInt: 14; + defaultSourceDPIInt: "0"; + +} +// end::css-css-004[] + +// tag::css-css-005[] +var(--header-color, blue); +// end::css-css-005[] + +// tag::css-css-006[] +var(, ?) +// end::css-css-006[] + +// tag::css-css-007[] +#Constants { + --main-bg-color: red; +} + +MyContainer { + background-color: var(--main-bg-color); +} +// end::css-css-007[] + +// tag::css-css-008[] +#Constants { + --main-bg-color: red; +} + +MyContainer { + background-color: var(--main-bg-color, blue); +} +// end::css-css-008[] + +// tag::css-css-009[] +RoundBorder { + border: 1px #3399ff cn1-round-border; + text-align:center; + margin:2mm; + padding:3mm; +} + +RoundBorderFilled { + background: cn1-round-border; + background-color: #ccc; + text-align:center; + margin:2mm; + padding:3mm; +} +// end::css-css-009[] + +// tag::css-css-010[] +PillBorder { + border: 1pt #3399ff cn1-pill-border; + text-align:center; +} + +PillBorderFilled { + background: cn1-pill-border; + background-color: #3399ff; + color:white; + text-align:center; +} +// end::css-css-010[] + +// tag::css-css-011[] +RoundRectLabel { + background-color: red; + border-radius: 2mm; +} +// end::css-css-011[] + +// tag::css-css-012[] +EllipticalBorder { + border-radius: 2mm 4mm 6mm 1mm / 1mm 3mm 5mm 7mm; + cn1-box-shadow-spread: 1.5mm; + cn1-box-shadow-inset: inset; +} +// end::css-css-012[] + +// tag::css-css-013[] +/* 2-color cardinal direction (still supported, smallest descriptor) */ +MyContainer { background: linear-gradient(0deg, #ccc, #666); } +MyContainer { background: linear-gradient(to top, #ccc, #666); } + +/* Arbitrary angle */ +MyContainer { background: linear-gradient(45deg, #eaeaea, #666666); } + +/* Multi-stop with positions */ +MyButton { background: linear-gradient(135deg, #ff0080 0%, #ff8c00 50%, #40e0d0 100%); } + +/* Diagonal direction keyword */ +MyForm { background: linear-gradient(to bottom right, #f06, #003); } + +/* Mismatched alphas are fine — the gradient is no longer rejected */ +MyComponent { background: linear-gradient(90deg, rgba(255, 0, 0, 0.6), blue); } +// end::css-css-013[] + +// tag::css-css-014[] +MyContainer { background: radial-gradient(circle, gray, white); } +MyContainer { background: radial-gradient(ellipse closest-side at 25% 75%, #fff, #000); } +MyContainer { background: radial-gradient(circle farthest-corner at right, #ffe, #066 60%, #001 100%); } +// end::css-css-014[] + +// tag::css-css-015[] +MyContainer { background: conic-gradient(red, yellow, green, blue, red); } +MyContainer { background: conic-gradient(from 45deg at 50% 50%, #f06 0%, #fc0 25%, #0c6 50%, #06f 75%, #f06 100%); } +// end::css-css-015[] + +// tag::css-css-016[] +MyStripe { background: repeating-linear-gradient(45deg, #eee 0px, #eee 10px, #ccc 10px, #ccc 20px); } +MyTarget { background: repeating-radial-gradient(circle at center, #fff, #fff 8px, #c33 8px, #c33 16px); } +// end::css-css-016[] + +// tag::css-css-017[] +MyOverlay { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(12px); +} + +MyBlurredImage { + filter: blur(4px); +} + +MyFaded { + filter: brightness(0.7) contrast(1.15); +} + +MyGrayscale { + filter: grayscale(1); +} + +MySepia { + filter: sepia(0.8) saturate(1.1); +} +// end::css-css-017[] + +// tag::css-css-018[] +SomeStyle { + background-image: url(images/my-image.png); + cn1-source-dpi: 160; +} +// end::css-css-018[] + +// tag::css-css-019[] +SomeStyle { + background-image: url(images/my-image.png); + cn1-source-dpi: 0; +} +// end::css-css-019[] + +// tag::css-css-020[] +#Constants { + defaultSourceDPIInt: 0; +} +// end::css-css-020[] + +// tag::css-css-021[] +MyStyle { + background-image: url(images/mymultiimage.png); +} +// end::css-css-021[] + +// tag::css-css-022[] +Images { + background-image: url(images/NowLogo.png), + url(images/Username-icon.png), + url(images/Password-icon.png), + url(images/Name-icon.png), + url(images/Email-icon.png), + url(images/SeaIce.png), + url(images/Back-icon.png), + url(images/Source-icon.png), + url(images/Date-icon.png), + url(images/Arrow-right.png), + url(images/Share-icon.png), + url(images/Text-icon.png), + url(images/Comments-icon.png), + url(images/RedPlanet.png), + url(images/News-icon.png), + url(images/Channels-icon.png), + url(images/Bookmarks-icon.png), + url(images/Overview-icon.png), + url(images/Calendar-icon.png), + url(images/Timeline-icon.png), + url(images/Profile-icon.png), + url(images/Widgets-icon.png), + url(images/Settings-icon.png), + url(images/Bookmark-icon.png); +} +// end::css-css-022[] + +// tag::css-css-023[] +Images { + background-image: url(http://solutions.weblite.ca/logo.png); +} +// end::css-css-023[] + +// tag::css-css-024[] +NinePiece { + border-image:url('dashbg_landscape.png'); +} +// end::css-css-024[] + +// tag::css-css-025[] +NinePiece { + border-image:url('dashbg_landscape.png'); + border-image-slice:10% 49%; /*vertical horizontal*/ +} + +NinePiece2 { + border-image:url('dashbg_landscape.png'); + border-image-slice:10% 49% 20%; /*top horizontal bottom*/ +} + +NinePiece3 { + border-image:url('dashbg_landscape.png'); + border-image-slice:10% 30% 40% 20%; /*top right bottom left*/ +} + +NinePiece4 { + border-image:url('dashbg_landscape.png'); + border-image-slice:10%; /*all*/ +} + +// end::css-css-025[] + +// tag::css-css-026[] +MyContainer { + background-image: url(myimage.png); + cn1-background-type: cn1-image-scaled-fill; +} +// end::css-css-026[] + +// tag::css-css-027[] +SideCommand { + font-family: "native:MainThin"; +} +// end::css-css-027[] + +// tag::css-css-028[] +@font-face { + font-family: "Montserrat"; + src: url(res/Montserrat-Regular.ttf); +} +// end::css-css-028[] + +// tag::css-css-029[] +MyLabel { + font-family: "Montserrat"; +} +// end::css-css-029[] + +// tag::css-css-030[] +@font-face { + font-family: "MyFont"; + src: url(http://example.com/path/to/myfont.ttf); +} +// end::css-css-030[] + +// tag::css-css-031[] +@font-face { + font-family: "FontAwesome"; + src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/fontawesome-webfont.ttf); +} +// end::css-css-031[] + +// tag::css-css-032[] +#Constants { + defaultFontSizeInt: 18; +} +// end::css-css-032[] + +// tag::css-css-033[] +#Constants { + defaultFontSizeInt: 18; + defaultDesktopFontSizeInt: 14; +} +// end::css-css-033[] + +// tag::css-css-034[] +@font-face { + font-family: "Montserrat"; + src: url(res/Montserrat-Regular.ttf); +} + +@font-face { + font-family: "Montserrat-Bold"; + src: url(res/Montserrat-Bold.ttf); +} + +@font-face { + font-family: "FontAwesome"; + src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/fontawesome-webfont.ttf); +} + +PlainText0p5mm { + font-size: 0.5mm; +} + +PlainText1mm { + font-size: 1mm; +} + +PlainText2mm { + font-size: 2mm; +} + +PlainText5mm { + font-size: 5mm; +} + +PlainText10mm { + font-size: 10mm; +} + +PlainText50mm { + font-size: 50mm; +} + +PlainTextSmall { + font-size: small; +} + +PlainTextMedium { + font-size: medium; +} + +PlainTextLarge { + font-size: large; +} + +PlainText3pt { + font-size: 3pt; +} + +PlainText6pt { + font-size: 6pt; +} + +PlainText12pt { + font-size: 12pt; +} + +PlainText20pt { + font-size: 20pt; +} + +PlainText36pt { + font-size: 36pt; +} + +BoldText { + font-weight: bold; +} + +BoldText1mm { + font-weight: bold; + font-size: 1mm; +} + +BoldText2mm { + font-weight: bold; + font-size: 2mm; +} + +BoldText3mm { + font-weight: bold; + font-size: 3mm; +} + +BoldText5mm { + font-weight: bold; + font-size: 5mm; +} + +ItalicText { + font-style: italic; +} + +ItalicText3mm { + font-style: italic; + font-size: 3mm; +} + +ItalicBoldText { + font-style: italic; + font-weight: bold; +} + +PlainTextUnderline { + text-decoration: underline; +} + +BoldTextUnderline { + text-decoration: underline; + font-weight: bold; +} + +ItalicTextUnderline { + text-decoration: underline; + font-style: italic; +} + +PlainText3d { + text-decoration: cn1-3d; + color:white; + background-color: #3399ff +} + +BoldText3d { + text-decoration: cn1-3d; + font-weight: bold; + color:white; + background-color: #3399ff; +} + +ItalicText3d { + text-decoration: cn1-3d; + font-style: italic; + color:white; + background-color: #3399ff; +} + +PlainText3dLowered { + text-decoration: cn1-3d-lowered; + color:black; + background-color: #3399ff; +} + +BoldText3dLowered { + text-decoration: cn1-3d-lowered; + font-weight: bold; + color:black; + background-color: #3399ff; +} + +ItalicText3dLowered { + text-decoration: cn1-3d-lowered; + font-style: italic; + color:black; + background-color: #3399ff; +} + +PlainText3dShadow { + text-decoration: cn1-3d-shadow-north; + color:white; + background-color: #3399ff; +} + +BoldText3dShadow { + text-decoration: cn1-3d-shadow-north; + font-weight: bold; + color:white; + background-color: #3399ff; +} + +ItalicText3dShadow { + text-decoration: cn1-3d-shadow-north; + font-style: italic; + color:white; + background-color: #3399ff; +} + + +MainThin { + + font-size: 200%; + background: radial-gradient(circle at top left, yellow, blue 100%); +} + +MainRegular0001 { + font-family: "native:MainRegular"; + /*background: cn1-pill-border; + background-color: red;*/ + color: blue; + border: 1px cn1-pill-border blue; + /*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/ + padding: 2mm; +} + +MainRegular0001.pressed { + font-family: "native:MainRegular"; + background: cn1-pill-border blue; + /*background-color: red;*/ + color: white; + border: 1px solid white; + /*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/ + padding: 2mm; +} + +Heading { + font-size: 4mm; + font-family: "Montserrat-Bold"; + color: black; + padding: 2mm; + text-align: center; +} + +XMLVIewIcon { + font-family: "FontAwesome"; +} +// end::css-css-034[] + +// tag::css-css-035[] +Button { + color: #222222; +} + +@media (prefers-color-scheme: dark) { + Button { + color: #f0f0f0; + background-color: #000000; + } + + Button.selected { + color: #ff0000; + } +} +// end::css-css-035[] + +// tag::css-css-036[] +Label { + color: black; +} + +@media platform-and { + Label { + color: green; + } +} + +@media platform-ios { + Label { + color: red; + } +} +// end::css-css-036[] + +// tag::css-css-037[] +@media platform-and, density-high { + .... +} +// end::css-css-037[] + +// tag::css-css-038[] +@media platform-ios, density-high, density-low { + .... +} +// end::css-css-038[] + +// tag::css-css-039[] +@media device-desktop, platform-mac { + .... +} +// end::css-css-039[] + +// tag::css-css-040[] +Label { + font-size: 3mm; +} +// end::css-css-040[] + +// tag::css-css-041[] +#Constants { + device-desktop-font-scale: "1.5"; +} + +Label { + font-size: 3mm; +} +// end::css-css-041[] + +// tag::css-css-042[] +Label { + font-size: 3mm; +} + +@media device-desktop { + Label { + font-size: 4.5mm; + } +} +// end::css-css-042[] + +// tag::css-css-043[] +#Constants { + device-phone-font-scale: "1.5"; + device-tablet-font-scale: "1.2"; + device-desktop-font-scale: "1.4"; + platform-ios-font-scale: "0.9"; + density-low-font-scale: "1.2"; + platform-ios-density-low-font-scale: "1.3"; +} +// end::css-css-043[] diff --git a/docs/demos/common/src/main/css/developer-guide/miscellaneous-features.css b/docs/demos/common/src/main/css/developer-guide/miscellaneous-features.css new file mode 100644 index 00000000000..1a5180b0ef3 --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/miscellaneous-features.css @@ -0,0 +1,18 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::miscellaneous-features-css-001[] +#Constants { + pullToRefreshModernBool: true; + pullToRefreshIndicatorDiameterMm: 8; /* 8mm circle */ + pullToRefreshIndicatorStrokeMm: "0.6"; /* 0.6mm stroke thickness */ +} + +TabIndicator { + /* The pull-to-refresh arc shares its color with the animated tab + indicator -- both follow the brand's accent. */ + color: #007aff; + background-color: transparent; + padding: 0; + margin: 0; +} +// end::miscellaneous-features-css-001[] diff --git a/docs/demos/common/src/main/css/developer-guide/native-themes.css b/docs/demos/common/src/main/css/developer-guide/native-themes.css new file mode 100644 index 00000000000..d7634d92fa3 --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/native-themes.css @@ -0,0 +1,73 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::native-themes-css-001[] +#Constants { + includeNativeBool: true; + darkModeBool: true; +} + +/* Touch the four UIIDs that paint with primary / primary-container. + The role-as-rule pattern keeps brand changes localised. */ +Button { background-color: #00796b; } +Button.pressed { background-color: #4db6ac; color: #00251a; } +RaisedButton { background-color: #b2dfdb; color: #00251a; } +RaisedButton.pressed { background-color: #80cbc4; } +SelectedTab { color: #00796b; } +BackCommand { color: #00796b; } +TitleCommand { color: #00796b; } +// end::native-themes-css-001[] + +// tag::native-themes-css-002[] +#Constants { + includeNativeBool: true; + darkModeBool: true; + + /* Override the native theme's accent palette. The compiler picks + up these declarations from #Constants and exports them as + @accent-color / @accent-color-dark theme constants so every + UIID bound to var(--accent-color) in the parent native theme + picks them up at app launch - no per-UIID rule edit needed. */ + --accent-color: #ff2d95; + --accent-color-dark: #ff2d95; + --accent-pressed-color: #c71a75; + --accent-pressed-color-dark: #c71a75; + --accent-on-color: #ffffff; + + /* Material 3 RaisedButton uses a separate "container" tonal + pair; iOS ignores these (no bindings reference them) so it's + safe to set them unconditionally. */ + --accent-container-color: #ff2d95; + --accent-container-color-dark: #ff2d95; + --accent-on-container-color: #ffffff; + --accent-on-container-color-dark: #ffffff; +} +// end::native-themes-css-002[] + +// tag::native-themes-css-003[] +#Constants { + includeNativeBool: true; + darkModeBool: true; +} + +/* Tweak only what's different. Everything you do not redeclare + keeps coming from the native theme. */ +RaisedButton { background-color: #d81b60; } +RaisedButton.pressed { background-color: #b71c5c; } +RaisedButton.disabled { background-color: #ffd6e2; color: #ffffff; } + +@media (prefers-color-scheme: dark) { + RaisedButton { background-color: #ff80ab; color: #4a0026; } + RaisedButton.pressed { background-color: #f06292; } +} +// end::native-themes-css-003[] + +// tag::native-themes-css-004[] +DangerButton { + cn1-derive: RaisedButton; + background-color: #d32f2f; +} +DangerButton.pressed { + cn1-derive: RaisedButton; + background-color: #b71c1c; +} +// end::native-themes-css-004[] diff --git a/docs/demos/common/src/main/css/developer-guide/svg-transcoder.css b/docs/demos/common/src/main/css/developer-guide/svg-transcoder.css new file mode 100644 index 00000000000..8e881fa3878 --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/svg-transcoder.css @@ -0,0 +1,25 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::svg-transcoder-css-001[] +HomeIcon { + background: url(home.svg); + cn1-svg-width: 6mm; + cn1-svg-height: 6mm; + bg-type: image_scaled_fit; +} +// end::svg-transcoder-css-001[] + +// tag::svg-transcoder-css-002[] +StarIcon { background: url(star.svg); cn1-svg-width: 4mm; cn1-svg-height: 4mm; } +PrimaryIcon { background: url(home.svg); cn1-svg-width: 6mm; cn1-svg-height: 6mm; } +LogoBanner { background: url(logo.svg); cn1-svg-width: 32mm; cn1-svg-height: 12mm; } +// end::svg-transcoder-css-002[] + +// tag::svg-transcoder-css-003[] +SpinnerStyle { + background: url(spinner.json); + cn1-svg-width: 12mm; + cn1-svg-height: 12mm; + bg-type: image_scaled_fit; +} +// end::svg-transcoder-css-003[] diff --git a/docs/demos/common/src/main/css/developer-guide/the-components-of-codename-one.css b/docs/demos/common/src/main/css/developer-guide/the-components-of-codename-one.css new file mode 100644 index 00000000000..96736eb302e --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/the-components-of-codename-one.css @@ -0,0 +1,19 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::the-components-of-codename-one-css-001[] +#Constants { + tabsAnimatedIndicatorBool: true; + tabsAnimatedIndicatorDurationInt: 200; /* tween duration in ms */ + tabsAnimatedIndicatorThicknessMm: 1; /* underline thickness */ +} + +TabIndicator { + /* The indicator picks up its color from this UIID's fg. If the + UIID isn't defined or has fgColor == 0, the indicator falls + back to the currently-selected tab's fgColor. */ + color: #007aff; + background-color: transparent; + padding: 0; + margin: 0; +} +// end::the-components-of-codename-one-css-001[] diff --git a/docs/demos/common/src/main/css/developer-guide/tvplatforms.css b/docs/demos/common/src/main/css/developer-guide/tvplatforms.css new file mode 100644 index 00000000000..8638a1cab3b --- /dev/null +++ b/docs/demos/common/src/main/css/developer-guide/tvplatforms.css @@ -0,0 +1,15 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::tvplatforms-css-001[] +Label { + color: black; +} + +@media device-tv { + Label { + /* Larger type for the 10-foot UI */ + font-size: 3mm; + color: white; + } +} +// end::tvplatforms-css-001[] diff --git a/docs/demos/common/src/main/css/guide-snippets-theme.css b/docs/demos/common/src/main/css/guide-snippets-theme.css new file mode 100644 index 00000000000..42fff812771 --- /dev/null +++ b/docs/demos/common/src/main/css/guide-snippets-theme.css @@ -0,0 +1,275 @@ +/** + * Compiled developer-guide CSS snippets. + * Complete examples that do not require external assets or generated image borders live here. + */ +#Constants { + includeNativeBool: true; + defaultSourceDPIInt: "0"; +} + +/* tag::native-themes-css-001[] */ +#Constants { + includeNativeBool: true; + darkModeBool: true; +} + +/* Touch the four UIIDs that paint with primary / primary-container. + The role-as-rule pattern keeps brand changes localised. */ +Button { background-color: #00796b; } +Button.pressed { background-color: #4db6ac; color: #00251a; } +RaisedButton { background-color: #b2dfdb; color: #00251a; } +RaisedButton.pressed { background-color: #80cbc4; } +SelectedTab { color: #00796b; } +BackCommand { color: #00796b; } +TitleCommand { color: #00796b; } +/* end::native-themes-css-001[] */ + +/* tag::native-themes-css-002[] */ +#Constants { + includeNativeBool: true; + darkModeBool: true; + + /* Override the native theme's accent palette. The compiler picks + up these declarations from #Constants and exports them as + @accent-color / @accent-color-dark theme constants so every + UIID bound to var(--accent-color) in the parent native theme + picks them up at app launch - no per-UIID rule edit needed. */ + --accent-color: #ff2d95; + --accent-color-dark: #ff2d95; + --accent-pressed-color: #c71a75; + --accent-pressed-color-dark: #c71a75; + --accent-on-color: #ffffff; + + /* Material 3 RaisedButton uses a separate "container" tonal + pair; iOS ignores these (no bindings reference them) so it's + safe to set them unconditionally. */ + --accent-container-color: #ff2d95; + --accent-container-color-dark: #ff2d95; + --accent-on-container-color: #ffffff; + --accent-on-container-color-dark: #ffffff; +} +/* end::native-themes-css-002[] */ + +/* tag::native-themes-css-003[] */ +#Constants { + includeNativeBool: true; + darkModeBool: true; +} + +/* Tweak only what's different. Everything you do not redeclare + keeps coming from the native theme. */ +RaisedButton { background-color: #d81b60; } +RaisedButton.pressed { background-color: #b71c5c; } +RaisedButton.disabled { background-color: #ffd6e2; color: #ffffff; } + +@media (prefers-color-scheme: dark) { + RaisedButton { background-color: #ff80ab; color: #4a0026; } + RaisedButton.pressed { background-color: #f06292; } +} +/* end::native-themes-css-003[] */ + +/* tag::native-themes-css-004[] */ +DangerButton { + cn1-derive: RaisedButton; + background-color: #d32f2f; +} +DangerButton.pressed { + cn1-derive: RaisedButton; + background-color: #b71c1c; +} +/* end::native-themes-css-004[] */ + +/* tag::the-components-of-codename-one-css-001[] */ +#Constants { + tabsAnimatedIndicatorBool: true; + tabsAnimatedIndicatorDurationInt: 200; /* tween duration in ms */ + tabsAnimatedIndicatorThicknessMm: 1; /* underline thickness */ +} + +TabIndicator { + /* The indicator picks up its color from this UIID's fg. If the + UIID isn't defined or has fgColor == 0, the indicator falls + back to the currently-selected tab's fgColor. */ + color: #007aff; + background-color: transparent; + padding: 0; + margin: 0; +} +/* end::the-components-of-codename-one-css-001[] */ + +/* tag::tvplatforms-css-001[] */ +Label { + color: black; +} + +@media device-tv { + Label { + /* Larger type for the 10-foot UI */ + font-size: 3mm; + color: white; + } +} +/* end::tvplatforms-css-001[] */ + +/* tag::miscellaneous-features-css-001[] */ +#Constants { + pullToRefreshModernBool: true; + pullToRefreshIndicatorDiameterMm: 8; /* 8mm circle */ + pullToRefreshIndicatorStrokeMm: "0.6"; /* 0.6mm stroke thickness */ +} + +TabIndicator { + /* The pull-to-refresh arc shares its color with the animated tab + indicator -- both follow the brand's accent. */ + color: #007aff; + background-color: transparent; + padding: 0; + margin: 0; +} +/* end::miscellaneous-features-css-001[] */ + +/* tag::css-css-001[] */ +Button { + text-align: center; + border: 1pt solid gray; + background-color: transparent; +} + +Button.pressed { + background-color: gray; +} +/* end::css-css-001[] */ + +/* tag::css-css-002[] */ +MyButton { + cn1-derive: Button; + background-color: blue; +} +/* end::css-css-002[] */ + +/* tag::css-css-003[] */ +#Device { + min-resolution: 120dpi; + max-resolution: 480dpi; + resolution: 480dpi; +} +/* end::css-css-003[] */ + +/* tag::css-css-007[] */ +#Constants { + --main-bg-color: red; +} + +MyContainer { + background-color: var(--main-bg-color); +} +/* end::css-css-007[] */ + +/* tag::css-css-008[] */ +#Constants { + --main-bg-color: red; +} + +MyContainer { + background-color: var(--main-bg-color, blue); +} +/* end::css-css-008[] */ + +/* tag::css-css-020[] */ +#Constants { + defaultSourceDPIInt: 0; +} +/* end::css-css-020[] */ + +/* tag::css-css-027[] */ +SideCommand { + font-family: "native:MainThin"; +} +/* end::css-css-027[] */ + +/* tag::css-css-032[] */ +#Constants { + defaultFontSizeInt: 18; +} +/* end::css-css-032[] */ + +/* tag::css-css-033[] */ +#Constants { + defaultFontSizeInt: 18; + defaultDesktopFontSizeInt: 14; +} +/* end::css-css-033[] */ + +/* tag::css-css-035[] */ +Button { + color: #222222; +} + +@media (prefers-color-scheme: dark) { + Button { + color: #f0f0f0; + background-color: #000000; + } + + Button.selected { + color: #ff0000; + } +} +/* end::css-css-035[] */ + +/* tag::css-css-036[] */ +Label { + color: black; +} + +@media platform-and { + Label { + color: green; + } +} + +@media platform-ios { + Label { + color: red; + } +} +/* end::css-css-036[] */ + +/* tag::css-css-040[] */ +Label { + font-size: 3mm; +} +/* end::css-css-040[] */ + +/* tag::css-css-041[] */ +#Constants { + device-desktop-font-scale: "1.5"; +} + +Label { + font-size: 3mm; +} +/* end::css-css-041[] */ + +/* tag::css-css-042[] */ +Label { + font-size: 3mm; +} + +@media device-desktop { + Label { + font-size: 4.5mm; + } +} +/* end::css-css-042[] */ + +/* tag::css-css-043[] */ +#Constants { + device-phone-font-scale: "1.5"; + device-tablet-font-scale: "1.2"; + device-desktop-font-scale: "1.4"; + platform-ios-font-scale: "0.9"; + density-low-font-scale: "1.2"; + platform-ios-density-low-font-scale: "1.3"; +} +/* end::css-css-043[] */ diff --git a/docs/demos/common/src/main/css/theme.css b/docs/demos/common/src/main/css/theme.css index 1b645201b06..585e4144f64 100644 --- a/docs/demos/common/src/main/css/theme.css +++ b/docs/demos/common/src/main/css/theme.css @@ -50,5 +50,5 @@ SideCommand { color: black; font-family: "native:MainLight"; font-size: 4mm; - border-bottom: 2px solid #cccccc; + border: 1px solid #cccccc; } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/DemoRegistry.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/DemoRegistry.java index 6e233f5dbd6..fcb7c06f66d 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/DemoRegistry.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/DemoRegistry.java @@ -12,6 +12,10 @@ import com.codenameone.developerguide.animations.SlideTransitionsDemo; import com.codenameone.developerguide.animations.SwipeBackSupportDemo; import com.codenameone.developerguide.animations.UnlayoutAnimationsDemo; +import com.codenameone.developerguide.screenshots.GuideStaticScreenshotDemos; +import com.codename1.ui.Component; +import com.codename1.ui.Display; +import com.codename1.ui.layouts.BorderLayout; import java.util.Arrays; import java.util.Collections; @@ -34,7 +38,130 @@ public final class DemoRegistry { new SlideTransitionsDemo(), new BubbleTransitionDemo(), new MorphTransitionDemo(), - new SwipeBackSupportDemo() + new SwipeBackSupportDemo(), + GuideStaticScreenshotDemos.flowLayout("Flow Layout", Component.LEFT, Component.TOP), + GuideStaticScreenshotDemos.flowLayout("Flow Layout Center", Component.CENTER, Component.TOP), + GuideStaticScreenshotDemos.flowLayout("Flow Layout Right", Component.RIGHT, Component.TOP), + GuideStaticScreenshotDemos.flowLayout("Flow Layout Center Middle", Component.CENTER, Component.CENTER), + GuideStaticScreenshotDemos.boxLayoutY(), + GuideStaticScreenshotDemos.boxLayoutX(false), + GuideStaticScreenshotDemos.boxLayoutX(true), + GuideStaticScreenshotDemos.borderLayout("Border Layout", false, false), + GuideStaticScreenshotDemos.borderLayout("Border Layout Center", true, false), + GuideStaticScreenshotDemos.borderLayout("Border Layout RTL", false, true), + GuideStaticScreenshotDemos.gridLayout("Grid Layout 2x2", 2, 2), + GuideStaticScreenshotDemos.gridLayout("Grid Layout 2x4", 2, 4), + GuideStaticScreenshotDemos.gridAutoFit("Grid AutoFit Portrait", false), + GuideStaticScreenshotDemos.gridAutoFit("Grid AutoFit Landscape", true), + GuideStaticScreenshotDemos.buttonDemo(), + GuideStaticScreenshotDemos.linkButtonDemo(), + GuideStaticScreenshotDemos.raisedFlatButtonsDemo(), + GuideStaticScreenshotDemos.radioCheckboxDemo(), + GuideStaticScreenshotDemos.componentGroupDemo(), + GuideStaticScreenshotDemos.multiButtonDemo(), + GuideStaticScreenshotDemos.spanButtonDemo(), + GuideStaticScreenshotDemos.spanLabelDemo(), + GuideStaticScreenshotDemos.onOffSwitchDemo(), + GuideStaticScreenshotDemos.tabsDemo(0), + GuideStaticScreenshotDemos.tabsDemo(1), + GuideStaticScreenshotDemos.tabsDemo(2), + GuideStaticScreenshotDemos.pickerDemo(), + GuideStaticScreenshotDemos.floatingHintDemo(), + GuideStaticScreenshotDemos.accordionDemo(), + GuideStaticScreenshotDemos.floatingActionDemo(false), + GuideStaticScreenshotDemos.floatingActionDemo(true), + GuideStaticScreenshotDemos.splitPaneDemo(), + GuideStaticScreenshotDemos.sliderDemo(), + GuideStaticScreenshotDemos.graphicsHiWorldDemo(), + GuideStaticScreenshotDemos.graphicsGlassPaneDemo(), + GuideStaticScreenshotDemos.shapedClippingDemo(), + GuideStaticScreenshotDemos.fontImageDemo("FontImage Fixed", 0), + GuideStaticScreenshotDemos.fontImageDemo("FontImage Style", 1), + GuideStaticScreenshotDemos.fontImageDemo("FontImage Material", 2), + GuideStaticScreenshotDemos.dialogDemo("Dialog South", dialog -> dialog.showPacked(BorderLayout.SOUTH, true)), + GuideStaticScreenshotDemos.dialogDemo("Dialog Bottom Half", dialog -> + dialog.show(0, Display.getInstance().getDisplayHeight() / 2, 0, 0)), + GuideStaticScreenshotDemos.dialogDemo("Dialog Tint", dialog -> dialog.showPacked(BorderLayout.CENTER, true)), + GuideStaticScreenshotDemos.dialogDemo("Dialog Green Tint", dialog -> { + dialog.setTintColor(0x3300aa44); + dialog.showPacked(BorderLayout.CENTER, true); + }), + GuideStaticScreenshotDemos.dialogDemo("Dialog Blur", dialog -> { + dialog.setBlurBackgroundRadius(8); + dialog.showPacked(BorderLayout.CENTER, true); + }), + GuideStaticScreenshotDemos.dialogDemo("Dialog Blur No Tint", dialog -> { + dialog.setTintColor(0); + dialog.setBlurBackgroundRadius(8); + dialog.showPacked(BorderLayout.CENTER, true); + }), + GuideStaticScreenshotDemos.interactionDialogDemo() + ) + ); + private static final List SCREENSHOTS = Collections.unmodifiableList( + Arrays.asList( + screenshot("layout-animations", "Layout Animations", "layout-animation-1.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-2.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-3.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-4.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-5.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-6.png"), + screenshot("layout-animations", "Layout Animations", "layout-animation-7.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-slide.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-slide-vertical.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-slide-fade.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-cover.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-uncover.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-fade.png"), + screenshot("slide-transitions", "Slide Transitions", "transition-flip.png"), + screenshot("bubble-transition", "Bubble Transition", "transition-bubble.png"), + screenshot("morph-transition", "Morph Transition", "mighty-morphing-components-1.png"), + screenshot("flow-layout", "Flow Layout", "flow-layout.png"), + screenshot("flow-layout-center", "Flow Layout Center", "flow-layout-center.png"), + screenshot("flow-layout-right", "Flow Layout Right", "flow-layout-right.png"), + screenshot("flow-layout-center-middle", "Flow Layout Center Middle", "flow-layout-center-middle.png"), + screenshot("box-layout-y", "BoxLayout Y", "box-layout-y.png"), + screenshot("box-layout-x", "BoxLayout X", "box-layout-x.png"), + screenshot("box-layout-x-no-grow", "BoxLayout X No Grow", "box-layout-x-no-grow.png"), + screenshot("border-layout", "Border Layout", "border-layout.png"), + screenshot("border-layout-center", "Border Layout Center", "border-layout-center.png"), + screenshot("border-layout-rtl", "Border Layout RTL", "border-layout-RTL.png"), + screenshot("grid-layout-2x2", "Grid Layout 2x2", "grid-layout-2x2.png"), + screenshot("grid-layout-2x4", "Grid Layout 2x4", "grid-layout-2x4.png"), + screenshot("grid-layout-autofit-portrait", "Grid AutoFit Portrait", "grid-layout-autofit-portrait.png"), + screenshot("grid-layout-autofit-landscape", "Grid AutoFit Landscape", "grid-layout-autofit-landscape.png"), + screenshot("components-button", "Button", "components-button.png"), + screenshot("components-link-button", "Link Button", "components-link-button.png"), + screenshot("raised-flat-buttons", "Raised and Flat Buttons", "raised-flat-buttons.png"), + screenshot("components-radiobutton-checkbox", "RadioButton and CheckBox", "components-radiobutton-checkbox.png"), + screenshot("components-componentgroup", "ComponentGroup", "components-componentgroup.png"), + screenshot("components-multibutton", "MultiButton", "components-multibutton.png"), + screenshot("components-spanbutton", "SpanButton", "components-spanbutton.png"), + screenshot("components-spanlabel", "SpanLabel", "components-spanlabel.png"), + screenshot("components-onoffswitch", "OnOffSwitch", "components-onoffswitch.png"), + screenshot("components-tabs", "Tabs", "components-tabs.png"), + screenshot("components-tabs-swipe1", "Swipeable Tabs Page 1", "components-tabs-swipe1.png"), + screenshot("components-tabs-swipe2", "Swipeable Tabs Page 2", "components-tabs-swipe2.png"), + screenshot("components-picker", "Picker", "components-picker.png"), + screenshot("components-floatinghint", "FloatingHint", "components-floatinghint.png"), + screenshot("components-accordion", "Accordion", "components-accordion.png"), + screenshot("floating-action", "Floating Action", "floating-action.png"), + screenshot("badge-floating-button", "Badge Floating Button", "badge-floating-button.png"), + screenshot("splitpane", "SplitPane", "splitpane.png"), + screenshot("components-slider", "Slider", "components-slider.png"), + screenshot("graphics-hiworld", "Hi World", "graphics-hiworld.png"), + screenshot("graphics-glasspane", "Glass Pane", "graphics-glasspane.png"), + screenshot("shaped-clipping", "Shaped Clipping", "shaped-clipping.png"), + screenshot("graphics-fontimage-fixed", "FontImage Fixed", "graphics-fontimage-fixed.png"), + screenshot("graphics-fontimage-style", "FontImage Style", "graphics-fontimage-style.png"), + screenshot("graphics-fontimage-material", "FontImage Material", "graphics-fontimage-material.png"), + screenshot("components-dialog-modal-south", "Dialog South", "components-dialog-modal-south.png"), + screenshot("components-dialog-modal-bottom-half", "Dialog Bottom Half", "components-dialog-modal-bottom-half.png"), + screenshot("components-dialog-tint", "Dialog Tint", "components-dialog-tint.png"), + screenshot("components-dialog-green-tint", "Dialog Green Tint", "components-dialog-green-tint.png"), + screenshot("components-dialog-blur", "Dialog Blur", "components-dialog-blur.png"), + screenshot("components-dialog-blur-no-tint", "Dialog Blur No Tint", "components-dialog-blur-no-tint.png"), + screenshot("components-interaction-dialog", "Interaction Dialog", "components-interaction-dialog.png") ) ); @@ -45,4 +172,17 @@ private DemoRegistry() { public static List getDemos() { return DEMOS; } + + public static List getScreenshots() { + return SCREENSHOTS; + } + + private static GuideScreenshot screenshot(String id, String demoTitle, String fileName) { + for (Demo demo : DEMOS) { + if (demo.getTitle().equals(demoTitle)) { + return new GuideScreenshot(id, demo, fileName); + } + } + throw new IllegalStateException("No guide demo registered with title: " + demoTitle); + } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/GuideScreenshot.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/GuideScreenshot.java new file mode 100644 index 00000000000..3b50b1e1449 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/GuideScreenshot.java @@ -0,0 +1,28 @@ +package com.codenameone.developerguide; + +/** + * Stable mapping between a runnable guide demo and the image it documents. + */ +public final class GuideScreenshot { + private final String id; + private final Demo demo; + private final String fileName; + + public GuideScreenshot(String id, Demo demo, String fileName) { + this.id = id; + this.demo = demo; + this.fileName = fileName; + } + + public String getId() { + return id; + } + + public Demo getDemo() { + return demo; + } + + public String getFileName() { + return fileName; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideStaticScreenshotDemos.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideStaticScreenshotDemos.java new file mode 100644 index 00000000000..f6e099282d7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideStaticScreenshotDemos.java @@ -0,0 +1,677 @@ +package com.codenameone.developerguide.screenshots; + +import com.codename1.components.Accordion; +import com.codename1.components.FloatingActionButton; +import com.codename1.components.FloatingHint; +import com.codename1.components.InteractionDialog; +import com.codename1.components.MultiButton; +import com.codename1.components.OnOffSwitch; +import com.codename1.components.SpanButton; +import com.codename1.components.SpanLabel; +import com.codename1.components.SplitPane; +import com.codename1.ui.Button; +import com.codename1.ui.ButtonGroup; +import com.codename1.ui.CheckBox; +import com.codename1.ui.ComboBox; +import com.codename1.ui.Component; +import com.codename1.ui.ComponentGroup; +import com.codename1.ui.Container; +import com.codename1.ui.Dialog; +import com.codename1.ui.Display; +import com.codename1.ui.Font; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import com.codename1.ui.Label; +import com.codename1.ui.RadioButton; +import com.codename1.ui.Slider; +import com.codename1.ui.Stroke; +import com.codename1.ui.Tabs; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.geom.GeneralPath; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.plaf.Border; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.spinner.Picker; +import com.codenameone.developerguide.Demo; + +import java.util.Date; + +/** + * JavaSE-safe forms used to refresh developer-guide screenshots from live code. + */ +public final class GuideStaticScreenshotDemos { + + private GuideStaticScreenshotDemos() { + } + + public static Demo flowLayout(String title, int align, int valign) { + return formDemo(title, "FlowLayout alignment", () -> { + Form form = new Form(displayTitle(title), new BorderLayout()); + Container content = new Container(new FlowLayout(align, valign)); + content.setUIID("PaddedContainer"); + addTiles(content, "One", "Two", "Three", "Four"); + form.add(BorderLayout.CENTER, content); + return form; + }); + } + + public static Demo boxLayoutY() { + return formDemo("BoxLayout Y", "Vertical BoxLayout", () -> { + Form form = new Form("BoxLayout Y", BoxLayout.y()); + form.add(coloredLabel("First", 0x0d47a1)). + add(coloredLabel("Second", 0x1565c0)). + add(coloredLabel("Third", 0x1976d2)); + return form; + }); + } + + public static Demo boxLayoutX(boolean noGrow) { + return formDemo(noGrow ? "BoxLayout X No Grow" : "BoxLayout X", "Horizontal BoxLayout", () -> { + Form form = new Form(noGrow ? "Box X No Grow" : "BoxLayout X", new BorderLayout()); + Container row = new Container(new BoxLayout(noGrow ? BoxLayout.X_AXIS_NO_GROW : BoxLayout.X_AXIS)); + row.setUIID("PaddedContainer"); + addTiles(row, "A", "B", "C"); + form.add(BorderLayout.NORTH, row); + form.add(BorderLayout.CENTER, centeredHint("Horizontal axis")); + return form; + }); + } + + public static Demo borderLayout(String title, boolean centerBehavior, boolean rtl) { + return formDemo(title, "BorderLayout regions", () -> { + Form form = new Form(title, new BorderLayout(centerBehavior ? BorderLayout.CENTER_BEHAVIOR_CENTER : BorderLayout.CENTER_BEHAVIOR_SCALE)); + form.setRTL(rtl); + form.add(BorderLayout.NORTH, coloredLabel("North", 0x455a64)); + form.add(BorderLayout.SOUTH, coloredLabel("South", 0x546e7a)); + form.add(BorderLayout.EAST, coloredLabel("East", 0x00838f)); + form.add(BorderLayout.WEST, coloredLabel("West", 0x00695c)); + form.add(BorderLayout.CENTER, coloredLabel("Center", 0x2e7d32)); + return form; + }); + } + + public static Demo gridLayout(String title, int rows, int cols) { + return formDemo(title, "GridLayout", () -> { + Form form = new Form(title, new GridLayout(rows, cols)); + String[] labels; + if ("Grid Layout 2x2".equals(title)) { + labels = new String[]{"First", "Second", "Third", "Fourth", "Fifth"}; + } else if ("Grid Layout 2x4".equals(title)) { + labels = new String[]{ + "First item with longer text", + "Second item with longer text", + "Third item with longer text", + "Fourth item with longer text", + "Fifth item with longer text", + "Sixth item with longer text", + "Seventh item with longer text", + "Eighth item with longer text" + }; + } else { + labels = new String[rows * cols]; + for (int i = 0; i < labels.length; i++) { + labels[i] = "Cell " + (i + 1); + } + } + for (int i = 0; i < labels.length; i++) { + form.add(coloredLabel(labels[i], 0x1565c0 + ((i + 1) * 0x050505))); + } + return form; + }); + } + + public static Demo gridAutoFit(String title, boolean landscape) { + return formDemo(title, "GridLayout auto-fit", () -> { + Form form = new Form(displayTitle(title), new BorderLayout()); + Container grid = new Container(new GridLayout(landscape ? 2 : 4, landscape ? 4 : 2)); + for (int i = 1; i <= 8; i++) { + grid.add(coloredLabel("Item " + i, 0x00695c + (i * 0x030303))); + } + form.add(BorderLayout.CENTER, grid); + return form; + }); + } + + private static String displayTitle(String title) { + if ("Flow Layout Center Middle".equals(title)) { + return "Flow Layout Middle"; + } + if ("Grid AutoFit Landscape".equals(title)) { + return "AutoFit Landscape"; + } + return title; + } + + public static Demo buttonDemo() { + return formDemo("Button", "Button component", () -> { + Form form = simpleForm("Button"); + form.add(new Button("My Button")); + return form; + }); + } + + public static Demo linkButtonDemo() { + return formDemo("Link Button", "Button styled as a link", () -> { + Form form = simpleForm("Link Button"); + Button link = new Button("Open Account Settings"); + link.setUIID("Link"); + form.add(link); + return form; + }); + } + + public static Demo raisedFlatButtonsDemo() { + return formDemo("Raised and Flat Buttons", "Button styles", () -> { + Form form = simpleForm("Buttons"); + Button raised = new Button("Raised"); + raised.setUIID("RaisedButton"); + Button flat = new Button("Flat"); + form.add(raised).add(flat); + return form; + }); + } + + public static Demo radioCheckboxDemo() { + return formDemo("RadioButton and CheckBox", "Selection controls", () -> { + Form form = simpleForm("Selection"); + RadioButton first = new RadioButton("First Choice"); + RadioButton second = new RadioButton("Second Choice"); + new ButtonGroup(first, second); + first.setSelected(true); + CheckBox updates = new CheckBox("Enable Updates"); + updates.setSelected(true); + form.add(first).add(second).add(updates); + return form; + }); + } + + public static Demo componentGroupDemo() { + return formDemo("ComponentGroup", "Grouped controls", () -> { + Form form = simpleForm("ComponentGroup"); + form.add(ComponentGroup.enclose( + new TextField("Ada", "First Name"), + new TextField("Lovelace", "Last Name"), + new TextField("ada@example.com", "Email"))); + return form; + }); + } + + public static Demo multiButtonDemo() { + return formDemo("MultiButton", "Multi-line buttons", () -> { + Form form = simpleForm("MultiButton"); + for (int i = 1; i <= 3; i++) { + MultiButton button = new MultiButton("Inbox item " + i); + button.setTextLine2("Secondary text with a short summary"); + button.setTextLine3("Today"); + button.setMaterialIcon(FontImage.MATERIAL_MAIL, 4); + form.add(button); + } + return form; + }); + } + + public static Demo spanButtonDemo() { + return formDemo("SpanButton", "Wrapped button text", () -> { + Form form = simpleForm("SpanButton"); + SpanButton button = new SpanButton("A SpanButton wraps long text over several lines while remaining clickable."); + button.setMaterialIcon(FontImage.MATERIAL_INFO, 4); + form.add(button); + return form; + }); + } + + public static Demo spanLabelDemo() { + return formDemo("SpanLabel", "Wrapped label text", () -> { + Form form = simpleForm("SpanLabel"); + SpanLabel label = new SpanLabel("SpanLabel is useful when a label needs enough room to explain a result in normal prose."); + label.setMaterialIcon(FontImage.MATERIAL_DESCRIPTION, 4); + form.add(label); + return form; + }); + } + + public static Demo onOffSwitchDemo() { + return formDemo("OnOffSwitch", "Switch states", () -> { + Form form = simpleForm("OnOffSwitch"); + OnOffSwitch on = modernSwitch(); + on.setValue(true); + OnOffSwitch off = modernSwitch(); + form.add(labeled("Enabled", on)).add(labeled("Disabled", off)); + return form; + }); + } + + private static OnOffSwitch modernSwitch() { + int width = Display.getInstance().convertToPixels(9); + int height = Display.getInstance().convertToPixels(4.5f); + int knobSize = Display.getInstance().convertToPixels(3.5f); + int inset = (height - knobSize) / 2; + + OnOffSwitch switchComponent = new OnOffSwitch(); + switchComponent.setSwitchOnImage(switchImage(width, height, knobSize, inset, 0x21a67a, true)); + switchComponent.setSwitchOffImage(switchImage(width, height, knobSize, inset, 0xb7bdc7, false)); + switchComponent.setSwitchMaskImage(Image.createImage(width, height, 0x00000000)); + switchComponent.setNoTextMode(true); + return switchComponent; + } + + private static Image switchImage(int width, int height, int knobSize, int inset, int trackColor, boolean selected) { + Image image = Image.createImage(width, height, 0x00000000); + Graphics graphics = image.getGraphics(); + graphics.setAntiAliased(true); + graphics.setColor(trackColor); + graphics.fillRoundRect(0, 0, width, height, height, height); + graphics.setColor(0xffffff); + int knobX = selected ? width - knobSize - inset : inset; + graphics.fillRoundRect(knobX, inset, knobSize, knobSize, knobSize, knobSize); + return image; + } + + public static Demo tabsDemo(int selectedIndex) { + String title = selectedIndex == 0 ? "Tabs" : "Swipeable Tabs Page " + selectedIndex; + return formDemo(title, "Tabs component", () -> { + Form form = new Form("Tabs", new BorderLayout()); + Tabs tabs = new Tabs(); + tabs.addTab("News", coloredPanel("News feed", 0xe3f2fd)); + tabs.addTab("Messages", coloredPanel("Messages", 0xe8f5e9)); + tabs.addTab("Settings", coloredPanel("Settings", 0xfff3e0)); + tabs.setSelectedIndex(selectedIndex, false); + form.add(BorderLayout.CENTER, tabs); + return form; + }); + } + + public static Demo pickerDemo() { + return formDemo("Picker", "Picker component", () -> { + Form form = simpleForm("Picker"); + Picker datePicker = new Picker(); + datePicker.setType(Display.PICKER_TYPE_DATE); + datePicker.setDate(new Date(1704067200000L)); + Picker timePicker = new Picker(); + timePicker.setType(Display.PICKER_TYPE_TIME); + timePicker.setTime(10 * 60); + Picker strings = new Picker(); + strings.setType(Display.PICKER_TYPE_STRINGS); + strings.setStrings("Alpha", "Beta", "Gamma"); + strings.setSelectedString("Alpha"); + form.add(datePicker).add(timePicker).add(strings); + return form; + }); + } + + public static Demo floatingHintDemo() { + return formDemo("FloatingHint", "Floating text fields", () -> { + Form form = simpleForm("Floating Hint"); + TextField first = new TextField("", "First Field"); + TextField second = new TextField("", "Second Field"); + form.add(new FloatingHint(first)); + form.add(new FloatingHint(second)); + form.add(new Button("Go")); + return form; + }); + } + + public static Demo accordionDemo() { + return formDemo("Accordion", "Accordion component", () -> { + Form form = simpleForm("Accordion"); + Accordion accordion = new Accordion(); + accordion.addContent("Account", new SpanLabel("Email, password and profile details.")); + SpanLabel notifications = new SpanLabel("Push, email and weekly summaries."); + accordion.addContent("Notifications", notifications); + accordion.addContent("Billing", new SpanLabel("Invoices and payment methods.")); + accordion.expand(notifications); + form.add(accordion); + return form; + }); + } + + public static Demo floatingActionDemo(boolean badge) { + return formDemo(badge ? "Badge Floating Button" : "Floating Action", "Floating action button", () -> { + if (badge) { + Form form = simpleForm("Badge"); + Button chat = new Button("Chat"); + FontImage.setMaterialIcon(chat, FontImage.MATERIAL_CHAT, 7); + + FloatingActionButton badgeButton = FloatingActionButton.createBadge("33"); + Container badgedChat = badgeButton.bindFabToContainer(chat, Component.RIGHT, Component.TOP); + form.add(FlowLayout.encloseCenter(badgedChat)); + + TextField changeBadgeValue = new TextField("33"); + changeBadgeValue.addDataChangedListener((type, index) -> { + badgeButton.setText(changeBadgeValue.getText()); + badgeButton.getParent().revalidate(); + }); + form.add(changeBadgeValue); + return form; + } + FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD); + FloatingActionButton people = fab.createSubFAB(FontImage.MATERIAL_PEOPLE, ""); + FloatingActionButton contacts = fab.createSubFAB(FontImage.MATERIAL_IMPORT_CONTACTS, ""); + + Form form = new Form("Floating Action", new BorderLayout()); + Container content = new Container(new BorderLayout()); + content.setUIID("PaddedContainer"); + content.add(BorderLayout.NORTH, new Label("Expanded actions")); + Container actions = new Container(BoxLayout.y()); + actions.add(FlowLayout.encloseRight(new Label("People"), people)); + actions.add(FlowLayout.encloseRight(new Label("Contacts"), contacts)); + actions.add(FlowLayout.encloseRight(fab)); + content.add(BorderLayout.CENTER, actions); + form.add(BorderLayout.CENTER, content); + return form; + }); + } + + public static Demo splitPaneDemo() { + return formDemo("SplitPane", "Split pane component", () -> { + Label left = coloredLabel("Master", 0x0d47a1); + Label right = coloredLabel("Detail", 0x00695c); + Form form = new Form("SplitPane", new BorderLayout()); + form.add(BorderLayout.CENTER, new SplitPane(SplitPane.HORIZONTAL_SPLIT, left, right, "20%", "40%", "80%")); + return form; + }); + } + + public static Demo graphicsHiWorldDemo() { + return formDemo("Hi World", "Graphics paint", () -> newPaintForm("Hi World", (g, cmp) -> { + g.setColor(0xff0000); + g.fillRect(cmp.getX(), cmp.getY(), cmp.getWidth(), cmp.getHeight()); + g.setColor(0xffffff); + g.setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE)); + g.drawString("Hi World", cmp.getX() + 60, cmp.getY() + 90); + })); + } + + public static Demo graphicsGlassPaneDemo() { + return formDemo("Glass Pane", "Glass pane drawing", () -> { + Form form = simpleForm("Glass Pane"); + Style warningStyle = new Style(0xff0000, 0, Font.getDefaultFont(), (byte) 0); + Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING, warningStyle).toImage(); + TextField field = new TextField("My Field"); + field.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); + field.getAllStyles().setMargin(5, 5, 5, 5); + form.add(field); + form.setGlassPane((g, rect) -> { + int x = field.getAbsoluteX() + field.getWidth() - warningImage.getWidth() / 2; + int y = field.getAbsoluteY() + field.getHeight() / 2 - warningImage.getHeight() / 2; + g.drawImage(warningImage, x, y); + }); + return form; + }); + } + + public static Demo shapedClippingDemo() { + return formDemo("Shaped Clipping", "Graphics clipping", () -> newPaintForm("Shaped Clipping", (g, cmp) -> { + GeneralPath path = new GeneralPath(); + int x = cmp.getX(); + int y = cmp.getY(); + int width = cmp.getWidth(); + int height = cmp.getHeight(); + int shapeWidth = Math.min(width - 120, 360); + int shapeHeight = Math.min(height - 160, 720); + int left = x + (width - shapeWidth) / 2; + int top = y + 80; + path.moveTo(left + shapeWidth * 2 / 5, top); + path.lineTo(left + shapeWidth * 3 / 5, top); + path.lineTo(left + shapeWidth * 3 / 5, top + shapeHeight); + path.lineTo(left + shapeWidth * 2 / 5, top + shapeHeight); + path.lineTo(left + shapeWidth * 2 / 5, top + shapeHeight / 6); + path.lineTo(left + shapeWidth / 10, top + shapeHeight * 2 / 5); + path.lineTo(left + shapeWidth / 10, top + shapeHeight / 4); + path.lineTo(left + shapeWidth * 2 / 5, top); + + g.setColor(0xeeeeee); + g.fillRect(x, y, width, height); + g.setClip(path); + g.drawImage(createGuideIcon(), left, top, shapeWidth, shapeHeight); + g.setClip(path.getBounds()); + g.setColor(0x0d47a1); + g.drawShape(path, new Stroke(2, Stroke.CAP_ROUND, Stroke.JOIN_ROUND, 4)); + })); + } + + public static Demo fontImageDemo(String title, int mode) { + return formDemo(title, "FontImage", () -> { + Form form = simpleForm(title); + Button button = new Button("Save"); + button.setMaterialIcon(FontImage.MATERIAL_SAVE, 5); + if (mode == 1) { + button.getAllStyles().setFgColor(0xd81b60); + } else if (mode == 2) { + button.setText("Material Save"); + button.getAllStyles().setFgColor(0x00796b); + } + form.add(button); + return form; + }); + } + + public static Demo sliderDemo() { + return formDemo("Slider", "Slider values", () -> { + Form form = new Form("Star Slider", BoxLayout.y()); + Slider slider = createStarRankSlider(); + slider.setProgress(5); + form.add(FlowLayout.encloseCenter(slider)); + return form; + }); + } + + public static Demo dialogDemo(String title, DialogCustomizer customizer) { + return new FormDemo(title, "Dialog screenshot", () -> { + Form form = simpleForm(title); + form.add(centeredHint("Dialog host")); + return form; + }) { + @Override + public void show(Form parent) { + Form form = createForm(); + addBack(form, parent); + form.show(); + Display.getInstance().callSerially(() -> { + Dialog dialog = new Dialog("Title"); + dialog.setLayout(new BorderLayout()); + dialog.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); + customizer.customize(dialog); + }); + } + }; + } + + public static Demo interactionDialogDemo() { + return new FormDemo("Interaction Dialog", "InteractionDialog screenshot", () -> { + Form form = new Form("Interaction Dialog", new BorderLayout()); + Container content = new Container(BoxLayout.y()); + content.setUIID("PaddedContainer"); + content.add(new Label("Form")); + content.add(new Button("Refresh")); + content.add(new Button("Details")); + form.add(BorderLayout.WEST, content); + return form; + }) { + @Override + public void show(Form parent) { + Form form = createForm(); + addBack(form, parent); + form.show(); + Display.getInstance().callSerially(() -> { + InteractionDialog dialog = new InteractionDialog("Hello"); + dialog.setAnimateShow(false); + dialog.setRepositionAnimation(false); + dialog.setLayout(new BorderLayout()); + dialog.add(BorderLayout.CENTER, new Label("Hello Dialog")); + Button close = new Button("Close"); + close.addActionListener(event -> dialog.dispose()); + dialog.add(BorderLayout.SOUTH, close); + Dimension preferred = dialog.getContentPane().getPreferredSize(); + int displayWidth = Display.getInstance().getDisplayWidth(); + int dialogWidth = Math.max(preferred.getWidth() + preferred.getWidth() / 6, displayWidth * 2 / 3); + int left = displayWidth - dialogWidth; + dialog.show(0, 0, left, 0); + }); + } + }; + } + + public interface DialogCustomizer { + void customize(Dialog dialog); + } + + private interface FormFactory { + Form create(); + } + + private interface Painter { + void paint(Graphics graphics, Component component); + } + + private static Demo formDemo(String title, String description, FormFactory factory) { + return new FormDemo(title, description, factory); + } + + private static class FormDemo implements Demo { + private final String title; + private final String description; + private final FormFactory factory; + + FormDemo(String title, String description, FormFactory factory) { + this.title = title; + this.description = description; + this.factory = factory; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void show(Form parent) { + Form form = createForm(); + addBack(form, parent); + form.show(); + } + + Form createForm() { + return factory.create(); + } + } + + private static Form simpleForm(String title) { + Form form = new Form(title, BoxLayout.y()); + form.setScrollableY(true); + return form; + } + + private static Form newPaintForm(String title, Painter painter) { + Form form = new Form(title, new BorderLayout()); + form.add(BorderLayout.CENTER, new Component() { + @Override + public void paint(Graphics graphics) { + painter.paint(graphics, this); + } + }); + return form; + } + + private static Container coloredPanel(String text, int color) { + Container panel = new Container(new BorderLayout()); + panel.getAllStyles().setBgColor(color); + panel.getAllStyles().setBgTransparency(255); + panel.add(BorderLayout.CENTER, centeredHint(text)); + return panel; + } + + private static Label coloredLabel(String text, int color) { + Label label = new Label(text); + label.setUIID("Label"); + label.getAllStyles().setFgColor(0xffffff); + label.getAllStyles().setBgColor(color & 0xffffff); + label.getAllStyles().setBgTransparency(255); + label.getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + label.getAllStyles().setPadding(4, 4, 4, 4); + return label; + } + + private static Label centeredHint(String text) { + Label label = new Label(text); + label.setAlignment(Component.CENTER); + return label; + } + + private static Container labeled(String text, Component component) { + Container row = new Container(new BorderLayout()); + row.add(BorderLayout.CENTER, new Label(text)); + row.add(BorderLayout.EAST, component); + return row; + } + + private static void addTiles(Container container, String... labels) { + int[] colors = {0x0d47a1, 0x00695c, 0x6a1b9a, 0xad1457, 0xef6c00}; + for (int i = 0; i < labels.length; i++) { + container.add(coloredLabel(labels[i], colors[i % colors.length])); + } + } + + private static void addBack(Form form, Form parent) { + if (parent != null) { + form.getToolbar().addCommandToLeftBar("Back", null, ignored -> parent.showBack()); + } + } + + private static Slider createStarRankSlider() { + Slider starRank = new Slider(); + starRank.setEditable(true); + starRank.setMinValue(0); + starRank.setMaxValue(10); + Font font = Font.createTrueTypeFont("native:MainLight", "native:MainLight"). + derive(Display.getInstance().convertToPixels(5, true), Font.STYLE_PLAIN); + Style style = new Style(0xffff33, 0, font, (byte) 0); + Image fullStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, style).toImage(); + style.setOpacity(100); + style.setFgColor(0); + Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, style).toImage(); + initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar); + initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(), emptyStar); + initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar); + initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar); + starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5, fullStar.getHeight())); + return starRank; + } + + private static void initStarRankStyle(Style style, Image star) { + style.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH); + style.setBorder(Border.createEmpty()); + style.setBgImage(star); + style.setBgTransparency(0); + } + + private static Image createGuideIcon() { + Image image = Image.createImage(50, 100, 0xffffffff); + Graphics graphics = image.getGraphics(); + graphics.setAntiAliased(true); + graphics.setColor(0x42a5f5); + graphics.fillArc(6, 2, 38, 38, 0, 360); + graphics.setColor(0xffca28); + graphics.fillArc(12, 10, 8, 8, 0, 360); + graphics.fillArc(30, 10, 8, 8, 0, 360); + graphics.setColor(0x1e88e5); + graphics.fillRoundRect(10, 38, 30, 50, 8, 8); + graphics.setColor(0x0d47a1); + graphics.drawLine(18, 88, 12, 98); + graphics.drawLine(32, 88, 38, 98); + return image; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava001Snippet.java new file mode 100644 index 00000000000..93911716e1f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava001Snippet.java @@ -0,0 +1,80 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.ai.*; + +class AiAndSpeechJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::ai-and-speech-java-001[] + // OpenAI (also drives Ollama, vLLM, llama.cpp, and other + // OpenAI-compatible endpoints). + LlmClient openai = LlmClient.openai(apiKey); + + // Local Ollama on the default port (http://localhost:11434). + LlmClient ollama = LlmClient.ollama("llama3.2"); + + // Any OpenAI-compatible endpoint (Together, Groq, Fireworks, vLLM, null). + LlmClient together = LlmClient.localOpenAiCompatible( + "https://api.together.xyz/v1", + apiKey, + "meta-llama/Llama-3.3-70B-Instruct-Turbo"); + + // Anthropic and Google Gemini, both via their OpenAI-compatible + // endpoints. The wire format is identical; only the base URL, + // default model, and auth differ. + LlmClient anthropic = LlmClient.anthropic(apiKey); + LlmClient gemini = LlmClient.gemini(apiKey); + // end::ai-and-speech-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava015Snippet.java new file mode 100644 index 00000000000..9ebcff5ee3f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava015Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class AiAndSpeechJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::ai-and-speech-java-015[] + if (TextToSpeech.isSupported()) { + TtsOptions opts = new TtsOptions() + .setLanguageTag("fr-FR") + .setRate(1.0f); + TextToSpeech.speak("Bonjour", opts); + } + // end::ai-and-speech-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationComponentBindingJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationComponentBindingJava001Snippet.java new file mode 100644 index 00000000000..6f15f3969d1 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationComponentBindingJava001Snippet.java @@ -0,0 +1,84 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.annotations.*; +import com.codename1.binding.BindAttr; +import com.codename1.properties.Property; + +class AnnotationComponentBindingJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::annotation-component-binding-java-001[] + @Bindable + public class LoginModel { + + @Bind(name = "userField", attr = BindAttr.TEXT) + private String user; + public String getUser() { return user; } + public void setUser(String u) { this.user = u; } // <1> + + @Bind(name = "rememberMe", attr = BindAttr.SELECTED) + public boolean remember; // <2> + + @Bind(name = "banner", attr = BindAttr.UIID, twoWay = false) + public String bannerStyle; + + @Bind(name = "fullName", + attr = BindAttr.TEXT, + getter = "computeFullName", + setter = "applyFullName") // <3> + private String fullName; + public String computeFullName() { return fullName.toUpperCase(); } + public void applyFullName(String f){ this.fullName = f.trim(); } + } + // end::annotation-component-binding-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationJsonXmlMappingJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationJsonXmlMappingJava001Snippet.java new file mode 100644 index 00000000000..edc67f2c2bc --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationJsonXmlMappingJava001Snippet.java @@ -0,0 +1,78 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.annotations.*; +import com.codename1.properties.*; + +class AnnotationJsonXmlMappingJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::annotation-json-xml-mapping-java-001[] + @Mapped + @XmlRoot("user") + public class User { + + @JsonProperty("first_name") + @XmlElement("first") + public String firstName; + + public int age; + + // Renders as in XML; omitted from JSON. + @XmlAttribute + @JsonIgnore + public String role; + + public User() { } // <1> + } + // end::annotation-json-xml-mapping-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationSqliteOrmJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationSqliteOrmJava001Snippet.java new file mode 100644 index 00000000000..f7867da9f68 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationSqliteOrmJava001Snippet.java @@ -0,0 +1,78 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.annotations.*; + +class AnnotationSqliteOrmJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::annotation-sqlite-orm-java-001[] + @Entity(table = "users") + public class User { + + @Id(autoIncrement = true) + public long id; + + @Column(name = "full_name", nullable = false) + public String name; + + public int age; + + public java.util.Date createdAt; + + @DbTransient + public String cacheKey; // <1> + + public User() { } + } + // end::annotation-sqlite-orm-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava001Snippet.java new file mode 100644 index 00000000000..f2601ae18bc --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava001Snippet.java @@ -0,0 +1,82 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.oidc.OidcClient; +import com.codename1.io.oidc.OidcTokens; +import com.codename1.util.SuccessCallback; + +class AuthenticationAndIdentityJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::authentication-and-identity-java-001[] + OidcClient.discover("https://accounts.google.com").ready(new SuccessCallback() { + public void onSucess(OidcClient client) { + client.setClientId("YOUR_CLIENT_ID") + .setRedirectUri("com.example.app:/oauth2redirect") + .setScopes("openid", "email", "profile"); + client.authorize().ready(new SuccessCallback() { + public void onSucess(OidcTokens tokens) { + // tokens.getAccessToken() -- bearer for API calls + // tokens.getIdToken() -- JWT with user identity claims + // tokens.getEmail() -- convenience accessor + } + }).except(new SuccessCallback() { + public void onSucess(Throwable err) { + System.out.println("Sign-in failed: " + err.getMessage()); + } + }); + } + }); + // end::authentication-and-identity-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava003Snippet.java new file mode 100644 index 00000000000..4eff83b0b06 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava003Snippet.java @@ -0,0 +1,71 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class AuthenticationAndIdentityJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::authentication-and-identity-java-003[] + AppleSignIn.getInstance().signIn("name email", new AppleSignInCallback() { + public void onSuccess(AppleSignInResult result) { + // result.getIdentityToken() -- JWT to verify on your server + // result.getUserId() -- stable opaque user id, use as PK + // result.getEmail() -- may be a real or relay address + } + public void onError(String err) { } + public void onCancel() { } + }); + // end::authentication-and-identity-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava015Snippet.java new file mode 100644 index 00000000000..739975d418c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava015Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class AuthenticationAndIdentityJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::authentication-and-identity-java-015[] + Oauth2 oauth = new Oauth2( + "https://provider.example.com/oauth2/authorize", + "CLIENT_ID", + "https://example.com/callback", + "openid email", + "https://provider.example.com/oauth2/token", + "CLIENT_SECRET"); + oauth.showAuthentication(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (e.getSource() instanceof AccessToken) { + AccessToken t = (AccessToken) e.getSource(); + // null + } + } + }); + // end::authentication-and-identity-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava002Snippet.java new file mode 100644 index 00000000000..dc4b5d4ce31 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava002Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-002[] + Container cnt = new Container(BoxLayout.y()); + cnt.add(new Label("Just Added")); + // end::basics-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava004Snippet.java new file mode 100644 index 00000000000..a2dfb24a09b --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava004Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import static com.codename1.ui.CN.*; + +class BasicsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-004[] + + // end::basics-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava010Snippet.java new file mode 100644 index 00000000000..6af0fac7c6f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava010Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-010[] + Container cnt = new Container(BoxLayout.y()); + cnt.add(new Label("Just Added")); // <1> + cnt.addAll(new Label("Adding Multiple"), // <2> + new Label("Second One")); + + cnt.add(new Label("Chaining")). // <3> + add(new Label("Value")); + // end::basics-java-010[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava012Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava012Snippet.java new file mode 100644 index 00000000000..f8d53ec4a3c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava012Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava012Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-012[] + Form hi = new Form("Flow Layout", new FlowLayout()); + hi.add(new Label("First")). + add(new Label("Second")). + add(new Label("Third")). + add(new Label("Fourth")). + add(new Label("Fifth")); + hi.show(); + // end::basics-java-012[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava013Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava013Snippet.java new file mode 100644 index 00000000000..aa83dbc651c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava013Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava013Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-013[] + Container flowLayout = FlowLayout.encloseIn( + new Label("First"), + new Label("Second"), + new Label("Third"), + new Label("Fourth"), + new Label("Fifth")); + // end::basics-java-013[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava014Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava014Snippet.java new file mode 100644 index 00000000000..75b42c5abb6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava014Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava014Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-014[] + Form hi = new Form("Box Y Layout", new BoxLayout(BoxLayout.Y_AXIS)); + hi.add(new Label("First")). + add(new Label("Second")). + add(new Label("Third")). + add(new Label("Fourth")). + add(new Label("Fifth")); + // end::basics-java-014[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava015Snippet.java new file mode 100644 index 00000000000..051db2a3c9d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava015Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-015[] + Container box = BoxLayout.encloseX(new Label("First"), + new Label("Second"), + new Label("Third"), + new Label("Fourth"), + new Label("Fifth")); + // end::basics-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava016Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava016Snippet.java new file mode 100644 index 00000000000..08eae92127f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava016Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava016Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-016[] + Form hi = new Form("Border Layout", new BorderLayout()); + hi.add(BorderLayout.CENTER, new Label("Center")). + add(BorderLayout.SOUTH, new Label("South")). + add(BorderLayout.NORTH, new Label("North")). + add(BorderLayout.EAST, new Label("East")). + add(BorderLayout.WEST, new Label("West")); + hi.show(); + // end::basics-java-016[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava017Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava017Snippet.java new file mode 100644 index 00000000000..3c5b3015021 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava017Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava017Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-017[] + Form hi = new Form("Border Layout", new BorderLayout()); + ((BorderLayout)hi.getLayout()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER); + hi.add(BorderLayout.CENTER, new Label("Center")). + add(BorderLayout.SOUTH, new Label("South")). + add(BorderLayout.NORTH, new Label("North")). + add(BorderLayout.EAST, new Label("East")). + add(BorderLayout.WEST, new Label("West")); + hi.show(); + // end::basics-java-017[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava018Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava018Snippet.java new file mode 100644 index 00000000000..df5f00bcba5 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava018Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava018Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-018[] + Form hi = new Form("Grid Layout 2×2", new GridLayout(2, 2)); + hi.add(new Label("First")). + add(new Label("Second")). + add(new Label("Third")). + add(new Label("Fourth")). + add(new Label("Fifth")); + // end::basics-java-018[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava019Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava019Snippet.java new file mode 100644 index 00000000000..85ea2431437 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava019Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava019Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-019[] + GridLayout.encloseIn(new Label("First"), + new Label("Second"), + new Label("Third"), + new Label("Fourth"), + new Label("Fifth")); + // end::basics-java-019[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava024Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava024Snippet.java new file mode 100644 index 00000000000..29a7ea040fd --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava024Snippet.java @@ -0,0 +1,75 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava024Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-024[] + TextModeLayout tl = new TextModeLayout(3, 2); + Form f = new Form("Pixel Perfect", tl); + TextComponent title = new TextComponent().label("Title"); + TextComponent price = new TextComponent().label("Price"); + TextComponent location = new TextComponent().label("Location"); + TextComponent description = new TextComponent().label("Description").multiline(true); + + f.add(tl.createConstraint().horizontalSpan(2), title); + f.add(tl.createConstraint().widthPercentage(30), price); + f.add(tl.createConstraint().widthPercentage(70), location); + f.add(tl.createConstraint().horizontalSpan(2), description); + f.setEditOnShow(title.getField()); + f.show(); + // end::basics-java-024[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava026Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava026Snippet.java new file mode 100644 index 00000000000..5415cf42c3d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava026Snippet.java @@ -0,0 +1,78 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava026Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-026[] + Form hi = new Form("Layered Layout"); + int w = Math.min(Display.getInstance().getDisplayWidth(), Display.getInstance().getDisplayHeight()); + Button settingsLabel = new Button(""); + Style settingsStyle = settingsLabel.getAllStyles(); + settingsStyle.setFgColor(0xff); + settingsStyle.setBorder(null); + settingsStyle.setBgColor(0xff00); + settingsStyle.setBgTransparency(255); + settingsStyle.setFont(settingsLabel.getUnselectedStyle().getFont().derive(w / 3, Font.STYLE_PLAIN)); + FontImage.setMaterialIcon(settingsLabel, FontImage.MATERIAL_SETTINGS); + Button close = new Button(""); + close.setUIID("Container"); + close.getAllStyles().setFgColor(0xff0000); + FontImage.setMaterialIcon(close, FontImage.MATERIAL_CLOSE); + hi.add(LayeredLayout.encloseIn(settingsLabel, + FlowLayout.encloseRight(close))); + // end::basics-java-026[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava027Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava027Snippet.java new file mode 100644 index 00000000000..e1b21fa74f6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava027Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava027Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-027[] + Container cnt = new Container(new LayeredLayout()); + Button btn = new Button("Submit"); + LayeredLayout ll = (LayeredLayout)cnt.getLayout(); + cnt.add(btn); + ll.setInsets(btn, "auto 0 0 auto"); + // end::basics-java-027[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava031Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava031Snippet.java new file mode 100644 index 00000000000..89ad490f14e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava031Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava031Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-031[] + Container cnt = new Container(new LayeredLayout()); + LayeredLayout ll = (LayeredLayout)cnt.getLayout(); + Button btn = new Button("Submit"); + TextField tf = new TextField(); + cnt.add(tf).add(btn); + ll.setInsets(tf, "auto") + .setInsets(btn, "auto auto auto 0") + .setReferenceComponentLeft(btn, tf, 1f); + // end::basics-java-031[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava033Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava033Snippet.java new file mode 100644 index 00000000000..df226b76c3d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava033Snippet.java @@ -0,0 +1,107 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava033Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-033[] + Button button; + hi.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + //natural height, maximum width + c.fill = GridBagConstraints.HORIZONTAL; + button = new Button("Button 1"); + c.weightx = 0.5; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 0; + c.gridy = 0; + hi.addComponent(c, button); + + button = new Button("Button 2"); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 0.5; + c.gridx = 1; + c.gridy = 0; + hi.addComponent(c, button); + + button = new Button("Button 3"); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 0.5; + c.gridx = 2; + c.gridy = 0; + hi.addComponent(c, button); + + button = new Button("Long-Named Button 4"); + c.fill = GridBagConstraints.HORIZONTAL; + c.ipady = 40; //make this component tall + c.weightx = 0.0; + c.gridwidth = 3; + c.gridx = 0; + c.gridy = 1; + hi.addComponent(c, button); + + button = new Button("5"); + c.fill = GridBagConstraints.HORIZONTAL; + c.ipady = 0; //reset to default + c.weighty = 1.0; //request any extra vertical space + c.anchor = GridBagConstraints.PAGE_END; //bottom of space + c.insets = new Insets(10,0,0,0); //top padding + c.gridx = 1; //aligned with button 2 + c.gridwidth = 2; //2 columns wide + c.gridy = 2; //third row + hi.addComponent(c, button); + // end::basics-java-033[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava034Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava034Snippet.java new file mode 100644 index 00000000000..0f49f7830ed --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava034Snippet.java @@ -0,0 +1,126 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava034Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-034[] + Form hi = new Form("GroupLayout"); + + Label label1 = new Label(); + Label label2 = new Label(); + Label label3 = new Label(); + Label label4 = new Label(); + Label label5 = new Label(); + Label label6 = new Label(); + Label label7 = new Label(); + + label1.setText("label1"); + + label2.setText("label2"); + + label3.setText("label3"); + + label4.setText("label4"); + + label5.setText("label5"); + + label6.setText("label6"); + + label7.setText("label7"); + + GroupLayout layout = new GroupLayout(hi.getContentPane()); + hi.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(layout.createParallelGroup(GroupLayout.LEADING) + .add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) + .add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(layout.createSequentialGroup() + .add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) + .addContainerGap(296, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(GroupLayout.TRAILING) + .add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(LayoutStyle.RELATED) + .add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(layout.createParallelGroup(GroupLayout.LEADING) + .add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(150, Short.MAX_VALUE)) + ); + // end::basics-java-034[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava038Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava038Snippet.java new file mode 100644 index 00000000000..fadd2d0e812 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava038Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava038Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::basics-java-038[] + theme = UIManager.initFirstTheme("/theme"); + // end::basics-java-038[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava039Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava039Snippet.java new file mode 100644 index 00000000000..bd898316151 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava039Snippet.java @@ -0,0 +1,62 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava039Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::basics-java-039[] + public void onButton_1ActionEvent(com.codename1.ui.events.ActionEvent ev) { + } + // end::basics-java-039[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava040Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava040Snippet.java new file mode 100644 index 00000000000..191ef7e9b1c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava040Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava040Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::basics-java-040[] + private com.codename1.ui.Button gui_Button_1 = new com.codename1.ui.Button(); + // end::basics-java-040[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava041Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava041Snippet.java new file mode 100644 index 00000000000..6ccb934f03d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava041Snippet.java @@ -0,0 +1,127 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BasicsJava041Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::basics-java-041[] + /** + * GUI builder created Form + * + * @author shai + */ + public class MyForm extends com.codename1.ui.Form { + + public MyForm() { + this(com.codename1.ui.util.Resources.getGlobalResources()); + } + + public MyForm(com.codename1.ui.util.Resources resourceObjectInstance) { + initGuiBuilderComponents(resourceObjectInstance); + } + + //-- DON'T EDIT BELOW THIS LINE!!! + private com.codename1.ui.Label gui_Label_1 = new com.codename1.ui.Label(); + private com.codename1.ui.Button gui_Button_1 = new com.codename1.ui.Button(); + + + // + private void guiBuilderBindComponentListeners() { + EventCallbackClass callback = new EventCallbackClass(); + gui_Button_1.addActionListener(callback); + } + + class EventCallbackClass implements com.codename1.ui.events.ActionListener, com.codename1.ui.events.DataChangedListener { + private com.codename1.ui.Component cmp; + public EventCallbackClass(com.codename1.ui.Component cmp) { + this.cmp = cmp; + } + + public EventCallbackClass() { + } + + public void actionPerformed(com.codename1.ui.events.ActionEvent ev) { + com.codename1.ui.Component sourceComponent = ev.getComponent(); + if(sourceComponent.getParent().getLeadParent() != null) { + sourceComponent = sourceComponent.getParent().getLeadParent(); + } + + if(sourceComponent == gui_Button_1) { + onButton_1ActionEvent(ev); + } + } + + public void dataChanged(int type, int index) { + } + } + private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) { + guiBuilderBindComponentListeners(); + setLayout(new com.codename1.ui.layouts.FlowLayout()); + setTitle("My new title"); + setName("MyForm"); + addComponent(gui_Label_1); + addComponent(gui_Button_1); + gui_Label_1.setText("Hi World"); + gui_Label_1.setName("Label_1"); + gui_Button_1.setText("Click Me"); + gui_Button_1.setName("Button_1"); + }// + + //-- DON'T EDIT ABOVE THIS LINE!!! + public void onButton_1ActionEvent(com.codename1.ui.events.ActionEvent ev) { + } + + } + // end::basics-java-041[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava001Snippet.java new file mode 100644 index 00000000000..8fdd213b3f8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava001Snippet.java @@ -0,0 +1,82 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.Biometrics; +import com.codename1.security.BiometricError; +import com.codename1.security.BiometricException; + +class BiometricAuthenticationJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::biometric-authentication-java-001[] + Biometrics b = Biometrics.getInstance(); + if (!b.canAuthenticate()) { + // Fall back to password + return; + } + b.authenticate("Unlock your account").onResult((success, err) -> { + if (err != null) { + BiometricError code = ((BiometricException) err).getError(); + switch (code) { + case USER_CANCELED: /* user dismissed the prompt */ break; + case LOCKED_OUT: /* too many bad attempts */ break; + case NOT_ENROLLED: /* prompt the user to enrol in Settings */ break; + default: /* generic failure */ break; + } + } else { + // Authenticated. Continue with the gated action. + } + }); + // end::biometric-authentication-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava003Snippet.java new file mode 100644 index 00000000000..7c837de3420 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava003Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class BiometricAuthenticationJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::biometric-authentication-java-003[] + SecureStorage.getInstance().setKeychainAccessGroup("TEAMID123.group.com.example.app"); + // end::biometric-authentication-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava004Snippet.java new file mode 100644 index 00000000000..03b8befc61a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava004Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.AuthenticationOptions; + +class BiometricAuthenticationJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::biometric-authentication-java-004[] + Biometrics.getInstance().authenticate(new AuthenticationOptions() + .setReason("Authorize transfer") // iOS localizedReason; Android title fallback + .setTitle("Confirm payment") // Android only + .setSubtitle("Stripe charge $25.00") // Android only + .setNegativeButtonText("Cancel") // Android only + .setBiometricOnly(true) // reject PIN / passcode fallback + .setSensitiveTransaction(true) // request class-3 ("strong") biometric (Android 30+) + ); + // end::biometric-authentication-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ComponentSelectorJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ComponentSelectorJava001Snippet.java new file mode 100644 index 00000000000..180075e89e8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ComponentSelectorJava001Snippet.java @@ -0,0 +1,74 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import static com.codename1.ui.ComponentSelector.$; + +class ComponentSelectorJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::component-selector-java-001[] + // null + + Button slideUp = $(new Button("Slide Up")) // + .setIcon(FontImage.MATERIAL_EXPAND_LESS) // + .addActionListener(e->{ // + $(e) // + .getParent() // + .find(">*") // + .slideUpAndWait(1000) // + .slideDownAndWait(1000); // + }) + .asComponent(Button.class); // + // end::component-selector-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/CssJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/CssJava001Snippet.java new file mode 100644 index 00000000000..a0b24400019 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/CssJava001Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class CssJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::css-java-001[] + Resources theme = Resources.openLayered("/theme"); + + Label bookmark = new Label(theme.getImage("Bookmark-icon.png")); + // end::css-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DeepLinksRoutingJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DeepLinksRoutingJava001Snippet.java new file mode 100644 index 00000000000..c1777ece4b7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DeepLinksRoutingJava001Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.annotations.Route; +import com.codename1.annotations.RouteParam; +import com.codename1.ui.Form; + +class DeepLinksRoutingJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::deep-links-routing-java-001[] + @Route("/users/:id") + public class ProfileForm extends Form { + public ProfileForm(@RouteParam("id") String id) { + setTitle("Profile " + id); + // null + } + } + // end::deep-links-routing-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava001Snippet.java new file mode 100644 index 00000000000..c89ba553df8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava001Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class DesktopIntegrationJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::desktop-integration-java-001[] + Display.getInstance().setProperty("desktop.titleBar", "native"); + Display.getInstance().setProperty("desktop.interactiveScrollbars", "true"); + // end::desktop-integration-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava002Snippet.java new file mode 100644 index 00000000000..580b02f5c5a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava002Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class DesktopIntegrationJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::desktop-integration-java-002[] + Form hi = new Form("My App", BoxLayout.y()); + + Command about = new Command("About My App"); + about.setDesktopMenu(Command.DESKTOP_MENU_ABOUT); // application menu + + Command open = new Command("Open Filenull"); + open.setDesktopMenu(Command.DESKTOP_MENU_FILE); // File menu + + Command quit = new Command("Quit"); + quit.setDesktopMenu(Command.DESKTOP_MENU_QUIT); // application menu + + hi.addCommand(about); + hi.addCommand(open); + hi.addCommand(quit); + hi.show(); + // end::desktop-integration-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava003Snippet.java new file mode 100644 index 00000000000..13513ac853f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava003Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class DesktopIntegrationJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::desktop-integration-java-003[] + Command save = new Command("Save"); + save.setDesktopMenu(Command.DESKTOP_MENU_FILE); + save.setDesktopShortcut('S'); // Cmd+S on macOS, Ctrl+S on Windows/Linux + + Command saveAs = new Command("Save Asnull"); + saveAs.setDesktopMenu(Command.DESKTOP_MENU_FILE); + saveAs.setDesktopShortcut('S', + Command.DESKTOP_SHORTCUT_MODIFIER_PRIMARY | Command.DESKTOP_SHORTCUT_MODIFIER_SHIFT); + // end::desktop-integration-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava001Snippet.java new file mode 100644 index 00000000000..55553773517 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava001Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-001[] + Button b = new Button("Click Me"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + // button was clicked, you can do anything you want herenull + } + }); + // end::events-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava002Snippet.java new file mode 100644 index 00000000000..98e764a7faa --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava002Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-002[] + Button b = new Button("Click Me"); + b.addActionListener((ev) -> { + // button was clicked, you can do anything you want here. + }); + // end::events-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava003Snippet.java new file mode 100644 index 00000000000..b83ca8d860d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava003Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-003[] + Display.getInstance().addEdtErrorHandler((e) -> { + Exception err = (Exception)e.getSource(); + // null + }); + // end::events-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava004Snippet.java new file mode 100644 index 00000000000..8aeb21b98a8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava004Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-004[] + Display.getInstance().addEdtErrorHandler((e) -> { + e.consume(); + Exception err = (Exception)e.getSource(); + // null + }); + // end::events-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava005Snippet.java new file mode 100644 index 00000000000..3896c1a2488 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava005Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-005[] + NetworkManager.getInstance().addErrorListener(new ActionListener() { + public void actionPerformed(NetworkEvent ev) { + // now we have access to the methods on NetworkEvent that provide more information about the network specific flags + } + }); + // end::events-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava006Snippet.java new file mode 100644 index 00000000000..d2b90e65139 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava006Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-006[] + NetworkManager.getInstance().addErrorListener((ev) -> { + // now we have access to the methods on NetworkEvent that provide more information about the network specific flags + }); + // end::events-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava007Snippet.java new file mode 100644 index 00000000000..9cce747aae8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava007Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-007[] + ConnectionRequest r = new ConnectionRequest() { + @Override + protected void readResponse(InputStream input) throws IOException { + // read the input stream + } + }; + // end::events-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava008Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava008Snippet.java new file mode 100644 index 00000000000..8f95b23cfc9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava008Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava008Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::events-java-008[] + ConnectionRequest r = new ConnectionRequest(); + r.addResponseListener((e) -> { + byte[] data = (byte[])e.getMetaData(); + // work with the byte data + }); + // end::events-java-008[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava009Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava009Snippet.java new file mode 100644 index 00000000000..d60e094e7aa --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava009Snippet.java @@ -0,0 +1,79 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava009Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::events-java-009[] + public class CustomToolbar extends Toolbar implements ScrollListener { + private int alpha; + + public CustomToolbar() { + } + + public void paintComponentBackground(Graphics g) { + int a = g.getAlpha(); + g.setAlpha(alpha); + super.paintComponentBackground(g); + g.setAlpha(a); + } + + public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) { + alpha = scrollY; + alpha = Math.max(alpha, 0); + alpha = Math.min(alpha, 255); + } + } + // end::events-java-009[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava011Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava011Snippet.java new file mode 100644 index 00000000000..bf1363120aa --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava011Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava011Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::events-java-011[] + private final EventDispatcher listeners = new EventDispatcher(); + + public void addActionListener(ActionListener a) { + listeners.addListener(a); + } + public void removeActionListener(ActionListener a) { + listeners.removeListener(a); + } + // end::events-java-011[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava014Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava014Snippet.java new file mode 100644 index 00000000000..99fb3ee66d9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava014Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class EventsJava014Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::events-java-014[] + public class MyComponent extends Component { + protected int getDragRegionStatus(int x, int y) { + return DRAG_REGION_LIKELY_DRAG_XY; + } + } + // end::events-java-014[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java new file mode 100644 index 00000000000..184236353cc --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java @@ -0,0 +1,79 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-001[] + // hide the title + Form hi = new Form("", new BorderLayout()); + hi.add(BorderLayout.CENTER, new Component() { + @Override + public void paint(Graphics g) { + // red color + g.setColor(0xff0000); + + // paint the screen in red + g.fillRect(getX(), getY(), getWidth(), getHeight()); + + // draw hi world in white text in the top left corner of the screen + g.setColor(0xffffff); + g.drawString("Hi World", getX(), getY()); + } + }); + hi.show(); + // end::graphics-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava004Snippet.java new file mode 100644 index 00000000000..5c1bd87b34d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava004Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-004[] + hi.setGlassPane(new Painter() { + @Override + public void paint(Graphics g, Rectangle rect) { + } + }); + // end::graphics-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava005Snippet.java new file mode 100644 index 00000000000..405a6c71f3c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava005Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-005[] + hi.setGlassPane((g, rect) -> { + }); + // end::graphics-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava006Snippet.java new file mode 100644 index 00000000000..c72af152797 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava006Snippet.java @@ -0,0 +1,79 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-006[] + Form hi = new Form("Glass Pane", new BoxLayout(BoxLayout.Y_AXIS)); + Style s = UIManager.getInstance().getComponentStyle("Label"); + s.setFgColor(0xff0000); + s.setBgTransparency(0); + Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s).toImage(); + TextField tf1 = new TextField("My Field"); + tf1.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); + tf1.getAllStyles().setMargin(5, 5, 5, 5); + hi.add(tf1); + hi.setGlassPane((g, rect) -> { + int x = tf1.getAbsoluteX() + tf1.getWidth(); + int y = tf1.getAbsoluteY(); + x -= warningImage.getWidth() / 2; + y += (tf1.getHeight() / 2 - warningImage.getHeight() / 2); + g.drawImage(warningImage, x, y); + }); + hi.show(); + // end::graphics-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava007Snippet.java new file mode 100644 index 00000000000..4507d435eb0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava007Snippet.java @@ -0,0 +1,89 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::graphics-java-007[] + public class DrawingCanvas extends Component { + GeneralPath p = new GeneralPath(); + int strokeColor = 0x0000ff; + int strokeWidth = 10; + + public void addPoint(float x, float y){ + // To be written + } + + @Override + protected void paintBackground(Graphics g) { + super.paintBackground(g); + Stroke stroke = new Stroke( + strokeWidth, + Stroke.CAP_BUTT, + Stroke.JOIN_ROUND, 1f + ); + g.setColor(strokeColor); + + // Draw the shape + g.drawShape(p, stroke); + + } + + @Override + public void pointerPressed(int x, int y) { + addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY()); + } + } + // end::graphics-java-007[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava011Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava011Snippet.java new file mode 100644 index 00000000000..7563a869425 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava011Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava011Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::graphics-java-011[] + public void paint(Graphics g){ + if ( g.isAffineSupported() ){ + // Do something that requires rotation and scaling + + } else { + // Fallback behavior here + } + } + // end::graphics-java-011[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava012Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava012Snippet.java new file mode 100644 index 00000000000..2379d967391 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava012Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava012Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::graphics-java-012[] + public class AnalogClock extends Component { + Date currentTime = new Date(); + + @Override + public void paintBackground(Graphics g) { + // Draw the clock in this method + } + } + // end::graphics-java-012[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava023Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava023Snippet.java new file mode 100644 index 00000000000..327fe72eb85 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava023Snippet.java @@ -0,0 +1,101 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava023Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-023[] + Image duke = null; + try { + // duke.png is just the default Codename One icon copied into place + duke = Image.createImage("/duke.png"); + } catch(IOException err) { + Log.e(err); + } + final Image finalDuke = duke; + + Form hi = new Form("Shape Clip"); + + // We create a 50 x 100 shape, this is arbitrary since we can scale it easily + GeneralPath path = new GeneralPath(); + path.moveTo(20,0); + path.lineTo(30, 0); + path.lineTo(30, 100); + path.lineTo(20, 100); + path.lineTo(20, 15); + path.lineTo(5, 40); + path.lineTo(5, 25); + path.lineTo(20,0); + + Stroke stroke = new Stroke(0.5f, Stroke.CAP_ROUND, Stroke.JOIN_ROUND, 4); + hi.getContentPane().getUnselectedStyle().setBgPainter((Graphics g, Rectangle rect) -> { + g.setColor(0xff); + float widthRatio = ((float)rect.getWidth()) / 50f; + float heightRatio = ((float)rect.getHeight()) / 100f; + g.scale(widthRatio, heightRatio); + g.translate((int)(((float)rect.getX()) / widthRatio), (int)(((float)rect.getY()) / heightRatio)); + g.setClip(path); + g.setAntiAliased(true); + g.drawImage(finalDuke, 0, 0, 50, 100); + g.setClip(path.getBounds()); + g.drawShape(path, stroke); + g.translate(-(int)(((float)rect.getX()) / widthRatio), -(int)(((float)rect.getY()) / heightRatio)); + g.resetAffine(); + }); + + hi.show(); + // end::graphics-java-023[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava024Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava024Snippet.java new file mode 100644 index 00000000000..23e9b8834d6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava024Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava024Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-024[] + g.drawRect(10,10, 100, 100); + // end::graphics-java-024[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava025Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava025Snippet.java new file mode 100644 index 00000000000..65ea51deb3c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava025Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava025Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-025[] + // Find out the current translation + int currX = g.getTranslateX(); + int currY = g.getTranslateY(); + + // Reset the translation to zeroes + g.translate(-currX, -currY); + + // Now we are working in absolute screen coordinates + g.drawRect(10, 10, 100, 100); + + // This rectangle should now be drawn at the exact screen + // coordinates (10,10). + + //Restore the translation + g.translate(currX, currY); + // end::graphics-java-025[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava026Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava026Snippet.java new file mode 100644 index 00000000000..c8d2350c39a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava026Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava026Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-026[] + class RectangleComponent extends Component { + public void paint(Graphics g){ + g.setColor(0x0000ff); + g.drawRect(getX()+5, getY()+5, getWidth()-10, getHeight()-10); + } + } + // end::graphics-java-026[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava027Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava027Snippet.java new file mode 100644 index 00000000000..84fd8d5a67d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava027Snippet.java @@ -0,0 +1,76 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava027Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-027[] + class RectangleComponent extends Component { + + @Override + protected Dimension calcPreferredSize() { + return new Dimension(250,250); + } + + public void paint(Graphics g) { + g.setColor(0x0000ff); + g.rotate((float) (Math.PI / 4.0)); + g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10); + g.rotate(-(float) (Math.PI / 4.0)); + } + } + // end::graphics-java-027[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java new file mode 100644 index 00000000000..963faf753bb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava032Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-032[] + Form hi = new Form("Icon Font"); + Font materialFont = FontImage.getMaterialDesignFont(); + int w = Display.getInstance().getDisplayWidth(); + FontImage fntImage = FontImage.createFixed("\uE161", materialFont, 0xff0000, w, w); + hi.add(fntImage); + hi.show(); + // end::graphics-java-032[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java new file mode 100644 index 00000000000..3c3288aa563 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava033Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-033[] + Form hi = new Form("Icon Font"); + Font materialFont = FontImage.getMaterialDesignFont(); + int size = Display.getInstance().convertToPixels(6, true); + materialFont = materialFont.derive(size, Font.STYLE_PLAIN); + Button myButton = new Button("Save"); + myButton.setIcon(FontImage.create("\uE161", myButton.getUnselectedStyle(), materialFont)); + hi.add(myButton); + hi.show(); + // end::graphics-java-033[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java new file mode 100644 index 00000000000..12f37161234 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava034Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-034[] + Form hi = new Form("Icon Font"); + Button myButton = new Button("Save"); + myButton.setIcon(FontImage.createMaterial(FontImage.MATERIAL_SAVE, myButton.getUnselectedStyle())); + hi.add(myButton); + // end::graphics-java-034[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava035Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava035Snippet.java new file mode 100644 index 00000000000..dcd234eded9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava035Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava035Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-035[] + Form hi = new Form("Icon Font"); + Button myButton = new Button("Save"); + FontImage.setMaterialIcon(myButton, FontImage.MATERIAL_SAVE); + hi.add(myButton); + // end::graphics-java-035[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava036Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava036Snippet.java new file mode 100644 index 00000000000..bbf6d1b84c4 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava036Snippet.java @@ -0,0 +1,86 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava036Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::graphics-java-036[] + Toolbar.setGlobalToolbar(true); + Form hi = new Form("Rounder", new BorderLayout()); + Label picture = new Label("", "Container"); + hi.add(BorderLayout.CENTER, picture); + hi.getUnselectedStyle().setBgColor(0xff0000); + hi.getUnselectedStyle().setBgTransparency(255); + Style s = UIManager.getInstance().getComponentStyle("TitleCommand"); + Image camera = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); + hi.getToolbar().addCommandToRightBar("", camera, (ev) -> { + try { + int width = Display.getInstance().getDisplayWidth(); + Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1)); + Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000); + Graphics gr = roundMask.getGraphics(); + gr.setColor(0xffffff); + gr.fillArc(0, 0, width, width, 0, 360); + Object mask = roundMask.createMask(); + capturedImage = capturedImage.applyMask(mask); + picture.setIcon(capturedImage); + hi.revalidate(); + } catch(IOException err) { + Log.e(err); + } + }); + // end::graphics-java-036[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava044Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava044Snippet.java new file mode 100644 index 00000000000..34d8c9cf00d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava044Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsJava044Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::graphics-java-044[] + private Map createListEntry(String name, String date, String coverURL) { + Map entry = new HashMap<>(); + entry.put("Line1", name); + entry.put("Line2", date); + entry.put("icon_URLImage", coverURL); + entry.put("icon_URLImageName", name); + return entry; + } + // end::graphics-java-044[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IndexJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IndexJava001Snippet.java new file mode 100644 index 00000000000..2f3107a9b71 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IndexJava001Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IndexJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::index-java-001[] + public class HelloWorld { // <1> + private Form current; // <2> + private Resources theme; // <3> + + // null class methods null + } + // end::index-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava001Snippet.java new file mode 100644 index 00000000000..774a5da9293 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava001Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-001[] + InputStream i = getClass().getResourceAsStream("/myFile"); + // end::io-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava002Snippet.java new file mode 100644 index 00000000000..95c309fecbe --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava002Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-002[] + InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/myFile"); + // end::io-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava003Snippet.java new file mode 100644 index 00000000000..b89f061b824 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava003Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-003[] + InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/res/myFile"); + // end::io-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava004Snippet.java new file mode 100644 index 00000000000..c38c47fd1c3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava004Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-004[] + InputStream i = Display.getInstance().getResourceAsStream(getClass(), "myFile"); + // end::io-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava007Snippet.java new file mode 100644 index 00000000000..b6e9c64d190 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava007Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-007[] + String token = Preferences.get("token", null); + // end::io-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava010Snippet.java new file mode 100644 index 00000000000..b259a77f49e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava010Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-010[] + String path = Display.getInstance().getDatabasePath("databaseName"); + // end::io-java-010[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava015Snippet.java new file mode 100644 index 00000000000..fef7fa886cb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava015Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-015[] + NetworkManager.getInstance().updateThreadCount(4); + // end::io-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava016Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava016Snippet.java new file mode 100644 index 00000000000..157cc8ded0c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava016Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava016Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-016[] + NetworkManager nm = NetworkManager.getInstance(); + if (nm.isVPNDetectionSupported()) { + boolean vpnActive = nm.isVPNActive(); + } + // end::io-java-016[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava032Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava032Snippet.java new file mode 100644 index 00000000000..1e117872482 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava032Snippet.java @@ -0,0 +1,88 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava032Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-032[] + Form hi = new Form("JSON Parsing", new BoxLayout(BoxLayout.Y_AXIS)); + JSONParser json = new JSONParser(); + try(Reader r = new InputStreamReader(Display.getInstance().getResourceAsStream(getClass(), "/anapioficeandfire.json"), "UTF-8");) { + Map data = json.parseJSON(r); + java.util.List> content = (java.util.List>)data.get("root"); // <1> + for(Map obj : content) { // <2> + String url = (String)obj.get("url"); + String name = (String)obj.get("name"); + java.util.List titles = (java.util.List)obj.get("titles"); // <3> + if(name == null || name.length() == 0) { + java.util.List aliases = (java.util.List)obj.get("aliases"); + if(aliases != null && aliases.size() > 0) { + name = aliases.get(0); + } + } + MultiButton mb = new MultiButton(name); + if(titles != null && titles.size() > 0) { + mb.setTextLine2(titles.get(0)); + } + mb.addActionListener((e) -> Display.getInstance().execute(url)); + hi.add(mb); + } + } catch(IOException err) { + Log.e(err); + } + hi.show(); + // end::io-java-032[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava037Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava037Snippet.java new file mode 100644 index 00000000000..d3b63b3ce7a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava037Snippet.java @@ -0,0 +1,100 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava037Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-037[] + Form hi = new Form("Location", new BoxLayout(BoxLayout.Y_AXIS)); + hi.add("Pinpointing Location"); + Display.getInstance().callSerially(() -> { + Location l = Display.getInstance().getLocationManager().getCurrentLocationSync(); + ConnectionRequest request = new ConnectionRequest("http://maps.googleapis.com/maps/api/geocode/json", false) { + private String country; + private String region; + private String city; + private String json; + + @Override + protected void readResponse(InputStream input) throws IOException { + Result result = Result.fromContent(input, Result.JSON); + country = result.getAsString("/results/address_components[types='country']/long_name"); + region = result.getAsString("/results/address_components[types='administrative_area_level_1']/long_name"); + city = result.getAsString("/results/address_components[types='locality']/long_name"); + json = result.toString(); + } + + @Override + protected void postResponse() { + hi.removeAll(); + hi.add(country); + hi.add(region); + hi.add(city); + hi.add(new SpanLabel(json)); + hi.revalidate(); + } + }; + request.setContentType("application/json"); + request.addRequestHeader("Accept", "application/json"); + request.addArgument("sensor", "true"); + request.addArgument("latlng", l.getLatitude() + "," + l.getLongitude()); + + NetworkManager.getInstance().addToQueue(request); + }); + hi.show(); + /* omitted */ + // end::io-java-037[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava055Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava055Snippet.java new file mode 100644 index 00000000000..049ba43c27b --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava055Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava055Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-055[] + String accountSID = "----------------"; + String authToken = "---------------"; + String fromPhone = "your Twilio phone number here"; + // end::io-java-055[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava058Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava058Snippet.java new file mode 100644 index 00000000000..93ad589930e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava058Snippet.java @@ -0,0 +1,71 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava058Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-058[] + public class GameOfThronesServiceServer { + public static String[] getBookNames() { + // your code goes herenull + return null; + } + + public static String[] getBookPovCharacters(String bookName) { + // your code goes herenull + return null; + } + } + // end::io-java-058[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava059Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava059Snippet.java new file mode 100644 index 00000000000..61a99b0b7c8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava059Snippet.java @@ -0,0 +1,73 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava059Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-059[] + public class GameOfThronesServiceServer { + public static String[] getBookNames() { + return new String[] { + "A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows", + "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring" + }; + } + + public static String[] getBookPovCharacters(String bookName) { + // your code goes herenull + return null; + } + } + // end::io-java-059[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava061Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava061Snippet.java new file mode 100644 index 00000000000..aba86d92e5f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava061Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava061Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-061[] + private static final String DESTINATION_URL = "http://localhost:8080/HelloWebServiceWizard/cn1proxy"; + // end::io-java-061[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava065Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava065Snippet.java new file mode 100644 index 00000000000..581aa6243b2 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava065Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava065Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-065[] + Map h = new HashMap<>(); + h.put("Hi","World"); + h.put("data", new byte[] {(byte)1}); + Storage.getInstance().writeObject("Test", h); + // end::io-java-065[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava072Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava072Snippet.java new file mode 100644 index 00000000000..df62af36bf6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava072Snippet.java @@ -0,0 +1,76 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava072Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::io-java-072[] + Form hi = new Form("Download Progress", new BorderLayout()); + Slider progress = new Slider(); + Button download = new Button("Download"); + download.addActionListener((e) -> { + ConnectionRequest cr = new ConnectionRequest("https://www.codenameone.com/img/blog/new_icon.png", false); + SliderBridge.bindProgress(cr, progress); + NetworkManager.getInstance().addToQueueAndWait(cr); + if(cr.getResponseCode() == 200) { + hi.add(BorderLayout.CENTER, new ScaleImageLabel(EncodedImage.create(cr.getResponseData()))); + hi.revalidate(); + } + }); + hi.add(BorderLayout.SOUTH, progress).add(BorderLayout.NORTH, download); + hi.show(); + // end::io-java-072[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava075Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava075Snippet.java new file mode 100644 index 00000000000..ad29ff737a5 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava075Snippet.java @@ -0,0 +1,89 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava075Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-075[] + public class Meeting { + private Date when; + private String subject; + private int attendance; + + public Date getWhen() { + return when; + } + + public String getSubject() { + return subject; + } + + public int getAttendance() { + return attendance; + } + + public void setWhen(Date when) { + this.when = when; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public void setAttendance(int attendance) { + this.attendance = attendance; + } + } + // end::io-java-075[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava076Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava076Snippet.java new file mode 100644 index 00000000000..733d1d59060 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava076Snippet.java @@ -0,0 +1,71 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava076Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-076[] + public class Meeting implements PropertyBusinessObject { + public final Property when = new Property<>("when"); + public final Property subject = new Property<>("subject"); + public final Property attendance = new Property<>("attendance"); + private final PropertyIndex idx = new PropertyIndex(this, "Meeting", when, subject, attendance); + + @Override + public PropertyIndex getPropertyIndex() { + return idx; + } + } + // end::io-java-076[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava088Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava088Snippet.java new file mode 100644 index 00000000000..df75aaa4f28 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava088Snippet.java @@ -0,0 +1,84 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class IoJava088Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::io-java-088[] + public class Contact implements PropertyBusinessObject { + public final IntProperty id = new IntProperty<>("id"); + public final Property name = new Property<>("name"); + public final Property email = new Property<>("email"); + public final Property phone = new Property<>("phone"); + public final Property dateOfBirth = new Property<>("dateOfBirth", Date.class); + public final Property gender = new Property<>("gender"); + public final IntProperty rank = new IntProperty<>("rank"); + public final PropertyIndex idx = new PropertyIndex(this, "Contact", id, name, email, phone, dateOfBirth, gender, rank); + + @Override + public PropertyIndex getPropertyIndex() { + return idx; + } + + public Contact() { + name.setLabel("Name"); + email.setLabel("E-Mail"); + phone.setLabel("Phone"); + dateOfBirth.setLabel("Date Of Birth"); + gender.setLabel("Gender"); + rank.setLabel("Rank"); + } + } + // end::io-java-088[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava001Snippet.java new file mode 100644 index 00000000000..82221fb6bfb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava001Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MapsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::maps-java-001[] + MapView map = new MapView(); + map.moveCamera(new LatLng(37.7749, -122.4194), 12); + form.add(BorderLayout.CENTER, map); + // end::maps-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava004Snippet.java new file mode 100644 index 00000000000..c4b4ff395af --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava004Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MapsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::maps-java-004[] + NativeMap map = new NativeMap(new LatLng(37.7749, -122.4194), 12); + map.addMarker(new MarkerOptions(new LatLng(37.7749, -122.4194)).title("San Francisco")); + form.add(BorderLayout.CENTER, map); + + if (!map.isNativeMap()) { + // Running on the simulator (or a build without a provider) -> vector fallback. + } + // end::maps-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava001Snippet.java new file mode 100644 index 00000000000..afecc4e3ed3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava001Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MavenCreatingCn1libsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::maven-creating-cn1libs-java-001[] + public class HelloWorld { + public static void helloWorld() { + System.out.println("Hello world"); + } + } + // end::maven-creating-cn1libs-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava004Snippet.java new file mode 100644 index 00000000000..80ef692e03d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava004Snippet.java @@ -0,0 +1,75 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.testing.AbstractTest; +import com.codename1.ui.CN; + +class MavenCreatingCn1libsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::maven-creating-cn1libs-java-004[] + public class BluetoothDemoTest extends AbstractTest { + @Override + public boolean runTest() throws Exception { + // Skip cleanly off-simulator: a real device has no hook registered + // and CN.canExecute will not return TRUE. + if (!Boolean.TRUE.equals(CN.canExecute("bluetooth:item2"))) { + return true; + } + // Seed the simulator — same effect as clicking "Add demo peripheral". + CN.execute("bluetooth:item2"); + // nullnow drive the public Bluetooth API as usual. + return true; + } + } + // end::maven-creating-cn1libs-java-004[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava001Snippet.java new file mode 100644 index 00000000000..a9021f2a6be --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava001Snippet.java @@ -0,0 +1,82 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MediaAndAudioJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::media-and-audio-java-001[] + final Form hi = new Form("MediaPlayer", new BorderLayout()); + hi.setToolbar(new Toolbar()); + Style s = UIManager.getInstance().getComponentStyle("Title"); + FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s); + hi.getToolbar().addCommandToRightBar("", icon, (evt) -> { + Display.getInstance().openGallery((e) -> { + if(e != null && e.getSource() != null) { + String file = (String)e.getSource(); + try { + Media video = MediaManager.createMedia(file, true); + hi.removeAll(); + hi.add(BorderLayout.CENTER, new MediaPlayer(video)); + hi.revalidate(); + } catch(IOException err) { + Log.e(err); + } + } + }, Display.GALLERY_VIDEO); + }); + hi.show(); + // end::media-and-audio-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava002Snippet.java new file mode 100644 index 00000000000..212a38d73eb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava002Snippet.java @@ -0,0 +1,81 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MediaAndAudioJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::media-and-audio-java-002[] + String bufferKey = "voice-buffer"; + AudioBuffer livePcm = MediaManager.getAudioBuffer(bufferKey, true, 8192); + livePcm.addCallback(buffer -> { + float[] frame = new float[buffer.getSize()]; + buffer.copyTo(frame); + // Process or copy this frame before the next callback arrives. + }); + + Media recorder = MediaManager.createMediaRecorder( + new MediaRecorderBuilder() + .path(bufferKey) + .samplingRate(44100) + .audioChannels(1) + .redirectToAudioBuffer(true)); + recorder.play(); + + // Later: + recorder.cleanup(); + MediaManager.releaseAudioBuffer(bufferKey); + // end::media-and-audio-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava001Snippet.java new file mode 100644 index 00000000000..30a20b0192a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava001Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-001[] + try { + Display.getInstance().sendSMS("+999999999", "My SMS Message"); + // Or: CN.sendSMS("+999999999", "My SMS Message"); + } catch(IOException err) { + Log.e(err); + Dialog.show("SMS Failed", "Unable to send the SMS", "OK", null); + } + // end::miscellaneous-features-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava003Snippet.java new file mode 100644 index 00000000000..a17722733c7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava003Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-003[] + Display.getInstance().dial("+999999999"); + // end::miscellaneous-features-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava004Snippet.java new file mode 100644 index 00000000000..4e423945ec1 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava004Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-004[] + Display display = Display.getInstance(); + if (display.isCallDetectionSupported() && display.isInCall()) { + Log.p("Call interruption is currently active (heuristic)"); + } + // end::miscellaneous-features-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava005Snippet.java new file mode 100644 index 00000000000..97918a68034 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava005Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-005[] + Message m = new Message("Body of message"); + Display.getInstance().sendMessage(new String[] {"someone@gmail.com"}, "Subject of message", m); + // end::miscellaneous-features-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava007Snippet.java new file mode 100644 index 00000000000..78df61d688c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava007Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-007[] + UIManager.getInstance().setUseLargerTextScale(true); + // end::miscellaneous-features-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava013Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava013Snippet.java new file mode 100644 index 00000000000..ec60feff46d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava013Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava013Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-013[] + String local = L10NManager.getInstance().getLanguage(); + // end::miscellaneous-features-java-013[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava014Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava014Snippet.java new file mode 100644 index 00000000000..3da5530ff0a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava014Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava014Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-014[] + UIManager.getInstance().localize( "KeyInBundle", "DefaultValue"); + // end::miscellaneous-features-java-014[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java new file mode 100644 index 00000000000..0cb79480b9a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava016Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-016[] + Form hi = new Form("L10N", new BoxLayout(BoxLayout.Y_AXIS)); + HashMap resourceBudle = new HashMap(); + resourceBudle.put("Localize", "This Label is localized"); + UIManager.getInstance().setBundle(resourceBudle); + hi.add(new Label("Localize")); + hi.show(); + // end::miscellaneous-features-java-016[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava018Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava018Snippet.java new file mode 100644 index 00000000000..dbba7bc16b4 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava018Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava018Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-018[] + Location position = LocationManager.getLocationManager().getCurrentLocationSync(); + // end::miscellaneous-features-java-018[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava019Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava019Snippet.java new file mode 100644 index 00000000000..0dd63eec2d9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava019Snippet.java @@ -0,0 +1,72 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava019Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-019[] + class MyListener implements LocationListener { + public void locationUpdated(Location location) { + // update UI etc. + } + + public void providerStateChanged(int newState) { + // handle status changes/errors appropriately + } + } + LocationManager.getLocationManager().setLocationListener(new MyListener()); + // end::miscellaneous-features-java-019[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava022Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava022Snippet.java new file mode 100644 index 00000000000..3b843aad743 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava022Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava022Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-022[] + String filePath = Capture.capturePhoto(); + // end::miscellaneous-features-java-022[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava024Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava024Snippet.java new file mode 100644 index 00000000000..fab5b049f3f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava024Snippet.java @@ -0,0 +1,91 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava024Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-024[] + Form hi = new Form("Capture", new BorderLayout()); + hi.setToolbar(new Toolbar()); + Style s = UIManager.getInstance().getComponentStyle("Title"); + FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); + + ImageViewer iv = new ImageViewer(icon); + + hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { + String filePath = Capture.capturePhoto(); + if(filePath != null) { + try { + DefaultListModel m = (DefaultListModel)iv.getImageList(); + Image img = Image.createImage(filePath); + if(m == null) { + m = new DefaultListModel<>(img); + iv.setImageList(m); + iv.setImage(img); + } else { + m.addItem(img); + } + m.setSelectedIndex(m.getSize() - 1); + } catch(IOException err) { + Log.e(err); + } + } + }); + + hi.add(BorderLayout.CENTER, iv); + hi.show(); + // end::miscellaneous-features-java-024[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava025Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava025Snippet.java new file mode 100644 index 00000000000..9057a5d3dae --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava025Snippet.java @@ -0,0 +1,112 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava025Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-025[] + Form hi = new Form("Capture", BoxLayout.y()); + hi.setToolbar(new Toolbar()); + Style s = UIManager.getInstance().getComponentStyle("Title"); + FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_MIC, s); + + FileSystemStorage fs = FileSystemStorage.getInstance(); + String recordingsDir = fs.getAppHomePath() + "recordings/"; + fs.mkdir(recordingsDir); + try { + for(String file : fs.listFiles(recordingsDir)) { + MultiButton mb = new MultiButton(file.substring(file.lastIndexOf("/") + 1)); + mb.addActionListener((e) -> { + try { + Media m = MediaManager.createMedia(recordingsDir + file, false); + m.play(); + } catch(IOException err) { + Log.e(err); + } + }); + hi.add(mb); + } + + hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { + try { + String file = Capture.captureAudio(); + if(file != null) { + SimpleDateFormat sd = new SimpleDateFormat("yyyy-MMM-dd-kk-mm"); + String fileName =sd.format(new Date()); + String filePath = recordingsDir + fileName; + Util.copy(fs.openInputStream(file), fs.openOutputStream(filePath)); + MultiButton mb = new MultiButton(fileName); + mb.addActionListener((e) -> { + try { + Media m = MediaManager.createMedia(filePath, false); + m.play(); + } catch(IOException err) { + Log.e(err); + } + }); + hi.add(mb); + hi.revalidate(); + } + } catch(IOException err) { + Log.e(err); + } + }); + } catch(IOException err) { + Log.e(err); + } + hi.show(); + // end::miscellaneous-features-java-025[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava028Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava028Snippet.java new file mode 100644 index 00000000000..2947fa776ee --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava028Snippet.java @@ -0,0 +1,91 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava028Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-028[] + Form hi = new Form("Capture", new BorderLayout()); + hi.setToolbar(new Toolbar()); + Style s = UIManager.getInstance().getComponentStyle("Title"); + FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); + + ImageViewer iv = new ImageViewer(icon); + + hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { + Display.getInstance().openGallery((e) -> { + if(e != null && e.getSource() != null) { + try { + DefaultListModel m = (DefaultListModel)iv.getImageList(); + Image img = Image.createImage((String)e.getSource()); + if(m == null) { + m = new DefaultListModel<>(img); + iv.setImageList(m); + iv.setImage(img); + } else { + m.addItem(img); + } + m.setSelectedIndex(m.getSize() - 1); + } catch(IOException err) { + Log.e(err); + } + } + }, Display.GALLERY_IMAGE); + }); + + hi.add(BorderLayout.CENTER, iv); + // end::miscellaneous-features-java-028[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava029Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava029Snippet.java new file mode 100644 index 00000000000..e31cb4837b9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava029Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava029Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-029[] + myMultiButton.addActionListener((e) -> { + if(e.getComponent() == myMultiButton) { + // this won't occur since the source component is really a button! + } + if(e.getActualComponent() == myMultiButton) { + // this will happennull + } + }); + // end::miscellaneous-features-java-029[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava030Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava030Snippet.java new file mode 100644 index 00000000000..71b0ab97da6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava030Snippet.java @@ -0,0 +1,103 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava030Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::miscellaneous-features-java-030[] + public class SpanButton extends Container { + private Button actualButton; + private TextArea text; + + public SpanButton(String txt) { + setUIID("Button"); + setLayout(new BorderLayout()); + text = new TextArea(getUIManager().localize(txt, txt)); + text.setUIID("Button"); + text.setEditable(false); + text.setFocusable(false); + actualButton = new Button(); + addComponent(BorderLayout.WEST, actualButton); + addComponent(BorderLayout.CENTER, text); + setLeadComponent(actualButton); + } + + + public void setText(String t) { + text.setText(getUIManager().localize(t, t)); + } + + public void setIcon(Image i) { + actualButton.setIcon(i); + } + + public String getText() { + return text.getText(); + } + + public Image getIcon() { + return actualButton.getIcon(); + } + + public void addActionListener(ActionListener l) { + actualButton.addActionListener(l); + } + + public void removeActionListener(ActionListener l) { + actualButton.removeActionListener(l); + } + + } + // end::miscellaneous-features-java-030[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava031Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava031Snippet.java new file mode 100644 index 00000000000..201f10c92d4 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava031Snippet.java @@ -0,0 +1,86 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava031Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; +Form f = new Form("Accordion", new BorderLayout()); +Accordion accr = new Accordion(); + +void addEntry(Accordion accr) { + TextArea t = new TextArea("New Entry"); + Button delete = new Button(); + FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE); + Label title = new Label(t.getText()); + t.addActionListener(ee -> title.setText(t.getText())); + delete.addActionListener(ee -> { + accr.removeContent(t); + accr.animateLayout(200); + }); + delete.setBlockLead(true); + delete.setUIID("Label"); + Container header = BorderLayout.center(title). + add(BorderLayout.EAST, delete); + accr.addContent(header, t); + accr.animateLayout(200); +} + void snippet() throws Exception { + // tag::miscellaneous-features-java-031[] + f.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_ADD, e -> addEntry(accr)); + addEntry(accr); + f.add(BorderLayout.CENTER, accr); + f.show(); + // end::miscellaneous-features-java-031[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava032Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava032Snippet.java new file mode 100644 index 00000000000..1f0594f8a0f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava032Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava032Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-032[] + Form hi = new Form("Pull To Refresh", BoxLayout.y()); + hi.getContentPane().addPullToRefresh(() -> { + hi.add("Pulled at " + L10NManager.getInstance().formatDateTimeShort(new Date())); + }); + hi.show(); + // end::miscellaneous-features-java-032[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava033Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava033Snippet.java new file mode 100644 index 00000000000..0c3ace42a6a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava033Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava033Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::miscellaneous-features-java-033[] + Boolean can = Display.getInstance().canExecute("imdb:///find?q=godfather"); + if(can != null && can) { + Display.getInstance().execute("imdb:///find?q=godfather"); + } else { + Display.getInstance().execute("http://www.imdb.com"); + } + // end::miscellaneous-features-java-033[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava039Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava039Snippet.java new file mode 100644 index 00000000000..956798e787a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava039Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MiscellaneousFeaturesJava039Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + + static class CursorEnabledComponent extends Component { + // tag::miscellaneous-features-java-039[] + @Override + protected void initComponent() { + super.initComponent(); + getComponentForm().setEnableCursors(true); + } + // end::miscellaneous-features-java-039[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava001Snippet.java new file mode 100644 index 00000000000..caad5496a8a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava001Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-001[] + public static final String SKU_WORLD = "com.codename1.world"; + // end::monetization-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava005Snippet.java new file mode 100644 index 00000000000..cf3c1815a8e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava005Snippet.java @@ -0,0 +1,83 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-005[] + private static final String NUM_WORLDS_KEY = "NUM_WORLDS.dat"; + public int getNumWorlds() { + synchronized (NUM_WORLDS_KEY) { + Storage s = Storage.getInstance(); + if (s.exists(NUM_WORLDS_KEY)) { + return (Integer)s.readObject(NUM_WORLDS_KEY); + } else { + return 0; + } + } + } + + public void addWorld() { + synchronized (NUM_WORLDS_KEY) { + Storage s = Storage.getInstance(); + int count = 0; + if (s.exists(NUM_WORLDS_KEY)) { + count = (Integer)s.readObject(NUM_WORLDS_KEY); + } + count++; + s.writeObject(NUM_WORLDS_KEY, new Integer(count)); + } + } + // end::monetization-java-005[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava018Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava018Snippet.java new file mode 100644 index 00000000000..02a8fac0747 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava018Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava018Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-018[] + private static final String localHost = "http://10.0.1.32"; + // end::monetization-java-018[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava026Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava026Snippet.java new file mode 100644 index 00000000000..8d13e36d1b9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava026Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava026Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-026[] + public static final boolean DISABLE_PLAY_STORE_VALIDATION=true; + // end::monetization-java-026[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava027Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava027Snippet.java new file mode 100644 index 00000000000..f0e84a62151 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava027Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava027Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-027[] + public static final String APPLE_SECRET = "your-shared-secret-here"; + // end::monetization-java-027[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava028Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava028Snippet.java new file mode 100644 index 00000000000..c0a189a2b59 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava028Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class MonetizationJava028Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::monetization-java-028[] + public static final boolean DISABLE_ITUNES_STORE_VALIDATION=true; + // end::monetization-java-028[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NearFieldCommunicationJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NearFieldCommunicationJava001Snippet.java new file mode 100644 index 00000000000..2b3248f60fd --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NearFieldCommunicationJava001Snippet.java @@ -0,0 +1,79 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.nfc.Nfc; +import com.codename1.nfc.NfcReadOptions; +import com.codename1.nfc.NdefMessage; + +class NearFieldCommunicationJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::near-field-communication-java-001[] + Nfc nfc = Nfc.getInstance(); + if (!nfc.canRead()) { + // no NFC hardware, or it is disabled in system settings + return; + } + nfc.readNdef(new NfcReadOptions() + .setNdefOnly(true) + .setAlertMessage("Hold near the poster")) + .onResult((NdefMessage msg, Throwable err) -> { + if (err != null) { + return; + } + String url = msg.getFirstRecord().getUriPayload(); + Display.getInstance().execute(url); + }); + // end::near-field-communication-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava001Snippet.java new file mode 100644 index 00000000000..8482c008c84 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava001Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.wifi.WiFi; + +class NetworkConnectivityJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::network-connectivity-java-001[] + if (WiFi.isInfoSupported()) { + String ssid = WiFi.getCurrentSSID(); // e.g. "Codename One" + String bssid = WiFi.getBSSID(); // "aa:bb:cc:11:22:33" + String gw = WiFi.getGateway(); // "192.168.1.1" + String ip = WiFi.getIp(); // "192.168.1.42" + } + // end::network-connectivity-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava004Snippet.java new file mode 100644 index 00000000000..c7ff94dd6c9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava004Snippet.java @@ -0,0 +1,78 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.bonjour.BonjourBrowser; +import com.codename1.io.bonjour.BonjourService; +import com.codename1.io.bonjour.BonjourServiceListener; + +class NetworkConnectivityJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::network-connectivity-java-004[] + BonjourBrowser browser = BonjourBrowser.browse("_http._tcp.", + new BonjourServiceListener() { + @Override + public void onServiceResolved(BonjourService s) { + Log.p("Found " + s.getName() + " at " + s.getHost() + ":" + s.getPort()); + } + @Override + public void onServiceLost(BonjourService s) { /* null */ } + @Override + public void onBrowseError(Throwable t) { Log.e(t); } + }); + + // when done: + browser.stop(); + // end::network-connectivity-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava006Snippet.java new file mode 100644 index 00000000000..12e5b64bec0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava006Snippet.java @@ -0,0 +1,74 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.NetworkManager; +import com.codename1.io.NetworkTypeListener; + +class NetworkConnectivityJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::network-connectivity-java-006[] + NetworkManager.getInstance().addNetworkTypeListener(new NetworkTypeListener() { + @Override + public void onNetworkTypeChanged(int oldType, int newType, boolean vpnActive) { + if (newType == NetworkManager.NETWORK_TYPE_NONE) { + // offline -- queue uploads for later + } + if (vpnActive) { + // refuse to roam to corporate-only resources + } + } + }); + // end::network-connectivity-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava007Snippet.java new file mode 100644 index 00000000000..bfffc0b55a2 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava007Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class NetworkConnectivityJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::network-connectivity-java-007[] + int type = NetworkManager.getInstance().getCurrentNetworkType(); + boolean vpn = NetworkManager.getInstance().isVPNActive(); + // end::network-connectivity-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava009Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava009Snippet.java new file mode 100644 index 00000000000..db1b0e528a0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava009Snippet.java @@ -0,0 +1,88 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.usb.Usb; +import com.codename1.io.usb.UsbDevice; +import com.codename1.io.usb.UsbDeviceListener; + +class NetworkConnectivityJava009Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::network-connectivity-java-009[] + if (!Usb.isSupported()) { return; } + + Usb.addDeviceListener(new UsbDeviceListener() { + @Override + public void onDeviceAttached(UsbDevice d) { + if (d.getVendorId() == 0x0403 && d.getProductId() == 0x6001) { + Usb.requestPermission(d); + } + } + @Override + public void onDeviceDetached(UsbDevice d) { } + @Override + public void onPermissionResult(UsbDevice d, boolean granted) { + if (granted) { + try (InputStream in = Usb.openInputStream(d, 0x81); + OutputStream out = Usb.openOutputStream(d, 0x02)) { + out.write("AT\r\n".getBytes()); + byte[] buf = new byte[64]; + int n = in.read(buf); + // null + } catch (IOException e) { Log.e(e); } + } + } + }); + // end::network-connectivity-java-009[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NotificationsAndBackgroundExecutionJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NotificationsAndBackgroundExecutionJava001Snippet.java new file mode 100644 index 00000000000..d60d38345eb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NotificationsAndBackgroundExecutionJava001Snippet.java @@ -0,0 +1,73 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.notifications.NotificationPermissionRequest; +import com.codename1.notifications.NotificationPermissionResult.AuthorizationLevel; + +class NotificationsAndBackgroundExecutionJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::notifications-and-background-execution-java-001[] + NotificationPermissionRequest req = new NotificationPermissionRequest() + .provisional(true) // iOS: deliver quietly without an explicit prompt + .timeSensitive(true); // iOS: allow the time-sensitive interruption level + + Display.getInstance().requestNotificationPermission(req, result -> { + if (result.isGranted()) { + // schedule notifications + } + AuthorizationLevel level = result.getAuthorizationLevel(); + }); + // end::notifications-and-background-execution-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava006Snippet.java new file mode 100644 index 00000000000..90cd228be52 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava006Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PerformanceJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::performance-java-006[] + Log.bindCrashProtection(true); + // end::performance-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava009Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava009Snippet.java new file mode 100644 index 00000000000..0cd040ae326 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava009Snippet.java @@ -0,0 +1,73 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.share.SMSShare; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PerformanceJava009Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + + static class LazySMSShare extends SMSShare { + // tag::performance-java-009[] + @Override + public Image getIcon() { + Image i = super.getIcon(); + if(i == null) { + i = Resources.getSystemResource().getImage("sms.png"); + setIcon(i); + } + return i; + } + // end::performance-java-009[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava010Snippet.java new file mode 100644 index 00000000000..a25f4fea053 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava010Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PerformanceJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::performance-java-010[] + private long lastScroll; + // end::performance-java-010[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava001Snippet.java new file mode 100644 index 00000000000..0469461e115 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava001Snippet.java @@ -0,0 +1,92 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-001[] + public class MyApplication implements PushCallback { + + // /* omitted */ + + /** + * Invoked when the push notification occurs + * + * @param value the value of the push notification + */ + public void push(String value) { + System.out.println("Received push message: "+value); + } + + /** + * Invoked when push registration is complete to pass the device ID to the application. + * + * @param deviceId OS native push ID you should not use this value and instead use Push.getPushKey() + * @see Push#getPushKey() + */ + public void registeredForPush(String deviceId) { + System.out.println("The Push ID for this device is "+Push.getPushKey()); + } + + /** + * Invoked to indicate an error occurred during registration for push notification + * @param error descriptive error string + * @param errorCode an error code + */ + public void pushRegistrationError(String error, int errorCode) { + System.out.println("An error occurred during push registration."); + } + } + // end::push-notifications-java-001[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava003Snippet.java new file mode 100644 index 00000000000..6ac843bf6ca --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava003Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-003[] + public void push(String value) { + System.out.println("Received push message: "+value); + } + // end::push-notifications-java-003[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava004Snippet.java new file mode 100644 index 00000000000..ee6911ace58 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava004Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::push-notifications-java-004[] + PushBuilder builder = new PushBuilder() + .type(1) + .body("Hello World") + .imageUrl("https://example.com/myimage.jpg"); + String payload = builder.build(); + int pushType = builder.getType(); // Will be 99 when image/category metadata is present + // end::push-notifications-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava005Snippet.java new file mode 100644 index 00000000000..288ff598d96 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava005Snippet.java @@ -0,0 +1,71 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-005[] + public void push(String message) { + PushContent content = PushContent.get(); + if (content != null) { + String title = content.getTitle(); + String body = content.getBody(); + String hiddenMeta = content.getMetaData(); + String imageUrl = content.getImageUrl(); + String replyText = content.getTextResponse(); + // Use these values (title/body/hiddenMeta/imageUrl/replyText) as needed in your application logic. + } + } + // end::push-notifications-java-005[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava007Snippet.java new file mode 100644 index 00000000000..5048c437a21 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava007Snippet.java @@ -0,0 +1,75 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-007[] + public void push(String message) { + PushContent content = PushContent.get(); + if (content != null) { + if ("invite".equals(content.getCategory())) { + if (content.getActionId() != null) { + System.out.println("The user selected the "+content.getActionId()+" action"); + if (content.getTextResponse() != null) { + System.out.println("They replied: "+content.getTextResponse()); + } + } else { + System.out.println("The user clicked on the invite notification, but didn't select an action."); + } + } + } + } + // end::push-notifications-java-007[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava008Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava008Snippet.java new file mode 100644 index 00000000000..c5b6323a633 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava008Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava008Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-008[] + private static final String PUSH_TOKEN = "********-****-****-****-*************"; + // end::push-notifications-java-008[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava009Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava009Snippet.java new file mode 100644 index 00000000000..073200e46d9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava009Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava009Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-009[] + private static final String FCM_SERVER_API_KEY = "******************-********************"; + // end::push-notifications-java-009[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava010Snippet.java new file mode 100644 index 00000000000..15322354813 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava010Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-010[] + private static final boolean ITUNES_PRODUCTION_PUSH = false; + // end::push-notifications-java-010[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava011Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava011Snippet.java new file mode 100644 index 00000000000..3be83414a4e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava011Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class PushNotificationsJava011Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::push-notifications-java-011[] + private static final String ITUNES_PRODUCTION_PUSH_CERT = "https://domain.com/linkToP12Prod.p12"; + private static final String ITUNES_PRODUCTION_PUSH_CERT_PASSWORD = "ProdPassword"; + private static final String ITUNES_DEVELOPMENT_PUSH_CERT = "https://domain.com/linkToP12Dev.p12"; + private static final String ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD = "DevPassword"; + // end::push-notifications-java-011[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava002Snippet.java new file mode 100644 index 00000000000..41fa8104bef --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava002Snippet.java @@ -0,0 +1,61 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::security-java-002[] + private static final String SECRET = "Don't let anyone see this/* omitted */"; + // end::security-java-002[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava003Snippet.java new file mode 100644 index 00000000000..daeecf118fb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava003Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; +private static final String SECRET = Util.xorDecode("RW1tI3Ema219KmpidGFhdTFhdnE1Yn9xajQ1MjM="); + void snippet() throws Exception { + // tag::security-java-003[] + // Don't let anyone see this/* omitted */ + // end::security-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava004Snippet.java new file mode 100644 index 00000000000..dd14aef4428 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava004Snippet.java @@ -0,0 +1,76 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-004[] + Form hi = new Form("Encoder", BoxLayout.y()); + TextField bla = new TextField("", "Type Text Here", 20, TextArea.ANY); + TextArea encoded = new TextArea(); + SpanLabel decoded = new SpanLabel(); + hi.addAll(bla, encoded, decoded); + bla.addDataChangedListener((a, b) -> { + String s = bla.getText(); + String e = Util.xorEncode(s); + encoded.setText(e); + decoded.setText(Util.xorDecode(e)); + hi.getContentPane().animateLayout(100); + }); + + hi.show(); + // end::security-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava010Snippet.java new file mode 100644 index 00000000000..57f134a64db --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava010Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-010[] + Display.getInstance().setProperty("blockCopyPaste", "true"); + // end::security-java-010[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava012Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava012Snippet.java new file mode 100644 index 00000000000..d19f75a4657 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava012Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava012Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-012[] + if(Display.getInstance().isJailbrokenDevice()) { + // probably jailbroken or rooted + } else { + // probably not + } + // end::security-java-012[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava013Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava013Snippet.java new file mode 100644 index 00000000000..b4002d8bdee --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava013Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.DeviceIntegrity; + +class SecurityJava013Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-013[] + if (DeviceIntegrity.isDeviceCompromised()) { + // root / jailbreak / Frida / emulator detected -- do not hard-exit, + // instead degrade: disable the transfer button, require step-up auth, etc. + for (String reason : DeviceIntegrity.getCompromiseReasons()) { + // reason is a machine-readable code: "root", "frida", "emulator", "jailbreak" + Log.p("Device compromise signal: " + reason); + } + } + // end::security-java-013[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava015Snippet.java new file mode 100644 index 00000000000..ff5427fdc7e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava015Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.DeviceIntegrity; + +class SecurityJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-015[] + // Enumerate the enabled accessibility services (component ids "pkg/.Service"). + String[] services = DeviceIntegrity.getEnabledAccessibilityServices(); + + // True if any enabled service is outside your trusted allow-list. + if (DeviceIntegrity.hasUntrustedAccessibilityService("com.google.android.marvin.talkback")) { + // Likely accessibility-abusing malware -- block the sensitive action. + } + // end::security-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava016Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava016Snippet.java new file mode 100644 index 00000000000..db2ce5c567c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava016Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SecurityJava016Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-016[] + // Entering a sensitive screen (Android FLAG_SECURE; no-op on iOS): + DeviceIntegrity.setSecureScreen(true); + // null leaving it: + DeviceIntegrity.setSecureScreen(false); + // end::security-java-016[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava020Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava020Snippet.java new file mode 100644 index 00000000000..70b9fdaed8a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava020Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.SecureRandom; + +class SecurityJava020Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-020[] + byte[] iv = SecureRandom.bytes(12); // 12-byte AES-GCM nonce + int pin = SecureRandom.intBelow(1_000_000); // bias-free 6-digit code + long id = SecureRandom.longBelow(1L << 53); + // end::security-java-020[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava024Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava024Snippet.java new file mode 100644 index 00000000000..7329230fce3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava024Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.Jwt; +import java.util.LinkedHashMap; +import java.util.Map; + +class SecurityJava024Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-024[] + Map claims = new LinkedHashMap(); + claims.put("sub", "user-123"); + claims.put("exp", System.currentTimeMillis() / 1000 + 3600); + + // Sign with a shared secret (HS256) + String token = Jwt.signHs256(claims, "secret".getBytes("UTF-8")); + + // Parse + verify + Jwt parsed = Jwt.parse(token); + if (!parsed.verifyHs256("secret".getBytes("UTF-8"))) { + throw new SecurityException("bad signature"); + } + String subject = (String) parsed.getClaim("sub"); + // end::security-java-024[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava028Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava028Snippet.java new file mode 100644 index 00000000000..51d1beef283 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava028Snippet.java @@ -0,0 +1,74 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.security.Otp; +import com.codename1.security.SecureRandom; + +class SecurityJava028Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::security-java-028[] + // 1. Generate (or fetch from your server) a 20-byte secret -- 160 bits is the + // de-facto standard for authenticator compatibility. + byte[] secret = SecureRandom.bytes(20); + + // 2. Build the otpauth URI. The authenticator app stores the secret against + // the "Acme Bank: alice@example.com" label and tags it with the issuer + // so it can show "Acme Bank" next to the rotating code. + String uri = Otp.otpauthUri("Acme Bank", "alice@example.com", secret); + + // 3. Render `uri` as a QR code and show it on screen for the user to scan. + // See "Rendering the QR code" below. + // end::security-java-028[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SvgTranscoderJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SvgTranscoderJava003Snippet.java new file mode 100644 index 00000000000..46bad2c7be9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SvgTranscoderJava003Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class SvgTranscoderJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::svg-transcoder-java-003[] + Image spin = Resources.getGlobalResources().getImage("spinner.json"); + // or by stem, like a multi-image: + Image spin2 = Resources.getGlobalResources().getImage("spinner"); + // end::svg-transcoder-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava001Snippet.java new file mode 100644 index 00000000000..8d6e7a36d34 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava001Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-001[] + Form currentForm = Display.getInstance().getCurrent(); + // end::the-components-of-codename-one-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava003Snippet.java new file mode 100644 index 00000000000..f9da9c74b9a --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava003Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-003[] + Form form = new Form(new BorderLayout()); + + Container bottomBar = new Container(BoxLayout.x()); + bottomBar.setSafeArea(true); + bottomBar.addAll(new Button("Home"), new Button("Search"), new Button("Profile")); + + form.add(BorderLayout.SOUTH, bottomBar); + form.show(); + // end::the-components-of-codename-one-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava004Snippet.java new file mode 100644 index 00000000000..13b45c8bed1 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava004Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-004[] + Form form = Display.getInstance().getCurrent(); + Rectangle safe = form.getSafeArea(); + + Graphics g = null; // e.g. inside paint() + g.setClip(safe.getX(), safe.getY(), safe.getWidth(), safe.getHeight()); + // Custom drawing code that should avoid the notch/gesture areas + // end::the-components-of-codename-one-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava005Snippet.java new file mode 100644 index 00000000000..eb23424fafb --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava005Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-005[] + Container drawer = new Container(BoxLayout.y()); + drawer.setSafeAreaRoot(true); // Ensure safe margins apply before the drawer is visible + drawer.setSafeArea(true); + // end::the-components-of-codename-one-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava006Snippet.java new file mode 100644 index 00000000000..857853324b6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava006Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-006[] + if(Dialog.show("Click Yes Or No", "Select one", "Yes", "No")) { + // user clicked yes + } else { + // user clicked no + } + // end::the-components-of-codename-one-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava007Snippet.java new file mode 100644 index 00000000000..42ba8a9bd76 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava007Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-007[] + Dialog d = new Dialog("Title"); + d.setLayout(new BorderLayout()); + d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); + d.showPacked(BorderLayout.SOUTH, true); + // end::the-components-of-codename-one-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava008Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava008Snippet.java new file mode 100644 index 00000000000..e0641446236 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava008Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava008Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-008[] + Dialog d = new Dialog("Title"); + d.setLayout(new BorderLayout()); + d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); + d.show(hi.getHeight() / 2, 0, 0, 0); + // end::the-components-of-codename-one-java-008[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava009Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava009Snippet.java new file mode 100644 index 00000000000..9a0d37991e7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava009Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava009Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-009[] + Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS)); + Button showDialog = new Button("Tint"); + showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On/* omitted */", "OK", null)); + hi.add(showDialog); + hi.show(); + // end::the-components-of-codename-one-java-009[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava010Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava010Snippet.java new file mode 100644 index 00000000000..7380662b06d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava010Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava010Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-010[] + Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS)); + hi.setTintColor(0x7700ff00); + Button showDialog = new Button("Tint"); + showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On/* omitted */", "OK", null)); + hi.add(showDialog); + hi.show(); + // end::the-components-of-codename-one-java-010[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava011Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava011Snippet.java new file mode 100644 index 00000000000..a413cae64c4 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava011Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava011Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-011[] + Form hi = new Form("Blur Dialog", new BoxLayout(BoxLayout.Y_AXIS)); + Dialog.setDefaultBlurBackgroundRadius(8); + Button showDialog = new Button("Blur"); + showDialog.addActionListener((e) -> Dialog.show("Blur", "Is On/* omitted */", "OK", null)); + hi.add(showDialog); + hi.show(); + // end::the-components-of-codename-one-java-011[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava012Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava012Snippet.java new file mode 100644 index 00000000000..b88f16bf39f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava012Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava012Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-012[] + hi.setTintColor(0); + // end::the-components-of-codename-one-java-012[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava015Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava015Snippet.java new file mode 100644 index 00000000000..697c5628a21 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava015Snippet.java @@ -0,0 +1,72 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava015Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-015[] + InteractionDialog dlg = new InteractionDialog("Hello"); + dlg.setLayout(new BorderLayout()); + dlg.add(BorderLayout.CENTER, new Label("Hello Dialog")); + Button close = new Button("Close"); + close.addActionListener((ee) -> dlg.dispose()); + dlg.addComponent(BorderLayout.SOUTH, close); + Dimension pre = dlg.getContentPane().getPreferredSize(); + int displayWidth = Display.getInstance().getDisplayWidth(); + int dialogWidth = Math.max(pre.getWidth() + pre.getWidth() / 6, displayWidth * 2 / 3); + dlg.show(0, 0, displayWidth - dialogWidth, 0); + // end::the-components-of-codename-one-java-015[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava017Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava017Snippet.java new file mode 100644 index 00000000000..8e8fb02db1f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava017Snippet.java @@ -0,0 +1,80 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava017Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-017[] + Form hi = new Form("AutoSize", BoxLayout.y()); + + Label a = new Label("Short Text"); + a.setAutoSizeMode(true); + Label b = new Label("Much Longer Text than the previous linenull"); + b.setAutoSizeMode(true); + Label c = new Label("MUCH MUCH MUCH Much Longer Text than the previous line by a pretty big marginnull"); + c.setAutoSizeMode(true); + + Label a1 = new Button("Short Text"); + a1.setAutoSizeMode(true); + Label b1 = new Button("Much Longer Text than the previous linenull"); + b1.setAutoSizeMode(true); + Label c1 = new Button("MUCH MUCH MUCH Much Longer Text than the previous line by a pretty big marginnull"); + c1.setAutoSizeMode(true); + hi.addAll(a, b, c, a1, b1, c1); + + hi.show(); + // end::the-components-of-codename-one-java-017[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava025Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava025Snippet.java new file mode 100644 index 00000000000..886f6e8c486 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava025Snippet.java @@ -0,0 +1,75 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava025Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-025[] + TextModeLayout tl = new TextModeLayout(3, 2); + Form f = new Form("Pixel Perfect", tl); + TextComponent title = new TextComponent().label("Title"); + TextComponent price = new TextComponent().label("Price"); + TextComponent location = new TextComponent().label("Location"); + TextComponent description = new TextComponent().label("Description").multiline(true); + + f.add(tl.createConstraint().horizontalSpan(2), title); + f.add(tl.createConstraint().widthPercentage(30), price); + f.add(tl.createConstraint().widthPercentage(70), location); + f.add(tl.createConstraint().horizontalSpan(2), description); + f.setEditOnShow(title.getField()); + f.show(); + // end::the-components-of-codename-one-java-025[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava026Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava026Snippet.java new file mode 100644 index 00000000000..ecba1830cfc --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava026Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava026Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-026[] + TextComponent t = new TextComponent(). + text("This appears in the text field"). + hint("This is the hint"). + label("This is the label"). + multiline(true); + // end::the-components-of-codename-one-java-026[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava028Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava028Snippet.java new file mode 100644 index 00000000000..fee3b87c38c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava028Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava028Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-028[] + TextComponent tc = new TextComponent(). + label("Input Required"). + errorMessage("Input is essential in this field"); + // end::the-components-of-codename-one-java-028[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava030Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava030Snippet.java new file mode 100644 index 00000000000..0645367e893 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava030Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava030Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-030[] + Form hi = new Form("Button"); + Button b = new Button("My Button"); + hi.add(b); + b.addActionListener((e) -> Log.p("Clicked")); + // end::the-components-of-codename-one-java-030[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java new file mode 100644 index 00000000000..d0865766db9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava031Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-031[] + Form hi = new Form("Button"); + Button b = new Button("Link Button"); + b.getAllStyles().setBorder(Border.createEmpty()); + b.getAllStyles().setTextDecoration(Style.TEXT_DECORATION_UNDERLINE); + hi.add(b); + b.addActionListener((e) -> Log.p("Clicked")); + // end::the-components-of-codename-one-java-031[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava032Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava032Snippet.java new file mode 100644 index 00000000000..37e9b941a76 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava032Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava032Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-032[] + if(UIManager.getInstance().isThemeConstant("hasRaisedButtonBool", false)) { + // that means we can use a raised button + } + // end::the-components-of-codename-one-java-032[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava033Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava033Snippet.java new file mode 100644 index 00000000000..da9404e306d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava033Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava033Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-033[] + Form f = new Form("Pixel Perfect", BoxLayout.y()); + Button b = new Button("Raised Button", "RaisedButton"); + Button r = new Button("Flat Button"); + f.add(b); + f.add(r); + f.show(); + // end::the-components-of-codename-one-java-033[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava037Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava037Snippet.java new file mode 100644 index 00000000000..fb5010cb5f0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava037Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava037Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-037[] + hi.add("Three Labels"). + add(ComponentGroup.enclose(new Label("GroupElementFirst UIID"), new Label("GroupElement UIID"), new Label("GroupElementLast UIID"))). + add("One Label"). + add(ComponentGroup.enclose(new Label("GroupElementOnly UIID"))). + add("Three Buttons"). + add(ComponentGroup.enclose(new Button("ButtonGroupFirst UIID"), new Button("ButtonGroup UIID"), new Button("ButtonGroupLast UIID"))). + add("One Button"). + add(ComponentGroup.enclose(new Button("ButtonGroupOnly UIID"))); + // end::the-components-of-codename-one-java-037[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava041Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava041Snippet.java new file mode 100644 index 00000000000..ed0f242a415 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava041Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava041Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-041[] + OnOffSwitch onOff = new OnOffSwitch(); + hi.add(onOff); + // end::the-components-of-codename-one-java-041[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava044Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava044Snippet.java new file mode 100644 index 00000000000..d7751a082dc --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava044Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava044Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-044[] + Dialog ip = new InfiniteProgress().showInifiniteBlocking(); + + // do some long operation here using invokeAndBlock or do something in a separate thread and callback later + // when you are done just call + + ip.dispose(); + // end::the-components-of-codename-one-java-044[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava050Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava050Snippet.java new file mode 100644 index 00000000000..cbf8ad82825 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava050Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava050Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::the-components-of-codename-one-java-050[] + private Map createListEntry(String name, String date) { + Map entry = new HashMap<>(); + entry.put("Line1", name); + entry.put("Line2", date); + return entry; + } + // end::the-components-of-codename-one-java-050[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava051Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava051Snippet.java new file mode 100644 index 00000000000..172ecbee254 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava051Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava051Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::the-components-of-codename-one-java-051[] + private Map createListEntry(String name, String date, Image cover) { + Map entry = new HashMap<>(); + entry.put("Line1", name); + entry.put("Line2", date); + entry.put("icon", cover); + return entry; + } + // end::the-components-of-codename-one-java-051[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava060Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava060Snippet.java new file mode 100644 index 00000000000..f73af18968d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava060Snippet.java @@ -0,0 +1,104 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava060Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; +com.codename1.ui.List list = new com.codename1.ui.List(createGenericListCellRendererModelData()); + + +private Container createGenericRendererContainer() { + Label name = new Label(); + name.setFocusable(true); + name.setName("Name"); + Label surname = new Label(); + surname.setFocusable(true); + surname.setName("Surname"); + CheckBox selected = new CheckBox(); + selected.setName("Selected"); + selected.setFocusable(true); + Container c = BorderLayout.center(name). + add(BorderLayout.SOUTH, surname). + add(BorderLayout.WEST, selected); + c.setUIID("ListRenderer"); + return c; +} + +private Object[] createGenericListCellRendererModelData() { + Map[] data = new HashMap[5]; + data[0] = new HashMap<>(); + data[0].put("Name", "Shai"); + data[0].put("Surname", "Almog"); + data[0].put("Selected", Boolean.TRUE); + data[1] = new HashMap<>(); + data[1].put("Name", "Chen"); + data[1].put("Surname", "Fishbein"); + data[1].put("Selected", Boolean.TRUE); + data[2] = new HashMap<>(); + data[2].put("Name", "Ofir"); + data[2].put("Surname", "Leitner"); + data[3] = new HashMap<>(); + data[3].put("Name", "Yaniv"); + data[3].put("Surname", "Vakarat"); + data[4] = new HashMap<>(); + data[4].put("Name", "Meirav"); + data[4].put("Surname", "Nachmanovitch"); + return data; +} + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-060[] + list.setRenderer(new GenericListCellRenderer(createGenericRendererContainer(), createGenericRendererContainer())); + // end::the-components-of-codename-one-java-060[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava067Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava067Snippet.java new file mode 100644 index 00000000000..0cc89d53ed3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava067Snippet.java @@ -0,0 +1,91 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava067Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::the-components-of-codename-one-java-067[] + private void initStarRankStyle(Style s, Image star) { + s.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH); + s.setBorder(Border.createEmpty()); + s.setBgImage(star); + s.setBgTransparency(0); + } + + private Slider createStarRankSlider() { + Slider starRank = new Slider(); + starRank.setEditable(true); + starRank.setMinValue(0); + starRank.setMaxValue(10); + Font fnt = Font.createTrueTypeFont("native:MainLight", "native:MainLight"). + derive(Display.getInstance().convertToPixels(5, true), Font.STYLE_PLAIN); + Style s = new Style(0xffff33, 0, fnt, (byte)0); + Image fullStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage(); + s.setOpacity(100); + s.setFgColor(0); + Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage(); + initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar); + initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(), emptyStar); + initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar); + initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar); + starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5, fullStar.getHeight())); + return starRank; + } + private void showStarPickingForm() { + Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS)); + hi.add(FlowLayout.encloseCenter(createStarRankSlider())); + hi.show(); + } + // end::the-components-of-codename-one-java-067[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava076Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava076Snippet.java new file mode 100644 index 00000000000..1bd8fda128e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava076Snippet.java @@ -0,0 +1,79 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava076Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-076[] + Form hi = new Form("ShareButton"); + ShareButton sb = new ShareButton(); + sb.setText("Share Screenshot"); + hi.add(sb); + + Image screenshot = Image.createImage(hi.getWidth(), hi.getHeight()); + hi.revalidate(); + hi.setVisible(true); + hi.paintComponent(screenshot.getGraphics(), true); + + String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png"; + try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile);) { + ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1); + } catch(IOException err) { + Log.e(err); + } + sb.setImageToShare(imageFile, "image/png"); + // end::the-components-of-codename-one-java-076[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava077Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava077Snippet.java new file mode 100644 index 00000000000..1127afd6763 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava077Snippet.java @@ -0,0 +1,74 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava077Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-077[] + ShareButton sb = new ShareButton(); + sb.setTextToShare("Check this out!"); + sb.setShareResultListener(result -> { + if (result.isSharedTo()) { + Log.p("Shared to " + result.getPackageName()); + } else if (result.isDismissed()) { + Log.p("User dismissed the share sheet"); + } else if (result.isFailed()) { + Log.p("Share failed: " + result.getError()); + } + }); + form.add(sb); + // end::the-components-of-codename-one-java-077[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava080Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava080Snippet.java new file mode 100644 index 00000000000..5a16dd44602 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava080Snippet.java @@ -0,0 +1,73 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava080Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-080[] + Form hi = new Form("Tabs", new BorderLayout()); + + Tabs t = new Tabs(); + Style s = UIManager.getInstance().getComponentStyle("Tab"); + FontImage icon1 = FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, s); + + Container container1 = BoxLayout.encloseY(new Label("Label1"), new Label("Label2")); + t.addTab("Tab1", icon1, container1); + t.addTab("Tab2", new SpanLabel("Some text directly in the tab")); + + hi.add(BorderLayout.CENTER, t); + // end::the-components-of-codename-one-java-080[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava081Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava081Snippet.java new file mode 100644 index 00000000000..dd978331c85 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava081Snippet.java @@ -0,0 +1,101 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava081Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-081[] + Form hi = new Form("Swipe Tabs", new LayeredLayout()); + Tabs t = new Tabs(); + t.hideTabs(); + + Style s = UIManager.getInstance().getComponentStyle("Button"); + FontImage radioEmptyImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, s); + FontImage radioFullImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, s); + ((DefaultLookAndFeel)UIManager.getInstance().getLookAndFeel()).setRadioButtonImages(radioFullImage, radioEmptyImage, radioFullImage, radioEmptyImage); + + Container container1 = BoxLayout.encloseY(new Label("Swipe the tab to see more"), + new Label("You can put anything here")); + t.addTab("Tab1", container1); + t.addTab("Tab2", new SpanLabel("Some text directly in the tab")); + + RadioButton firstTab = new RadioButton(""); + RadioButton secondTab = new RadioButton(""); + firstTab.setUIID("Container"); + secondTab.setUIID("Container"); + new ButtonGroup(firstTab, secondTab); + firstTab.setSelected(true); + Container tabsFlow = FlowLayout.encloseCenter(firstTab, secondTab); + + hi.add(t); + hi.add(BorderLayout.south(tabsFlow)); + + t.addSelectionListener((i1, i2) -> { + switch(i2) { + case 0: + if(!firstTab.isSelected()) { + firstTab.setSelected(true); + } + break; + case 1: + if(!secondTab.isSelected()) { + secondTab.setSelected(true); + } + break; + } + }); + // end::the-components-of-codename-one-java-081[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava082Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava082Snippet.java new file mode 100644 index 00000000000..e955d67087e --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava082Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava082Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-082[] + Tabs tabs = new Tabs(); + tabs.setAnimatedIndicator(true); + // end::the-components-of-codename-one-java-082[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava083Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava083Snippet.java new file mode 100644 index 00000000000..bb71fc99eab --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava083Snippet.java @@ -0,0 +1,82 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava083Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-083[] + final Form hi = new Form("MediaPlayer", new BorderLayout()); + hi.setToolbar(new Toolbar()); + Style s = UIManager.getInstance().getComponentStyle("Title"); + FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s); + hi.getToolbar().addCommandToRightBar("", icon, (evt) -> { + Display.getInstance().openGallery((e) -> { + if(e != null && e.getSource() != null) { + String file = (String)e.getSource(); + try { + Media video = MediaManager.createMedia(file, true); + hi.removeAll(); + hi.add(BorderLayout.CENTER, new MediaPlayer(video)); + hi.revalidate(); + } catch(IOException err) { + Log.e(err); + } + } + }, Display.GALLERY_VIDEO); + }); + hi.show(); + // end::the-components-of-codename-one-java-083[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava085Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava085Snippet.java new file mode 100644 index 00000000000..1c5448bd4b0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava085Snippet.java @@ -0,0 +1,72 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava085Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-085[] + Form hi = new Form("ImageViewer", new BorderLayout()); + + Image red = Image.createImage(100, 100, 0xffff0000); + Image green = Image.createImage(100, 100, 0xff00ff00); + Image blue = Image.createImage(100, 100, 0xff0000ff); + Image gray = Image.createImage(100, 100, 0xffcccccc); + + ImageViewer iv = new ImageViewer(red); + iv.setImageList(new DefaultListModel<>(red, green, blue, gray)); + hi.add(BorderLayout.CENTER, iv); + // end::the-components-of-codename-one-java-085[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava090Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava090Snippet.java new file mode 100644 index 00000000000..513c94e3bc7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava090Snippet.java @@ -0,0 +1,111 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava090Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-090[] + Toolbar.setGlobalToolbar(true); + Style s = UIManager.getInstance().getComponentStyle("Title"); + + Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); + TextField searchField = new TextField("", "Toolbar Search"); // <1> + searchField.getHintLabel().setUIID("Title"); + searchField.setUIID("Title"); + searchField.getAllStyles().setAlignment(Component.LEFT); + hi.getToolbar().setTitleComponent(searchField); + FontImage searchIcon = FontImage.createMaterial(FontImage.MATERIAL_SEARCH, s); + searchField.addDataChangeListener((i1, i2) -> { // <2> + String t = searchField.getText(); + if(t.length() < 1) { + for(Component cmp : hi.getContentPane()) { + cmp.setHidden(false); + cmp.setVisible(true); + } + } else { + t = t.toLowerCase(); + for(Component cmp : hi.getContentPane()) { + String val = null; + if(cmp instanceof Label) { + val = ((Label)cmp).getText(); + } else { + if(cmp instanceof TextArea) { + val = ((TextArea)cmp).getText(); + } else { + val = (String)cmp.getPropertyValue("text"); + } + } + boolean show = val != null && val.toLowerCase().indexOf(t) > -1; + cmp.setHidden(!show); + cmp.setVisible(show); // <3> + } + } + hi.getContentPane().animateLayout(250); + }); + hi.getToolbar().addCommandToRightBar("", searchIcon, (e) -> { // <4> + searchField.startEditingAsync(); + }); + + hi.add("A Game of Thrones"). + add("A Clash Of Kings"). + add("A Storm Of Swords"). + add("A Feast For Crows"). + add("A Dance With Dragons"). + add("The Winds of Winter"). + add("A Dream of Spring"); + hi.show(); + // end::the-components-of-codename-one-java-090[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava093Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava093Snippet.java new file mode 100644 index 00000000000..3f0d2540181 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava093Snippet.java @@ -0,0 +1,85 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava093Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-093[] + Toolbar.setGlobalToolbar(true); + + Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); + EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(hi.getWidth(), hi.getWidth() / 5, 0xffff0000), true); + URLImage background = URLImage.createToStorage(placeholder, "400px-AGameOfThrones.jpg", + "http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/400px-AGameOfThrones.jpg"); + background.fetch(); + Style stitle = hi.getToolbar().getTitleComponent().getUnselectedStyle(); + stitle.setBgImage(background); + stitle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); + stitle.setPaddingUnit(Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS); + stitle.setPaddingTop(15); + SpanButton credit = new SpanButton("This excerpt is from A Wiki Of Ice And Fire. Please check it out by clicking here!"); + credit.addActionListener((e) -> Display.getInstance().execute("http://awoiaf.westeros.org/index.php/A_Game_of_Thrones")); + hi.add(new SpanLabel("A Game of Thrones is the first of seven planned novels in A Song of Ice and Fire, an epic fantasy series by American author George R. R. Martin. It was first published on 6 August 1996. The novel was nominated for the 1998 Nebula Award and the 1997 World Fantasy Award,[1] and won the 1997 Locus Award.[2] The novella Blood of the Dragon, comprising the Daenerys Targaryen chapters from the novel, won the 1997 Hugo Award for Best Novella. ")). + add(new Label("Plot introduction", "Heading")). + add(new SpanLabel("A Game of Thrones is set in the Seven Kingdoms of Westeros, a land reminiscent of Medieval Europe. In Westeros the seasons last for years, sometimes decades, at a time.\n\n" + + "Fifteen years prior to the novel, the Seven Kingdoms were torn apart by a civil war, known alternately as \"Robert's Rebellion\" and the \"War of the Usurper.\" Prince Rhaegar Targaryen kidnapped Lyanna Stark, arousing the ire of her family and of her betrothed, Lord Robert Baratheon (the war's titular rebel). The Mad King, Aerys II Targaryen, had Lyanna's father and eldest brother executed when they demanded her safe return. Her second brother, Eddard, joined his boyhood friend Robert Baratheon and Jon Arryn, with whom they had been fostered as children, in declaring war against the ruling Targaryen dynasty, securing the allegiances of House Tully and House Arryn through a network of dynastic marriages (Lord Eddard to Catelyn Tully and Lord Arryn to Lysa Tully). The powerful House Tyrell continued to support the King, but House Lannister and House Martell both stalled due to insults against their houses by the Targaryens. The civil war climaxed with the Battle of the Trident, when Prince Rhaegar was killed in battle by Robert Baratheon. The Lannisters finally agreed to support King Aerys, but then brutallynull ")). + add(credit); + + ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200); + hi.getAnimationManager().onTitleScrollAnimation(title); + hi.show(); + // end::the-components-of-codename-one-java-093[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava094Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava094Snippet.java new file mode 100644 index 00000000000..cf23b6feb96 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava094Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava094Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-094[] + ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200); + hi.getAnimationManager().onTitleScrollAnimation(title); + // end::the-components-of-codename-one-java-094[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava095Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava095Snippet.java new file mode 100644 index 00000000000..c756e1d0376 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava095Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava095Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-095[] + Form hi = new Form("Browser", new BorderLayout()); + BrowserComponent browser = new BrowserComponent(); + browser.setURL("https://www.codenameone.com/"); + hi.add(BorderLayout.CENTER, browser); + // end::the-components-of-codename-one-java-095[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava096Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava096Snippet.java new file mode 100644 index 00000000000..b8b5b7fd955 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava096Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava096Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-096[] + BrowserComponent wb = new BrowserComponent(); + wb.setURL("jar:///Page.html"); + // end::the-components-of-codename-one-java-096[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava097Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava097Snippet.java new file mode 100644 index 00000000000..f1ec49eccf6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava097Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava097Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-097[] + try { + browserComponent.setURLHierarchy("/htmlFile.html"); + } catch(IOException err) { + /* omitted */ + } + // end::the-components-of-codename-one-java-097[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava098Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava098Snippet.java new file mode 100644 index 00000000000..a35219faa96 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava098Snippet.java @@ -0,0 +1,85 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava098Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-098[] + Form hi = new Form("BrowserComponent", new BorderLayout()); + BrowserComponent bc = new BrowserComponent(); + bc.setPage( "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

Demo

\n" + + " \n" + + "", null); + hi.add(BorderLayout.CENTER, bc); + bc.setBrowserNavigationCallback((url) -> { + if(url.startsWith("http://click")) { + Display.getInstance().callSerially(() -> bc.execute("fnc('

You clicked!

')")); + return false; + } + return true; + }); + // end::the-components-of-codename-one-java-098[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava111Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava111Snippet.java new file mode 100644 index 00000000000..f3490b0acdd --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava111Snippet.java @@ -0,0 +1,83 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava111Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-111[] + Form hi = new Form("BrowserComponent", new BorderLayout()); + BrowserComponent bc = new BrowserComponent(); + bc.setPage( "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

Demo

\n" + + " \n" + + "", null); + TextField tf = new TextField(); + hi.add(BorderLayout.CENTER, bc). + add(BorderLayout.SOUTH, tf); + bc.addWebEventListener("onLoad", (e) -> bc.execute("fnc('

Hello World

')")); + tf.addActionListener((e) -> bc.execute("fnc('

" + tf.getText() +"

')")); + hi.show(); + // end::the-components-of-codename-one-java-111[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava115Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava115Snippet.java new file mode 100644 index 00000000000..2277fce4ff3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava115Snippet.java @@ -0,0 +1,76 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava115Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-115[] + Form hi = new Form("Compose", new BorderLayout()); + RichTextArea editor = new RichTextArea(); + editor.setPlaceholder("Write somethingnull"); + editor.setHtml("

Trip itinerary

Meet at the main lobby.

"); + + Toolbar tb = hi.getToolbar(); + tb.addCommandToRightBar("B", null, e -> editor.bold()); + tb.addCommandToRightBar("I", null, e -> editor.italic()); + tb.addCommandToRightBar("List", null, e -> editor.insertUnorderedList()); + tb.addCommandToRightBar("Save", null, e -> + editor.getHtml(html -> Log.p("User wrote: " + html))); + + hi.add(BorderLayout.CENTER, editor); + hi.show(); + // end::the-components-of-codename-one-java-115[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava117Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava117Snippet.java new file mode 100644 index 00000000000..fd243730042 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava117Snippet.java @@ -0,0 +1,70 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava117Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-117[] + Form hi = new Form("Editor", new BorderLayout()); + CodeEditor editor = new CodeEditor(); + editor.setLanguage("java"); + editor.setTheme("light"); // or "dark" + editor.setShowLineNumbers(true); + editor.setText("public class Main {\n\n}"); + hi.add(BorderLayout.CENTER, editor); + hi.show(); + // end::the-components-of-codename-one-java-117[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava119Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava119Snippet.java new file mode 100644 index 00000000000..3f602570443 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava119Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava119Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-119[] + Form hi = new Form("Auto Complete", new BoxLayout(BoxLayout.Y_AXIS)); + AutoCompleteTextField ac = new AutoCompleteTextField("Short", "Shock", "Sholder", "Shrek"); + ac.setMinimumElementsShownInPopup(5); + hi.add(ac); + // end::the-components-of-codename-one-java-119[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava121Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava121Snippet.java new file mode 100644 index 00000000000..d7e88dccf7c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava121Snippet.java @@ -0,0 +1,115 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava121Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-121[] + final String[] characters = { "Tyrion Lannister", "Jaime Lannister", "Cersei Lannister", "Daenerys Targaryen", + "Jon Snow", "Petyr Baelish", "Jorah Mormont", "Sansa Stark", "Arya Stark", "Theon Greyjoy" + // snipped the rest for clarity + }; + + Form current = new Form("AutoComplete", BoxLayout.y()); + + AutoCompleteTextField ac = new AutoCompleteTextField(characters); + + final int size = Display.getInstance().convertToPixels(7); + final EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(size, size, 0xffcccccc), true); + + final String[] actors = { "Peter Dinklage", "Nikolaj Coster-Waldau", "Lena Headey"}; // <1> + final Image[] pictures = { + URLImage.createToStorage(placeholder, "tyrion","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/tyrion-lannister-512x512.jpg"), + URLImage.createToStorage(placeholder, "jaime","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/jamie-lannister-512x512.jpg"), + URLImage.createToStorage(placeholder, "cersei","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/cersei-lannister-512x512.jpg") + }; + + ac.setCompletionRenderer(new ListCellRenderer() { + private final Label focus = new Label(); // <2> + private final Label line1 = new Label(characters[0]); + private final Label line2 = new Label(actors[0]); + private final Label icon = new Label(pictures[0]); + private final Container selection = BorderLayout.center( + BoxLayout.encloseY(line1, line2)).add(BorderLayout.EAST, icon); + + @Override + public Component getListCellRendererComponent(com.codename1.ui.List list, Object value, int index, boolean isSelected) { + for(int iter = 0 ; iter < characters.length ; iter++) { + if(characters[iter].equals(value)) { + line1.setText(characters[iter]); + if(actors.length > iter) { + line2.setText(actors[iter]); + icon.setIcon(pictures[iter]); + } else { + line2.setText(""); // <3> + icon.setIcon(placeholder); + } + break; + } + } + return selection; + } + + @Override + public Component getListFocusComponent(com.codename1.ui.List list) { + return focus; + } + }); + current.add(ac); + + current.show(); + // end::the-components-of-codename-one-java-121[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava122Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava122Snippet.java new file mode 100644 index 00000000000..7714b7775a7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava122Snippet.java @@ -0,0 +1,89 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava122Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-122[] + Form hi = new Form("Picker", new BoxLayout(BoxLayout.Y_AXIS)); + Picker datePicker = new Picker(); + datePicker.setType(Display.PICKER_TYPE_DATE); + Picker dateTimePicker = new Picker(); + dateTimePicker.setType(Display.PICKER_TYPE_DATE_AND_TIME); + Picker timePicker = new Picker(); + timePicker.setType(Display.PICKER_TYPE_TIME); + Picker stringPicker = new Picker(); + stringPicker.setType(Display.PICKER_TYPE_STRINGS); + Picker durationPicker = new Picker(); + durationPicker.setType(Display.PICKER_TYPE_DURATION); + Picker minuteDurationPicker = new Picker(); + minuteDurationPicker.setType(Display.PICKER_TYPE_DURATION_MINUTES); + Picker hourDurationPicker = new Picker(); + hourDurationPicker.setType(Display.PICKER_TYPE_DURATION_HOURS); + + datePicker.setDate(new Date()); + dateTimePicker.setDate(new Date()); + timePicker.setTime(10 * 60); // 10:00AM = Minutes since midnight + stringPicker.setStrings("A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows", + "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring"); + stringPicker.setSelectedString("A Game of Thrones"); + + hi.add(datePicker).add(dateTimePicker).add(timePicker) + .add(stringPicker).add(durationPicker) + .add(minuteDurationPicker).add(hourDurationPicker); + hi.show(); + // end::the-components-of-codename-one-java-122[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava134Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava134Snippet.java new file mode 100644 index 00000000000..38ffb5dd8cd --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava134Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava134Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-134[] + Form hi = new Form("Undo", BoxLayout.y()); + Button add = new Button("Add"); + + add.addActionListener(e -> { + Label l = new Label("Added this"); + hi.add(l); + hi.revalidate(); + ToastBar.showMessage("Added, click here to undonull", FontImage.MATERIAL_UNDO, + ee -> { + l.remove(); + hi.revalidate(); + }); + }); + hi.add(add); + hi.show(); + // end::the-components-of-codename-one-java-134[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava135Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava135Snippet.java new file mode 100644 index 00000000000..eb169c21670 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava135Snippet.java @@ -0,0 +1,75 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava135Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-135[] + Form hi = new Form("Signature Component"); + hi.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + hi.add("Enter Your Name:"); + hi.add(new TextField()); + hi.add("Signature:"); + SignatureComponent sig = new SignatureComponent(); + sig.addActionListener((evt)-> { + System.out.println("The signature was changed"); + Image img = sig.getSignatureImage(); + // Now we can do whatever we want with the image of this signature. + }); + hi.addComponent(sig); + hi.show(); + // end::the-components-of-codename-one-java-135[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava136Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava136Snippet.java new file mode 100644 index 00000000000..79dffe95627 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava136Snippet.java @@ -0,0 +1,77 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava136Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-136[] + Form f = new Form("Accordion", new BoxLayout(BoxLayout.Y_AXIS)); + f.setScrollableY(true); + Accordion accr = new Accordion(); + accr.addContent("Item1", new SpanLabel("The quick brown fox jumps over the lazy dog\n" + + "The quick brown fox jumps over the lazy dog")); + accr.addContent("Item2", new SpanLabel("The quick brown fox jumps over the lazy dog\n" + + "The quick brown fox jumps over the lazy dog\n " + + "The quick brown fox jumps over the lazy dog\n " + + "The quick brown fox jumps over the lazy dog\n " + + "")); + + accr.addContent("Item3", BoxLayout.encloseY(new Label("Label"), new TextField(), new Button("Button"), new CheckBox("CheckBox"))); + + f.add(accr); + f.show(); + // end::the-components-of-codename-one-java-136[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava137Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava137Snippet.java new file mode 100644 index 00000000000..a326f83524d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava137Snippet.java @@ -0,0 +1,69 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava137Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-137[] + Form hi = new Form("Floating Hint", BoxLayout.y()); + TextField first = new TextField("", "First Field"); + TextField second = new TextField("", "Second Field"); + hi.add(new FloatingHint(first)). + add(new FloatingHint(second)). + add(new Button("Go")); + hi.show(); + // end::the-components-of-codename-one-java-137[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava138Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava138Snippet.java new file mode 100644 index 00000000000..514d6cf480f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava138Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava138Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-138[] + FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD); + fab.addActionListener(e -> ToastBar.showErrorMessage("Not implemented yetnull")); + fab.bindFabToContainer(form.getContentPane()); + // end::the-components-of-codename-one-java-138[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava139Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava139Snippet.java new file mode 100644 index 00000000000..df62614888c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava139Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava139Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-139[] + FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD); + fab.createSubFAB(FontImage.MATERIAL_PEOPLE, ""); + fab.createSubFAB(FontImage.MATERIAL_IMPORT_CONTACTS, ""); + fab.bindFabToContainer(form.getContentPane()); + // end::the-components-of-codename-one-java-139[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java new file mode 100644 index 00000000000..832b6d04560 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java @@ -0,0 +1,78 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava140Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::the-components-of-codename-one-java-140[] + Form hi = new Form("Badge"); + + Button chat = new Button("Chat"); + FontImage.setMaterialIcon(chat, FontImage.MATERIAL_CHAT, 7); + + FloatingActionButton badge = FloatingActionButton.createBadge("33"); + hi.add(badge.bindFabToContainer(chat, Component.RIGHT, Component.TOP)); + + TextField changeBadgeValue = new TextField("33"); + changeBadgeValue.addDataChangedListener((i, ii) -> { + badge.setText(changeBadgeValue.getText()); + badge.getParent().revalidate(); + }); + hi.add(changeBadgeValue); + + hi.show(); + // end::the-components-of-codename-one-java-140[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava142Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava142Snippet.java new file mode 100644 index 00000000000..efd796e3b7d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava142Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheComponentsOfCodenameOneJava142Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::the-components-of-codename-one-java-142[] + private Container encloseInMaximizableGrid(Component cmp1, Component cmp2) { + return new SplitPane(SplitPane.VERTICAL_SPLIT, cmp1, cmp2, "25%", "50%", "75%"); + } + // end::the-components-of-codename-one-java-142[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheEdtEventDispatchThreadJava012Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheEdtEventDispatchThreadJava012Snippet.java new file mode 100644 index 00000000000..32c87a49621 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheEdtEventDispatchThreadJava012Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TheEdtEventDispatchThreadJava012Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::the-edt-event-dispatch-thread-java-012[] + public void actionPerformed(ActionEvent ev) { + // will return true if the user clicks "OK" + if(!Dialog.show("Question", "How Are You", "OK", "Not OK")) { + // ask what went wrongnull + } + } + // end::the-edt-event-dispatch-thread-java-012[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava002Snippet.java new file mode 100644 index 00000000000..ce192c35c65 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava002Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class ThemeBasicsJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::theme-basics-java-002[] + try { + theme = Resources.openLayered("/theme"); + UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); + } catch(IOException e){ + e.printStackTrace(); + } + // end::theme-basics-java-002[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava005Snippet.java new file mode 100644 index 00000000000..c9e56d0cd19 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava005Snippet.java @@ -0,0 +1,152 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class ThemeBasicsJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::theme-basics-java-005[] + private Label createForFont(Font fnt, String s) { + Label l = new Label(s); + l.getUnselectedStyle().setFont(fnt); + return l; + } + + public void showForm() { + GridLayout gr = new GridLayout(5); + gr.setAutoFit(true); + Form hi = new Form("Fonts", gr); + + int fontSize = Display.getInstance().convertToPixels(3); + + // requires Handlee-Regular.ttf in the src folder root! + Font ttfFont = Font.createTrueTypeFont("Handlee", "Handlee-Regular.ttf"). + derive(fontSize, Font.STYLE_PLAIN); + + Font smallPlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL); + Font mediumPlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); + Font largePlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE); + Font smallBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL); + Font mediumBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM); + Font largeBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE); + Font smallItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_SMALL); + Font mediumItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); + Font largeItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_LARGE); + + Font smallPlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL); + Font mediumPlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); + Font largePlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_LARGE); + Font smallBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL); + Font mediumBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM); + Font largeBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE); + Font smallItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_SMALL); + Font mediumItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); + Font largeItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_LARGE); + + Font smallPlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL); + Font mediumPlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); + Font largePlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_LARGE); + Font smallBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL); + Font mediumBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM); + Font largeBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE); + Font smallItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_SMALL); + Font mediumItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); + Font largeItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_LARGE); + + String[] nativeFontTypes = { + "native:MainThin", "native:MainLight", + "native:MainRegular", "native:MainBold", + "native:MainBlack", "native:ItalicThin", + "native:ItalicLight", "native:ItalicRegular", + "native:ItalicBold", "native:ItalicBlack"}; + + for(String s : nativeFontTypes) { + Font tt = Font.createTrueTypeFont(s, s).derive(fontSize, Font.STYLE_PLAIN); + hi.add(createForFont(tt, s)); + } + + hi.add(createForFont(ttfFont, "Handlee TTF Font")). + add(createForFont(smallPlainSystemFont, "smallPlainSystemFont")). + add(createForFont(mediumPlainSystemFont, "mediumPlainSystemFont")). + add(createForFont(largePlainSystemFont, "largePlainSystemFont")). + add(createForFont(smallBoldSystemFont, "smallBoldSystemFont")). + add(createForFont(mediumBoldSystemFont, "mediumBoldSystemFont")). + add(createForFont(largeBoldSystemFont, "largeBoldSystemFont")). + add(createForFont(smallPlainSystemFont, "smallItalicSystemFont")). + add(createForFont(mediumItalicSystemFont, "mediumItalicSystemFont")). + add(createForFont(largeItalicSystemFont, "largeItalicSystemFont")). + + add(createForFont(smallPlainMonospaceFont, "smallPlainMonospaceFont")). + add(createForFont(mediumPlainMonospaceFont, "mediumPlainMonospaceFont")). + add(createForFont(largePlainMonospaceFont, "largePlainMonospaceFont")). + add(createForFont(smallBoldMonospaceFont, "smallBoldMonospaceFont")). + add(createForFont(mediumBoldMonospaceFont, "mediumBoldMonospaceFont")). + add(createForFont(largeBoldMonospaceFont, "largeBoldMonospaceFont")). + add(createForFont(smallItalicMonospaceFont, "smallItalicMonospaceFont")). + add(createForFont(mediumItalicMonospaceFont, "mediumItalicMonospaceFont")). + add(createForFont(largeItalicMonospaceFont, "largeItalicMonospaceFont")). + + add(createForFont(smallPlainProportionalFont, "smallPlainProportionalFont")). + add(createForFont(mediumPlainProportionalFont, "mediumPlainProportionalFont")). + add(createForFont(largePlainProportionalFont, "largePlainProportionalFont")). + add(createForFont(smallBoldProportionalFont, "smallBoldProportionalFont")). + add(createForFont(mediumBoldProportionalFont, "mediumBoldProportionalFont")). + add(createForFont(largeBoldProportionalFont, "largeBoldProportionalFont")). + add(createForFont(smallItalicProportionalFont, "smallItalicProportionalFont")). + add(createForFont(mediumItalicProportionalFont, "mediumItalicProportionalFont")). + add(createForFont(largeItalicProportionalFont, "largeItalicProportionalFont")); + + hi.show(); + } + // end::theme-basics-java-005[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThreeDGraphicsJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThreeDGraphicsJava003Snippet.java new file mode 100644 index 00000000000..2682817ff59 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThreeDGraphicsJava003Snippet.java @@ -0,0 +1,67 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class ThreeDGraphicsJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::3d-graphics-java-003[] + VertexBuffer vb = device.createVertexBuffer(VertexFormat.POSITION_NORMAL_TEXCOORD, 4); + vb.setData(new float[] { /* px,py,pz, nx,ny,nz, u,v per vertex null */ }); + IndexBuffer ib = device.createIndexBuffer(6); + ib.setData(new int[] { 0, 1, 2, 0, 2, 3 }); + Mesh mesh = new Mesh(vb, ib, PrimitiveType.TRIANGLES); + // end::3d-graphics-java-003[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TvplatformsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TvplatformsJava001Snippet.java new file mode 100644 index 00000000000..82645b83458 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TvplatformsJava001Snippet.java @@ -0,0 +1,71 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class TvplatformsJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::tvplatforms-java-001[] + Form f = new Form(BoxLayout.y()); + if (CN.isTV()) { + // 10-foot UI: larger fonts, generous spacing, focus-driven navigation + f.add(new Label("Hello TV")); + } else { + // Full phone/tablet layout + f.add(new Label("Hello")); + } + f.show(); + // end::tvplatforms-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoCaptureConstraintsJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoCaptureConstraintsJava004Snippet.java new file mode 100644 index 00000000000..0c0a27c99a3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoCaptureConstraintsJava004Snippet.java @@ -0,0 +1,65 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class VideoCaptureConstraintsJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::video-capture-constraints-java-004[] + VideoCaptureConstraints cnst = new VideoCaptureConstraints() + .preferredWidth(320) + .preferredHeight(240); + // end::video-capture-constraints-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java new file mode 100644 index 00000000000..51b1faeb47d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java @@ -0,0 +1,103 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.io.FileSystemStorage; +import com.codename1.media.AudioBuffer; +import com.codename1.media.VideoCodec; +import com.codename1.media.VideoFrame; +import com.codename1.media.VideoIO; +import com.codename1.media.VideoReader; +import com.codename1.media.VideoWriter; +import com.codename1.media.VideoWriterBuilder; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import java.util.ArrayList; +import java.util.List; + +class VideoIoJava001Snippet { + String videoPath = FileSystemStorage.getInstance().getAppHomePath() + "/source.mp4"; + + void checkSupport() throws Exception { + // tag::video-io-java-001[] + if (!VideoIO.isSupported()) { + // Video encoding / decoding is not available on this platform + // (for example TV, Watch or Car), so fall back gracefully. + return; + } + VideoIO io = VideoIO.getVideoIO(); + // end::video-io-java-001[] + } + + void encodeVideo() throws Exception { + // tag::video-io-java-002[] + String out = FileSystemStorage.getInstance().getAppHomePath() + "/generated.mp4"; + int w = 640, h = 480; + float fps = 30; + + VideoWriter writer = new VideoWriterBuilder() + .path(out) + .width(w).height(h).frameRate(fps) + .videoCodec(VideoIO.CODEC_H264).videoBitRate(4_000_000) + .build(); + + // Each frame is just an Image you fully control: draw whatever you like. + for (int i = 0; i < 90; i++) { // 3 seconds at 30fps + Image frame = Image.createImage(w, h, 0xff000000); + Graphics g = frame.getGraphics(); + g.setColor(0xffffff); + g.fillRect((i * 8) % w, h / 2 - 20, 60, 40); + writer.writeFrame(frame, Math.round(i * 1000f / fps)); + } + writer.close(); + // end::video-io-java-002[] + } + + void decodeFrames() throws Exception { + // tag::video-io-java-003[] + VideoReader reader = VideoIO.getVideoIO().openReader(videoPath); + System.out.println(reader.getWidth() + "x" + reader.getHeight() + + " " + reader.getFrameRate() + "fps, " + + reader.getDurationMillis() + "ms"); + + List thumbnails = new ArrayList<>(); + + // Frame accurate single frame (unlike Media.setTime which snaps to key frames): + VideoFrame oneSecond = reader.frameAt(1000); + if (oneSecond != null) { + thumbnails.add(oneSecond.toImage()); + } + + // Resample the (possibly variable frame rate) clip to a constant 10fps stream: + reader.readFrames(10, f -> { + // f.getARGB() / f.toImage() give you the decoded RGBA pixels + thumbnails.add(f.toImage()); + return thumbnails.size() < 50; // stop after 50 frames + }); + reader.close(); + // end::video-io-java-003[] + } + + void decodeAudio() throws Exception { + // tag::video-io-java-004[] + VideoReader reader = VideoIO.getVideoIO().openReader(videoPath); + if (reader.hasAudio()) { + AudioBuffer pcm = reader.readAudio(); + System.out.println("Decoded " + pcm.getSize() + " PCM samples at " + + reader.getAudioSampleRate() + "Hz, " + + reader.getAudioChannels() + " channels"); + } + reader.close(); + // end::video-io-java-004[] + } + + void discoverCodecs() { + // tag::video-io-java-005[] + VideoIO io = VideoIO.getVideoIO(); + for (VideoCodec codec : io.getAvailableEncoders()) { + System.out.println(codec.getId() + + " (" + codec.getName() + ")" + + (codec.isHardwareAccelerated() ? " [hardware]" : "")); + } + boolean canEncodeH264 = io.isEncoderSupported(VideoIO.CODEC_H264); + // end::video-io-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WearablesJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WearablesJava001Snippet.java new file mode 100644 index 00000000000..0cf4bf5fb99 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WearablesJava001Snippet.java @@ -0,0 +1,72 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WearablesJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::wearables-java-001[] + Form f = new Form(BoxLayout.y()); + if (CN.isWatch()) { + // Compact, single-column layout suited to a small round/square screen + f.add(new Label("Hi Watch")); + f.getToolbar().setVisible(false); + } else { + // Full phone/tablet layout + f.add(new SpanLabel("Welcome to the full size application")); + } + f.show(); + // end::wearables-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithIosJava002Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithIosJava002Snippet.java new file mode 100644 index 00000000000..df0116754d5 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithIosJava002Snippet.java @@ -0,0 +1,62 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithIosJava002Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::working-with-ios-java-002[] + public void localNotificationReceived(String notificationId) { + } + // end::working-with-ios-java-002[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava001Snippet.java new file mode 100644 index 00000000000..18aad7c3b03 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava001Snippet.java @@ -0,0 +1,68 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +import com.codename1.io.Log; + +class WorkingWithJavascriptJava001Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-001[] + class Class1 { + public static int getValue() { + Log.p("Hello world"); + return 1; + } + } + // end::working-with-javascript-java-001[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava003Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava003Snippet.java new file mode 100644 index 00000000000..bb836d69261 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava003Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava003Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + // tag::working-with-javascript-java-003[] + public static int getValue() { + return 1; + } + // end::working-with-javascript-java-003[] +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava004Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava004Snippet.java new file mode 100644 index 00000000000..9bd5265e57d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava004Snippet.java @@ -0,0 +1,63 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava004Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-004[] + Display.getInstance().setProperty("javascript.useProxyForSameDomain", "true"); + // end::working-with-javascript-java-004[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava005Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava005Snippet.java new file mode 100644 index 00000000000..aa6daf1fa04 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava005Snippet.java @@ -0,0 +1,66 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava005Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-005[] + Display d = Display.getInstance(); + if (d.getProperty("User-Agent", "Unknown").indexOf("Android") != -1) { + d.setProperty("javascript.native.theme", "/androidTheme.res"); + } + // end::working-with-javascript-java-005[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava006Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava006Snippet.java new file mode 100644 index 00000000000..304fba1ee30 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava006Snippet.java @@ -0,0 +1,74 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava006Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-006[] + Form f = new Form("Test Before Unload", BoxLayout.y()); + CheckBox enableBeforeUnload = new CheckBox("Enable Before Unload"); + enableBeforeUnload.setSelected(true); + enableBeforeUnload.addActionListener(e->{ + if (enableBeforeUnload.isSelected()) { + CN.setProperty("platformHint.javascript.beforeUnloadMessage", "Are you sure you want to leave this page? It might be bad"); + } else { + CN.setProperty("platformHint.javascript.beforeUnloadMessage", null); + } + }); + f.add(enableBeforeUnload); + f.show(); + // end::working-with-javascript-java-006[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava007Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava007Snippet.java new file mode 100644 index 00000000000..e046b7c2bba --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava007Snippet.java @@ -0,0 +1,72 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava007Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-007[] + CN.setProperty("platformHint.javascript.backsideHooksInterval", "1000"); + + // Now your app will process media.play() and Display.execute(null) calls + // once per second (1000ms). If play() or execute() has been called anytime + // in that second (since the last poll), it will seamlessly process the + // request. + + + // To disable polling, just set it to an interval 0 or lower. + // for example, CN.setProperty("platformHint.javascript.backsideHooksInterval", "0"); + // end::working-with-javascript-java-007[] + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava008Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava008Snippet.java new file mode 100644 index 00000000000..490cee15ed3 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava008Snippet.java @@ -0,0 +1,64 @@ +package com.codenameone.developerguide.snippets.generated; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class WorkingWithJavascriptJava008Snippet { + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + void snippet() throws Exception { + // tag::working-with-javascript-java-008[] + BrowserComponent browser = new BrowserComponent(); + browser.putClientProperty("HTML5Peer.removeOnDeinitialize", Boolean.FALSE); + // end::working-with-javascript-java-008[] + } +} diff --git a/docs/demos/common/src/main/java/com/example/petstore/PetApi.java b/docs/demos/common/src/main/java/com/example/petstore/PetApi.java new file mode 100644 index 00000000000..45e66f18216 --- /dev/null +++ b/docs/demos/common/src/main/java/com/example/petstore/PetApi.java @@ -0,0 +1,31 @@ +package com.example.petstore; + +import com.codename1.annotations.rest.Body; +import com.codename1.annotations.rest.GET; +import com.codename1.annotations.rest.Header; +import com.codename1.annotations.rest.POST; +import com.codename1.annotations.rest.Path; +import com.codename1.annotations.rest.RestClient; +import com.codename1.io.rest.Response; +import com.codename1.io.rest.RestClients; +import com.codename1.util.OnComplete; + +// tag::appendix-goal-generate-openapi-java-001[] +@RestClient +public interface PetApi { + + @GET("/pet/{petId}") + void getPetById(@Path("petId") Long petId, + @Header("Authorization") String bearerToken, + OnComplete> callback); + + @POST("/pet") + void addPet(@Body com.example.petstore.model.Pet body, + @Header("Authorization") String bearerToken, + OnComplete> callback); + + static PetApi of(String baseUrl) { + return RestClients.create(PetApi.class, baseUrl); + } +} +// end::appendix-goal-generate-openapi-java-001[] diff --git a/docs/demos/common/src/main/java/com/example/petstore/model/Pet.java b/docs/demos/common/src/main/java/com/example/petstore/model/Pet.java new file mode 100644 index 00000000000..98ac91c5ad8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/example/petstore/model/Pet.java @@ -0,0 +1,18 @@ +package com.example.petstore.model; + +import com.codename1.properties.Property; +import com.codename1.properties.PropertyBusinessObject; +import com.codename1.properties.PropertyIndex; + +// tag::appendix-goal-generate-openapi-java-002[] +public class Pet implements PropertyBusinessObject { + public final Property id = new Property("id"); + public final Property name = new Property("name"); + private final PropertyIndex index = new PropertyIndex(this, "Pet", id, name); + + @Override + public PropertyIndex getPropertyIndex() { + return index; + } +} +// end::appendix-goal-generate-openapi-java-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties new file mode 100644 index 00000000000..5251b5e10d6 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties @@ -0,0 +1,96 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-properties-001[] +android.debug +android.release +android.installLocation +android.licenseKey +android.stack_size +android.statusbar_hidden +android.googleAdUnitId +android.includeGPlayServices +android.headphoneCallback +android.gpsPermission +android.asyncPaint +android.supportV4 +android.theme +android.cusom_layout1 +android.versionCode +android.captureRecord +android.removeBasePermissions +android.blockExternalStoragePermission +android.min_sdk_version +android.smallScreens +android.streamMode +android.enableProguard +android.targetSDKVersion +android.web_loading_hidden +facebook.appId +facebook.clientToken +ios.keyboardOpen +ios.project_type +ios.newStorageLocation +ios.prerendered_icon +ios.application_exits +ios.themeMode +ios.xcode_version +javascript.inject_proxy +javascript.minifying +javascript.proxy.url +javascript.sourceFilesCopied +javascript.teavm.version +google.adUnitId +ios.includePush +ios.headphoneCallback +ios.enableAutoplayVideo +ios.googleAdUnitId +ios.googleAdUnitIdPadding +ios.enableBadgeClear +ios.locationUsageDescription +ios.bundleVersion +ios.objC +ios.testFlight +ios.metal +desktop.width +desktop.height +desktop.adaptToRetina +desktop.resizable +desktop.fontSizes +desktop.theme +desktop.themeMac +desktop.themeWin +desktop.windowsOutput +noExtraResources +android.permission. +// end::advanced-topics-under-the-hood-properties-001[] + +// tag::advanced-topics-under-the-hood-properties-002[] +android.xapplication +android.xpermissions +android.xintent_filter +android.facebook_permissions +android.stringsXml +android.style +android.nonconsumable +android.xapplication_attr +android.xactivity +android.pushVibratePattern +android.proguardKeep +android.sharedUserId +android.sharedUserLabel +ios.urlScheme +ios.interface_orientation +ios.plistInject +ios.facebook_permissions +ios.applicationDidEnterBackground +ios.viewDidLoad +ios.glAppDelegateHeader +ios.glAppDelegateBody +ios.beforeFinishLaunching +ios.afterFinishLaunching +ios.add_libs +// end::advanced-topics-under-the-hood-properties-002[] + +// tag::advanced-topics-under-the-hood-properties-003[] +codename1.arg.android.xapplication= +// end::advanced-topics-under-the-hood-properties-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh new file mode 100644 index 00000000000..d1f88989684 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh @@ -0,0 +1,20 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-bash-001[] +mvn archetype:generate \ + -DarchetypeArtifactId=cn1lib-archetype \ + -DarchetypeGroupId=com.codenameone \ + -DarchetypeVersion=LATEST \ + -DgroupId=com.example.mylib \ + -DartifactId=mylib \ + -Dversion=1.0-SNAPSHOT \ + -DinteractiveMode=false +// end::advanced-topics-under-the-hood-bash-001[] + +// tag::advanced-topics-under-the-hood-bash-002[] +java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project +// end::advanced-topics-under-the-hood-bash-002[] + +// tag::advanced-topics-under-the-hood-bash-003[] +java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project +// end::advanced-topics-under-the-hood-bash-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt new file mode 100644 index 00000000000..8104e2ebe7d --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt @@ -0,0 +1,179 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-bytecode-001[] + Last modified 10-Jul-2017; size 1456 bytes + MD5 checksum 1cb00f6e63b918bb5a9f146ca8b0b78e + Compiled from "KotlinForm.kt" +public final class com.codename1.hellokotlin2.KotlinForm extends com.codename1.ui.Form + SourceFile: "KotlinForm.kt" + InnerClasses: + static final #31; //class com/codename1/hellokotlin2/KotlinForm$1 + RuntimeVisibleAnnotations: + 0: #56(#57=[I#58,I#58,I#59],#60=[I#58,I#61,I#58],#62=I#58,#63=[s#64],#65=[s#55,s#66,s#6,s#67]) + minor version: 0 + major version: 50 + flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER +Constant pool: + #1 = Utf8 com/codename1/hellokotlin2/KotlinForm + #2 = Class #1 // com/codename1/hellokotlin2/KotlinForm + #3 = Utf8 com/codename1/ui/Form + #4 = Class #3 // com/codename1/ui/Form + #5 = Utf8 + #6 = Utf8 ()V + #7 = Utf8 Hello Kotlin + #8 = String #7 // Hello Kotlin + #9 = Utf8 com/codename1/ui/layouts/BoxLayout + #10 = Class #9 // com/codename1/ui/layouts/BoxLayout + #11 = Utf8 y + #12 = Utf8 ()Lcom/codename1/ui/layouts/BoxLayout; + #13 = NameAndType #11:#12 // y:()Lcom/codename1/ui/layouts/BoxLayout; + #14 = Methodref #10.#13 // com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; + #15 = Utf8 com/codename1/ui/layouts/Layout + #16 = Class #15 // com/codename1/ui/layouts/Layout + #17 = Utf8 (Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V + #18 = NameAndType #5:#17 // "":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V + #19 = Methodref #4.#18 // com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V + #20 = Utf8 com/codename1/ui/Label + #21 = Class #20 // com/codename1/ui/Label + #22 = Utf8 (Ljava/lang/String;)V + #23 = NameAndType #5:#22 // "":(Ljava/lang/String;)V + #24 = Methodref #21.#23 // com/codename1/ui/Label."":(Ljava/lang/String;)V + #25 = Utf8 com/codename1/ui/Button + #26 = Class #25 // com/codename1/ui/Button + #27 = Utf8 Click Me + #28 = String #27 // Click Me + #29 = Methodref #26.#23 // com/codename1/ui/Button."":(Ljava/lang/String;)V + #30 = Utf8 com/codename1/hellokotlin2/KotlinForm$1 + #31 = Class #30 // com/codename1/hellokotlin2/KotlinForm$1 + #32 = Utf8 (Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V + #33 = NameAndType #5:#32 // "":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V + #34 = Methodref #31.#33 // com/codename1/hellokotlin2/KotlinForm$1."":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V + #35 = Utf8 com/codename1/ui/events/ActionListener + #36 = Class #35 // com/codename1/ui/events/ActionListener + #37 = Utf8 addActionListener + #38 = Utf8 (Lcom/codename1/ui/events/ActionListener;)V + #39 = NameAndType #37:#38 // addActionListener:(Lcom/codename1/ui/events/ActionListener;)V + #40 = Methodref #26.#39 // com/codename1/ui/Button.addActionListener:(Lcom/codename1/ui/events/ActionListener;)V + #41 = Utf8 com/codename1/ui/Component + #42 = Class #41 // com/codename1/ui/Component + #43 = Utf8 add + #44 = Utf8 (Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + #45 = NameAndType #43:#44 // add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + #46 = Methodref #2.#45 // com/codename1/hellokotlin2/KotlinForm.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + #47 = Utf8 com/codename1/ui/Container + #48 = Class #47 // com/codename1/ui/Container + #49 = Methodref #48.#45 // com/codename1/ui/Container.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + #50 = Utf8 clickMe + #51 = Utf8 Lcom/codename1/ui/Button; + #52 = Utf8 label + #53 = Utf8 Lcom/codename1/ui/Label; + #54 = Utf8 this + #55 = Utf8 Lcom/codename1/hellokotlin2/KotlinForm; + #56 = Utf8 Lkotlin/Metadata; + #57 = Utf8 mv + #58 = Integer 1 + #59 = Integer 6 + #60 = Utf8 bv + #61 = Integer 0 + #62 = Utf8 k + #63 = Utf8 d1 + #64 = Utf8 + \n\n\20¢¨ + #65 = Utf8 d2 + #66 = Utf8 Lcom/codename1/ui/Form; + #67 = Utf8 HelloKotlin2 + #68 = Utf8 KotlinForm.kt + #69 = Utf8 Code + #70 = Utf8 LocalVariableTable + #71 = Utf8 LineNumberTable + #72 = Utf8 SourceFile + #73 = Utf8 InnerClasses + #74 = Utf8 RuntimeVisibleAnnotations +{ + public com.codename1.hellokotlin2.KotlinForm(); + descriptor: ()V + flags: ACC_PUBLIC + Code: + stack=5, locals=3, args_size=1 + 0: aload_0 + 1: ldc #8 // String Hello Kotlin + 3: invokestatic #14 // Method com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; + 6: checkcast #16 // class com/codename1/ui/layouts/Layout + 9: invokespecial #19 // Method com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V + 12: new #21 // class com/codename1/ui/Label + 15: dup + 16: ldc #8 // String Hello Kotlin + 18: invokespecial #24 // Method com/codename1/ui/Label."":(Ljava/lang/String;)V + 21: astore_1 + 22: new #26 // class com/codename1/ui/Button + 25: dup + 26: ldc #28 // String Click Me + 28: invokespecial #29 // Method com/codename1/ui/Button."":(Ljava/lang/String;)V + 31: astore_2 + 32: aload_2 + 33: new #31 // class com/codename1/hellokotlin2/KotlinForm$1 + 36: dup + 37: aload_0 + 38: aload_1 + 39: invokespecial #34 // Method com/codename1/hellokotlin2/KotlinForm$1."":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V + 42: checkcast #36 // class com/codename1/ui/events/ActionListener + 45: invokevirtual #40 // Method com/codename1/ui/Button.addActionListener:(Lcom/codename1/ui/events/ActionListener;)V + 48: aload_0 + 49: aload_1 + 50: checkcast #42 // class com/codename1/ui/Component + 53: invokevirtual #46 // Method add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + 56: aload_2 + 57: checkcast #42 // class com/codename1/ui/Component + 60: invokevirtual #49 // Method com/codename1/ui/Container.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; + 63: pop + 64: return + LocalVariableTable: + Start Length Slot Name Signature + 32 32 2 clickMe Lcom/codename1/ui/Button; + 22 42 1 label Lcom/codename1/ui/Label; + 0 65 0 this Lcom/codename1/hellokotlin2/KotlinForm; + LineNumberTable: + line 13: 0 + line 14: 12 + line 15: 22 + line 16: 32 + line 21: 48 +} + +// end::advanced-topics-under-the-hood-bytecode-001[] + +// tag::advanced-topics-under-the-hood-bytecode-002[] +InnerClasses: + static final #31; //class com/codename1/hellokotlin2/KotlinForm$1 +// end::advanced-topics-under-the-hood-bytecode-002[] + +// tag::advanced-topics-under-the-hood-bytecode-003[] +Constant pool: + #1 = Utf8 com/codename1/hellokotlin2/KotlinForm + #2 = Class #1 // com/codename1/hellokotlin2/KotlinForm + #3 = Utf8 com/codename1/ui/Form + #4 = Class #3 // com/codename1/ui/Form + #5 = Utf8 + #6 = Utf8 ()V + #7 = Utf8 Hello Kotlin + #8 = String #7 // Hello Kotlin + #9 = Utf8 com/codename1/ui/layouts/BoxLayout + ... etc. +// end::advanced-topics-under-the-hood-bytecode-003[] + +// tag::advanced-topics-under-the-hood-bytecode-004[] +public com.codename1.hellokotlin2.KotlinForm(); + descriptor: ()V + flags: ACC_PUBLIC + Code: + stack=5, locals=3, args_size=1 + 0: aload_0 + 1: ldc #8 // String Hello Kotlin + 3: invokestatic #14 // Method com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; + 6: checkcast #16 // class com/codename1/ui/layouts/Layout + 9: invokespecial #19 // Method com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V + 12: new #21 // class com/codename1/ui/Label + 15: dup + 16: ldc #8 // String Hello Kotlin + etc. +// end::advanced-topics-under-the-hood-bytecode-004[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml new file mode 100644 index 00000000000..f3be8785773 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml @@ -0,0 +1,178 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-xml-001[] + + + + + + + + + + + + + + + + //this doesnt work + + + + + + +// end::advanced-topics-under-the-hood-xml-001[] + +// tag::advanced-topics-under-the-hood-xml-002[] +android.xpermissions= +// end::advanced-topics-under-the-hood-xml-002[] + +// tag::advanced-topics-under-the-hood-xml-003[] +android.xapplication= +// end::advanced-topics-under-the-hood-xml-003[] + +// tag::advanced-topics-under-the-hood-xml-004[] + + + + + + +// end::advanced-topics-under-the-hood-xml-004[] + +// tag::advanced-topics-under-the-hood-xml-005[] + + com.example + mylib-lib + 1.0.0 + pom + +// end::advanced-topics-under-the-hood-xml-005[] + +// tag::advanced-topics-under-the-hood-xml-006[] + + #ff00ff00 + #80ff0000 + #800000ff + +// end::advanced-topics-under-the-hood-xml-006[] + +// tag::advanced-topics-under-the-hood-xml-007[] +android.xintent_filter= +// end::advanced-topics-under-the-hood-xml-007[] + +// tag::advanced-topics-under-the-hood-xml-008[] +ios.plistInject=CFBundleURLTypes CFBundleURLName com.yourcompany.myapp CFBundleURLSchemes myapp +// end::advanced-topics-under-the-hood-xml-008[] + +// tag::advanced-topics-under-the-hood-xml-009[] +ios.urlScheme=myapp +// end::advanced-topics-under-the-hood-xml-009[] + +// tag::advanced-topics-under-the-hood-xml-010[] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// end::advanced-topics-under-the-hood-xml-010[] + +// tag::advanced-topics-under-the-hood-xml-011[] + +// end::advanced-topics-under-the-hood-xml-011[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advertising.properties b/docs/demos/common/src/main/snippets/developer-guide/advertising.properties new file mode 100644 index 00000000000..f1b5fb97500 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advertising.properties @@ -0,0 +1,6 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advertising-properties-001[] +codename1.arg.android.xapplication= +codename1.arg.ios.plistInject=GADApplicationIdentifierca-app-pub-XXXXXXXX~YYYYYYYY +// end::advertising-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/advertising.xml b/docs/demos/common/src/main/snippets/developer-guide/advertising.xml new file mode 100644 index 00000000000..cbfc14aba40 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/advertising.xml @@ -0,0 +1,18 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advertising-xml-001[] + + com.codenameone + cn1-admob-lib + ${cn1.version} + pom + +// end::advertising-xml-001[] + +// tag::advertising-xml-002[] + + com.codenameone + cn1-ads-mock + ${cn1.version} + +// end::advertising-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/ai-and-speech.xml b/docs/demos/common/src/main/snippets/developer-guide/ai-and-speech.xml new file mode 100644 index 00000000000..7098b1402ee --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/ai-and-speech.xml @@ -0,0 +1,10 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::ai-and-speech-xml-001[] + + com.codenameone + cn1-ai-mlkit-barcode-lib + ${cn1.version} + pom + +// end::ai-and-speech-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-build.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-build.sh new file mode 100644 index 00000000000..9ac18d64bf1 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-build.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-build-bash-001[] +mvn cn1:build -Dcodename1.platform=javase -Dcodename1.buildTarget=mac-os-x-desktop +// end::appendix-goal-build-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-clone.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-clone.sh new file mode 100644 index 00000000000..3bb7b493322 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-clone.sh @@ -0,0 +1,7 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-clone-bash-001[] +mvn cn1:clone \ + -DgroupId=com.example.newgroup \ + -DartifactId=newapp \ +// end::appendix-goal-clone-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-create-gui-form.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-create-gui-form.sh new file mode 100644 index 00000000000..915372c6732 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-create-gui-form.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-create-gui-form-bash-001[] +mvn cn1:create-gui-form -DclassName=com.example.MyForm +// end::appendix-goal-create-gui-form-bash-001[] + +// tag::appendix-goal-create-gui-form-bash-002[] +mvn cn1:guibuilder -DclassName=com.example.MyForm +// end::appendix-goal-create-gui-form-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-designer.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-designer.sh new file mode 100644 index 00000000000..4810ef3f9fe --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-designer.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-designer-bash-001[] +mvn cn1:designer +// end::appendix-goal-designer-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.sh new file mode 100644 index 00000000000..5e8dc7965b2 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-app-project-bash-001[] +mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ + -DsourceProject=/path/to/my/ProjectTemplate \ + -DgroupId=com.example \ + -DartifactId=myapp \ + -Dcn1Version=$CN1VERSION +// end::appendix-goal-generate-app-project-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.txt b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.txt new file mode 100644 index 00000000000..ec006a6c672 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.txt @@ -0,0 +1,17 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-app-project-rpf-001[] +template.mainName=MyApp +template.packageName=com.example +template.type=maven + +[dependencies] +==== + + com.codenameone + googlemaps-lib + 1.0.1 + pom + +==== +// end::appendix-goal-generate-app-project-rpf-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt new file mode 100644 index 00000000000..2c8fe7cf39d --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt @@ -0,0 +1,193 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-archetype-text-001[] +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +package ${package}; +/* +[archetype] <1> +---- +extends=../cn1app-archetype <2> +groupId=com.codenameone.archetypes +artifactId=helloworld2-archetype +version=7.0-SNAPSHOT +---- + +[dependencies] <3> +---- + +com.codenameone.libs +filechooser-lib +1.0-SNAPSHOT +pom + +---- + */ +import static com.codename1.ui.CN.*; +import com.codename1.ui.*; +import com.codename1.ui.layouts.*; +import com.codename1.io.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.Resources; + +/** + * This file was generated by Codename One for the purpose + * of building native mobile applications using Java. + */ +public class ${mainName} { + + private Form current; + private Resources theme; + + public void init(Object context) { + // use two network threads instead of one + updateNetworkThreadCount(2); + + theme = UIManager.initFirstTheme("/theme"); + + // Enable Toolbar on all Forms by default + Toolbar.setGlobalToolbar(true); + + // Pro feature + Log.bindCrashProtection(true); + + addNetworkErrorListener(err -> { + // prevent the event from propagating + err.consume(); + if(err.getError()!= null) { + Log.e(err.getError()); + } + Log.sendLogAsync(); + Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null); + }); + } + + + public void start() { + if(current!= null){ + current.show(); + return; + } + //WebSocket sock; + Form hi = new Form("Hi World", BoxLayout.y()); + hi.add(new Label("Hello World")); + + hi.show(); + } + + public void stop() { + current = getCurrentForm(); + if(current instanceof Dialog) { + ((Dialog)current).dispose(); + current = getCurrentForm(); + } + } + + public void destroy() { + } + +} + +// end::appendix-goal-generate-archetype-text-001[] + +// tag::appendix-goal-generate-archetype-text-002[] +[archetype] +---- +extends=../cn1app-archetype +groupId=com.codenameone.archetypes +artifactId=helloworld2-archetype +version=7.0-SNAPSHOT +---- +// end::appendix-goal-generate-archetype-text-002[] + +// tag::appendix-goal-generate-archetype-text-003[] +[dependencies] +--- + + com.codenameone.libs + filechooser-lib + 1.0-SNAPSHOT + pom + +--- +// end::appendix-goal-generate-archetype-text-003[] + +// tag::appendix-goal-generate-archetype-text-004[] +[css] +--- +#Constants { + includeNativeBool: true; +} +Button { + color:green; + border:1px solid green; + border-radius: 2mm; + margin: 5mm; +} +--- +// end::appendix-goal-generate-archetype-text-004[] + +// tag::appendix-goal-generate-archetype-text-005[] +[properties] +--- +codename1.arg.win.desktop-vm=zuluFx8-32bit +codename1.arg.win.desktopExtractDll=true +codename1.arg.win.launchOnStart=true +codename1.arg.win.runAfterInstall=true +--- +// end::appendix-goal-generate-archetype-text-005[] + +// tag::appendix-goal-generate-archetype-text-006[] +[files] +---- +src/main/guibuilder/__mainName__MainForm.gui +src/main/java/__mainName__MainForm.java +---- + +[file:src/main/guibuilder/__mainName__MainForm.gui] +---- + + + + + + + +---- + +[file:src/main/java/__mainName__MainForm.java] +---- +package ${package}; +public class ${mainName}MainForm extends com.codename1.ui.Form { +public ${mainName}MainForm() { +this(com.codename1.ui.util.Resources.getGlobalResources()); +} + + public ${mainName}MainForm(com.codename1.ui.util.Resources resourceObjectInstance) { + initGuiBuilderComponents(resourceObjectInstance); + } + +//-- DON'T EDIT BELOW THIS LINE!!! + protected com.codename1.ui.Button gui_Button = new com.codename1.ui.Button(); + + +// + private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) { + setLayout(new com.codename1.ui.layouts.LayeredLayout()); + setInlineStylesTheme(resourceObjectInstance); + setScrollableY(true); + setInlineStylesTheme(resourceObjectInstance); + setTitle("MyForm"); + setName("MyForm"); + gui_Button.setText("Click Me"); + gui_Button.setInlineStylesTheme(resourceObjectInstance); + gui_Button.setName("Button"); + addComponent(gui_Button); + ((com.codename1.ui.layouts.LayeredLayout)gui_Button.getParent().getLayout()).setInsets(gui_Button, "auto auto auto auto").setReferenceComponents(gui_Button, "-1 -1 -1 -1").setReferencePositions(gui_Button, "0.0 0.0 0.0 0.0"); + }// + +//-- DON'T EDIT ABOVE THIS LINE!!! +} +---- +// end::appendix-goal-generate-archetype-text-006[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.sh new file mode 100644 index 00000000000..afdb5c59b52 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.sh @@ -0,0 +1,15 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-cn1lib-project-bash-001[] +mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-cn1lib-project \ + -DsourceProject=/path/to/MyLegacyAntLibraryProject \ + -DgroupId=com.example \ + -DartifactId=my-maven-lib \ + -Dversion=1.0-SNAPSHOT \ + -U +// end::appendix-goal-generate-cn1lib-project-bash-001[] + +// tag::appendix-goal-generate-cn1lib-project-bash-002[] +cd my-maven-lib +mvn install +// end::appendix-goal-generate-cn1lib-project-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.xml b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.xml new file mode 100644 index 00000000000..7c41774b1c9 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.xml @@ -0,0 +1,10 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-cn1lib-project-xml-001[] + + com.example + my-maven-lib-lib + 1.0-SNAPSHOT + pom + +// end::appendix-goal-generate-cn1lib-project-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-graphql.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-graphql.sh new file mode 100644 index 00000000000..c1e462652d9 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-graphql.sh @@ -0,0 +1,8 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-graphql-bash-001[] +mvn -pl common cn1:generate-graphql \ + -Dcn1.graphql.schema=schema.graphqls \ + -Dcn1.graphql.operations=operations.graphql \ + -Dcn1.graphql.basePackage=com.example.starwars +// end::appendix-goal-generate-graphql-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-grpc.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-grpc.sh new file mode 100644 index 00000000000..e4263fac8a0 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-grpc.sh @@ -0,0 +1,7 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-grpc-bash-001[] +mvn -pl common cn1:generate-grpc \ + -Dcn1.grpc.proto=helloworld.proto \ + -Dcn1.grpc.basePackage=com.example.hello +// end::appendix-goal-generate-grpc-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-native-interfaces.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-native-interfaces.sh new file mode 100644 index 00000000000..0bc6fc037b9 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-native-interfaces.sh @@ -0,0 +1,11 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-native-interfaces-bash-001[] +mvn cn1:generate-native-interfaces +// end::appendix-goal-generate-native-interfaces-bash-001[] + +// tag::appendix-goal-generate-native-interfaces-bash-002[] +mvn cn1:generate-native-interfaces \ + -Dcn1.generateNativeInterfaces.swift=true \ + -Dcn1.generateNativeInterfaces.kotlin=true +// end::appendix-goal-generate-native-interfaces-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-openapi.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-openapi.sh new file mode 100644 index 00000000000..00b80b76a43 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-generate-openapi.sh @@ -0,0 +1,7 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-generate-openapi-bash-001[] +mvn -pl common cn1:generate-openapi \ + -Dcn1.openapi.spec=petstore.json \ + -Dcn1.openapi.basePackage=com.example.petstore +// end::appendix-goal-generate-openapi-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-guibuilder.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-guibuilder.sh new file mode 100644 index 00000000000..38f2d63fced --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-guibuilder.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-guibuilder-bash-001[] +mvn cn1:guibuilder -DclassName=com.example.MyForm +// end::appendix-goal-guibuilder-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-install-cn1lib.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-install-cn1lib.sh new file mode 100644 index 00000000000..dc0714f57fb --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-install-cn1lib.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-install-cn1lib-bash-001[] +mvn cn1:install-cn1lib -Dfile=/path/to/MyLegacyLib.cn1lib +// end::appendix-goal-install-cn1lib-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-test.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-test.sh new file mode 100644 index 00000000000..b237a4fc069 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-test.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-test-bash-001[] +mvn install +// end::appendix-goal-test-bash-001[] + +// tag::appendix-goal-test-bash-002[] +mvn package +// end::appendix-goal-test-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-update.sh b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-update.sh new file mode 100644 index 00000000000..3b1ff5dcdf8 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/appendix-goal-update.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::appendix-goal-update-bash-001[] +mvn cn1:update +// end::appendix-goal-update-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.json b/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.json new file mode 100644 index 00000000000..2e9867bda60 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.json @@ -0,0 +1,19 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::apple-wallet-extension-json-001[] +{ + "certificates": ["base64...", "base64..."], + "nonce": "base64...", + "nonceSignature": "base64...", + "cardIdentifier": "the WalletPassEntry identifier", + "authToken": "the token published via setAuthToken" +} +// end::apple-wallet-extension-json-001[] + +// tag::apple-wallet-extension-json-002[] +{ + "activationData": "base64...", + "encryptedPassData": "base64...", + "ephemeralPublicKey": "base64..." +} +// end::apple-wallet-extension-json-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties b/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties new file mode 100644 index 00000000000..82f992af485 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties @@ -0,0 +1,17 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::apple-wallet-extension-properties-001[] +codename1.arg.ios.wallet.extension=true +codename1.arg.ios.wallet.appGroup=group.com.mybank.app +codename1.arg.ios.wallet.issuerEndpoint=https://api.mybank.com/wallet/provision +// end::apple-wallet-extension-properties-001[] + +// tag::apple-wallet-extension-properties-002[] +codename1.arg.ios.wallet.includeUI=true +codename1.arg.ios.wallet.authEndpoint=https://api.mybank.com/wallet/login +// end::apple-wallet-extension-properties-002[] + +// tag::apple-wallet-extension-properties-003[] +codename1.arg.ios.wallet.nonuiProvisioningProfile=WalletNonUI.mobileprovision +codename1.arg.ios.wallet.uiProvisioningProfile=WalletUI.mobileprovision +// end::apple-wallet-extension-properties-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/basics.sh b/docs/demos/common/src/main/snippets/developer-guide/basics.sh new file mode 100644 index 00000000000..752b538b833 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/basics.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::basics-bash-001[] +mvn cn1:create-gui-form -DclassName=com.example.MyForm +// end::basics-bash-001[] + +// tag::basics-bash-002[] +mvn cn1:guibuilder -DclassName=com.example.MyForm +// end::basics-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/basics.xml b/docs/demos/common/src/main/snippets/developer-guide/basics.xml new file mode 100644 index 00000000000..9e3bc8f3a19 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/basics.xml @@ -0,0 +1,13 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::basics-xml-001[] + + + + + + + + +// end::basics-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/biometric-authentication.properties b/docs/demos/common/src/main/snippets/developer-guide/biometric-authentication.properties new file mode 100644 index 00000000000..4865c235a43 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/biometric-authentication.properties @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::biometric-authentication-properties-001[] +ios.NSFaceIDUsageDescription=Authenticate to securely access your account +// end::biometric-authentication-properties-001[] + +// tag::biometric-authentication-properties-002[] +ios.keychainAccessGroup=TEAMID123.group.com.example.app +// end::biometric-authentication-properties-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.properties b/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.properties new file mode 100644 index 00000000000..092d29f5379 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.properties @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::deep-links-routing-properties-001[] +codename1.arg.ios.associatedDomains=applinks:example.com,applinks:www.example.com +// end::deep-links-routing-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.sh b/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.sh new file mode 100644 index 00000000000..6d17278f323 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/deep-links-routing.sh @@ -0,0 +1,10 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::deep-links-routing-sh-001[] +xcrun simctl openurl booted "https://example.com/users/42" +// end::deep-links-routing-sh-001[] + +// tag::deep-links-routing-sh-002[] +adb shell am start -a android.intent.action.VIEW \ + -d "https://example.com/users/42" com.example.app +// end::deep-links-routing-sh-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/desktop-integration.properties b/docs/demos/common/src/main/snippets/developer-guide/desktop-integration.properties new file mode 100644 index 00000000000..bd4bd2d90ca --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/desktop-integration.properties @@ -0,0 +1,6 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::desktop-integration-properties-001[] +codename1.arg.desktop.titleBar=native +codename1.arg.desktop.interactiveScrollbars=true +// end::desktop-integration-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/game-assets.json b/docs/demos/common/src/main/snippets/developer-guide/game-assets.json new file mode 100644 index 00000000000..3407729c80d --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/game-assets.json @@ -0,0 +1,20 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::game-assets-json-001[] +{ "id": "player", "name": "Player", "kind": "actor", + "w": 28, "h": 32, "color": "#4D86FF", "unique": true, + "defaults": { "lives": 3, "jumpHeight": 96 } } +// end::game-assets-json-001[] + +// tag::game-assets-json-002[] +{ + "id": "key", + "name": "Key", + "kind": "actor", + "w": 24, + "h": 24, + "color": "#E0C040", + "unique": false, + "defaults": { "opens": "door", "value": 1 } +} +// end::game-assets-json-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/game-builder.sh b/docs/demos/common/src/main/snippets/developer-guide/game-builder.sh new file mode 100644 index 00000000000..044967d7220 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/game-builder.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::game-builder-bash-001[] +mvn cn1:create-game-scene -DclassName=com.example.game.Level1 -Dmode=2d +// end::game-builder-bash-001[] + +// tag::game-builder-bash-002[] +mvn cn1:gamebuilder +// end::game-builder-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/index.sh b/docs/demos/common/src/main/snippets/developer-guide/index.sh new file mode 100644 index 00000000000..90c9137c7dc --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/index.sh @@ -0,0 +1,13 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::index-bash-001[] +mvn archetype:generate \ + -DarchetypeGroupId=com.codenameone \ + -DarchetypeArtifactId=cn1app-archetype \ + -DarchetypeVersion=LATEST \ + -DgroupId=YOUR_GROUP_ID \ + -DartifactId=YOUR_ARTIFACT_ID \ + -Dversion=1.0-SNAPSHOT \ + -DmainName=YOUR_MAIN_NAME \ + -DinteractiveMode=false +// end::index-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/io.xml b/docs/demos/common/src/main/snippets/developer-guide/io.xml new file mode 100644 index 00000000000..a7eaabf13ea --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/io.xml @@ -0,0 +1,62 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::io-xml-001[] + + + OK + + + London + London + locality + political + + + + Ontario + ON + administrative_area_level_1 + political + + + Canada + CA + country + political + + + +// end::io-xml-001[] + +// tag::io-xml-002[] + + + Bernard + Tomic + SOUTHPORT + QLD + 1992-10-21 + + + Mathew + Ebden + CHURCHLANDS + WA + 1987-11-26 + + + Lleyton + Hewitt + EXETER + SA + 1981-02-24 + + + +// end::io-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-control-center.sh b/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-control-center.sh new file mode 100644 index 00000000000..49950227d7a --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-control-center.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-appendix-control-center-bash-001[] +./run.sh settings +// end::maven-appendix-control-center-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-rich-properties.txt b/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-rich-properties.txt new file mode 100644 index 00000000000..e06d78998ed --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-appendix-rich-properties.txt @@ -0,0 +1,31 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-appendix-rich-properties-rpf-001[] +[keyname] <1> +=== <2> +Key value <3> +=== <4> +// end::maven-appendix-rich-properties-rpf-001[] + +// tag::maven-appendix-rich-properties-rpf-002[] +firstName=Bob +lastName=Smith +[bio] +==== +Bob is a hard worker. +He attended Harvard and is looking for opportunities in the fast food industry. +==== +age=23 + +# A comment that it is ignored +[xmldata] +======== + + Bob Smith + 23 + +======== + +favoriteColor=Brown + +// end::maven-appendix-rich-properties-rpf-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.properties b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.properties new file mode 100644 index 00000000000..d24c19342ba --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.properties @@ -0,0 +1,36 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-creating-cn1libs-properties-001[] +# META-INF/codenameone/simulator-hooks.properties +name=Bluetooth +namespace=bluetooth # optional; defaults to slugified `name` + +# Each itemN is the action; the matching labelN is the menu text. +# Items are positional — the loader reads item1, item2, item3, ... and +# stops at the first missing index. Don't skip numbers. +item1=com.example.bt.simulator.Hooks#toggleAdapter +label1=Toggle adapter on/off + +item2=com.example.bt.simulator.Hooks#addDemoPeripheral +label2=Add demo peripheral + +# Label omitted → API-only hook. Callable from tests, invisible in menu. +item3=com.example.bt.simulator.Hooks#primeReadFailure +// end::maven-creating-cn1libs-properties-001[] + +// tag::maven-creating-cn1libs-properties-002[] +name=Bluetooth +namespace=bluetooth + +item1=com.codename1.bluetoothle.BluetoothSimulatorHooks#toggleAdapter +label1=Toggle adapter on/off + +item2=com.codename1.bluetoothle.BluetoothSimulatorHooks#addDemoPeripheral +label2=Add demo peripheral + +item3=com.codename1.bluetoothle.BluetoothSimulatorHooks#switchToNativeBle +label3=Switch backend → native BLE (real hardware) + +# API-only: used by the test suite but never displayed in the menu +item4=com.codename1.bluetoothle.BluetoothSimulatorHooks#primeReadFailure +// end::maven-creating-cn1libs-properties-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh new file mode 100644 index 00000000000..f15a5c186a2 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh @@ -0,0 +1,25 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-creating-cn1libs-bash-001[] +mvn archetype:generate \ + -DarchetypeArtifactId=cn1lib-archetype \ + -DarchetypeGroupId=com.codenameone \ + -DarchetypeVersion=LATEST \ + -DgroupId=com.example.mylib \ + -DartifactId=mylib \ + -Dversion=1.0-SNAPSHOT \ + -DinteractiveMode=false +// end::maven-creating-cn1libs-bash-001[] + +// tag::maven-creating-cn1libs-bash-002[] +mvn archetype:generate +// end::maven-creating-cn1libs-bash-002[] + +// tag::maven-creating-cn1libs-bash-003[] +mvn archetype:generate -DarchetypeGroupId=com.codenameone \ + -DarchetypeArtifactId=cn1lib-archetype +// end::maven-creating-cn1libs-bash-003[] + +// tag::maven-creating-cn1libs-bash-004[] +mvn install +// end::maven-creating-cn1libs-bash-004[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.txt b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.txt new file mode 100644 index 00000000000..5f5647c99c0 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.txt @@ -0,0 +1,88 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-creating-cn1libs-listing-001[] +Steves-Mac-Pro:MyFirstLibrary shannah$ find . +. +./tests +./tests/pom.xml +./tests/javase +./tests/javase/pom.xml +./tests/common +./tests/common/codenameone_settings.properties +./tests/common/pom.xml +./tests/common/nbactions.xml +./tests/common/src +./tests/common/src/test +./tests/common/src/test/java +./tests/common/src/test/java/com +./tests/common/src/test/java/com/example +./tests/common/src/test/java/com/example/myfirstlib +./tests/common/src/test/java/com/example/myfirstlib/MyFirstTest.java +./tests/common/src/main +./tests/common/src/main/css +./tests/common/src/main/css/theme.css +./tests/common/src/main/java +./tests/common/src/main/java/com +./tests/common/src/main/java/com/example +./tests/common/src/main/java/com/example/myfirstlib +./tests/common/src/main/java/com/example/myfirstlib/LibraryTests.java +./tests/cn1libs +./tests/.mvn +./tests/.mvn/jvm.config +./pom.xml +./javase +./javase/pom.xml +./javase/src +./javase/src/main +./javase/src/main/java +./javase/src/main/java/com +./javase/src/main/java/com/example +./javase/src/main/java/com/example/myfirstlib +./ios +./ios/pom.xml +./ios/src +./ios/src/main +./ios/src/main/objectivec +./common +./common/codenameone_library_required.properties +./common/pom.xml +./common/codenameone_library_appended.properties +./common/src +./common/src/test +./common/src/test/java +./common/src/test/java/com +./common/src/test/java/com/example +./common/src/test/java/com/example/myfirstlib +./common/src/test/java/com/example/myfirstlib/MyLibraryTest.java +./common/src/main +./common/src/main/css +./common/src/main/css/theme.css +./common/src/main/java +./common/src/main/java/com +./common/src/main/java/com/example +./common/src/main/java/com/example/myfirstlib +./common/src/main/java/com/example/myfirstlib/MyLibrary.java +./android +./android/pom.xml +./android/src +./android/src/main +./android/src/main/java +./android/src/main/java/com +./android/src/main/java/com/example +./android/src/main/java/com/example/myfirstlib +./lib +./lib/pom.xml +./MyFirstLibrary.iml +./javascript +./javascript/pom.xml +./javascript/src +./javascript/src/main +./javascript/src/main/javascript +./.idea +./.idea/encodings.xml +./.idea/jarRepositories.xml +./.idea/.gitignore +./.idea/workspace.xml +./.idea/misc.xml +./.idea/compiler.xml +// end::maven-creating-cn1libs-listing-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.xml b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.xml new file mode 100644 index 00000000000..7e9083d88e3 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.xml @@ -0,0 +1,14 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-creating-cn1libs-xml-001[] + +// end::maven-creating-cn1libs-xml-001[] + +// tag::maven-creating-cn1libs-xml-002[] + + com.example + mylib-lib + 1.0-SNAPSHOT + pom + +// end::maven-creating-cn1libs-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.sh b/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.sh new file mode 100644 index 00000000000..796fbee0a8f --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.sh @@ -0,0 +1,68 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-getting-started-bash-001[] +java -version +mvn -v +// end::maven-getting-started-bash-001[] + +// tag::maven-getting-started-bash-002[] +mvn archetype:generate \ + -DarchetypeGroupId=com.codenameone \ + -DarchetypeArtifactId=cn1app-archetype \ + -DarchetypeVersion=LATEST \ + -DgroupId=YOUR_GROUP_ID \ + -DartifactId=YOUR_ARTIFACT_ID \ + -Dversion=1.0-SNAPSHOT \ + -DmainName=YOUR_MAIN_NAME \ + -DinteractiveMode=false +// end::maven-getting-started-bash-002[] + +// tag::maven-getting-started-bash-003[] +mvn com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project \ + -DarchetypeGroupId=$archetypeGroupId \ + -DarchetypeArtifactId=$archetypeArtifactId \ + -DarchetypeVersion=$archetypeVersion \ + -DartifactId=$artifactId \ + -DgroupId=$groupId \ + -Dversion=$version \ + -DmainName=$mainName \ + -DinteractiveMode=false \ + -DsourceProject=/path/to/kotlin-example-app +// end::maven-getting-started-bash-003[] + +// tag::maven-getting-started-bash-004[] +# Specify your the version of the codenameone-maven-plugin. +# Find the latest version at +# https://search.maven.org/search?q=a:codenameone-maven-plugin +CN1VERSION={cn1-plugin-release-version} +mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ + -DgroupId=YOUR_GROUP_ID \ + -DartifactId=YOUR_ARTIFACT_ID \ + -DsourceProject=/path/to/your/project \ + -Dcn1Version=$CN1VERSION +// end::maven-getting-started-bash-004[] + +// tag::maven-getting-started-bash-005[] +cd myapp +./run.sh +// end::maven-getting-started-bash-005[] + +// tag::maven-getting-started-bash-006[] +CN1_VERSION={cn1-plugin-release-version} +curl -L https://github.com/codenameone/KitchenSink/archive/v1.0-cn7.0.11.zip > master.zip +unzip master.zip +rm master.zip +mvn com.codenameone:codenameone-maven-plugin:${CN1_VERSION}:generate-app-project \ + -DarchetypeGroupId=com.codename1 \ + -DarchetypeArtifactId=cn1app-archetype \ + -DarchetypeVersion=${CN1_VERSION} \ + -DartifactId=kitchensink \ + -DgroupId=com.example \ + -Dversion=1.0-SNAPSHOT \ + -DinteractiveMode=false \ + -DsourceProject=KitchenSink-1.0-cn7.0.11 +// end::maven-getting-started-bash-006[] + +// tag::maven-getting-started-bash-007[] +mvn cn1:install-cn1lib -Dfile=/path/to/yourlibrary.cn1lib +// end::maven-getting-started-bash-007[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.xml b/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.xml new file mode 100644 index 00000000000..7c0401bf217 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-getting-started.xml @@ -0,0 +1,23 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-getting-started-xml-001[] + + com.codenameone + cn1app-archetype + LATEST + maven-archetype + +// end::maven-getting-started-xml-001[] + +// tag::maven-getting-started-xml-002[] + + com.codenameone + googlemaps-lib + 1.0.1 + pom + +// end::maven-getting-started-xml-002[] + +// tag::maven-getting-started-xml-003[] + +// end::maven-getting-started-xml-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-project-templates.txt b/docs/demos/common/src/main/snippets/developer-guide/maven-project-templates.txt new file mode 100644 index 00000000000..38d15ec40a0 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-project-templates.txt @@ -0,0 +1,16 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-project-templates-rpf-001[] +template.mainName=$YOUR_PROJECT_MAIN_NAME +template.packageName=$YOUR_PROJECT_PACKAGE_NAME + +[dependencies] +==== +... YOUR PROJECT MAVEN DEPENDENCIES ... +==== + +[parentDependencies] +==== +... YOUR PARENT PROJECT MAVEN DEPENDENCIES ... +==== +// end::maven-project-templates-rpf-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.sh b/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.sh new file mode 100644 index 00000000000..1b1cac0701b --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.sh @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-updating-codename-one-bash-001[] +mvn cn:update +// end::maven-updating-codename-one-bash-001[] + +// tag::maven-updating-codename-one-bash-002[] +./run.sh update +// end::maven-updating-codename-one-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.xml b/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.xml new file mode 100644 index 00000000000..06858f20f03 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.xml @@ -0,0 +1,6 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::maven-updating-codename-one-xml-001[] +{cn1-plugin-release-version} +{cn1-release-version} +// end::maven-updating-codename-one-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.properties b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.properties new file mode 100644 index 00000000000..e75c2bb6d98 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.properties @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::miscellaneous-features-properties-001[] +ios.NSCameraUsageDescription=This app needs to use your camera to scan bar codes +// end::miscellaneous-features-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings new file mode 100644 index 00000000000..b86869f912c --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings @@ -0,0 +1,19 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::miscellaneous-features-strings-001[] +"CFBundleDisplayName"="Hello App"; +// end::miscellaneous-features-strings-001[] + +// tag::miscellaneous-features-strings-002[] +"CFBundleDisplayName"="Bonjour App"; +// end::miscellaneous-features-strings-002[] + +// tag::miscellaneous-features-strings-003[] +"CFBundleDisplayName"="Hello App"; +"NSCameraUsageDescription"="This app needs to use your camera to scan bar codes"; +// end::miscellaneous-features-strings-003[] + +// tag::miscellaneous-features-strings-004[] +"CFBundleDisplayName"="Bonjour App"; +"NSCameraUsageDescription"="Cette application doit utiliser votre appareil photo pour scanner les codes à barres"; +// end::miscellaneous-features-strings-004[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.txt b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.txt new file mode 100644 index 00000000000..ea64de510da --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/miscellaneous-features.txt @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::miscellaneous-features-text-001[] +cn1_icon_[_].png +// end::miscellaneous-features-text-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/monetization.json b/docs/demos/common/src/main/snippets/developer-guide/monetization.json new file mode 100644 index 00000000000..3bb44dda2aa --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/monetization.json @@ -0,0 +1,16 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::monetization-json-001[] +{ + "type": "service_account", + "project_id": "iapdemo-152500", + "private_key_id": "1b1d39************7d839826b8a", + "private_key": "-----BEGIN PRIVATE KEY-----... Some private key string -----END PRIVATE KEY-----\n", + "client_email": "iapdemo@iapdemo-152500.iam.gserviceaccount.com", + "client_id": "117601572633333082772", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/iapdemo%40iapdemo-152500.iam.gserviceaccount.com" +} +// end::monetization-json-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/monetization.sql b/docs/demos/common/src/main/snippets/developer-guide/monetization.sql new file mode 100644 index 00000000000..32be7b6d5cc --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/monetization.sql @@ -0,0 +1,17 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::monetization-sql-001[] +create TABLE RECEIPTS +( + TRANSACTION_ID VARCHAR(128) not null, + USERNAME VARCHAR(64) not null, + SKU VARCHAR(128) not null, + ORDER_DATA VARCHAR(32000), + PURCHASE_DATE BIGINT, + EXPIRY_DATE BIGINT, + CANCELLATION_DATE BIGINT, + LAST_VALIDATED BIGINT, + STORE_CODE VARCHAR(20) default '' not null, + primary key (TRANSACTION_ID, STORE_CODE) +) +// end::monetization-sql-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/network-connectivity.xml b/docs/demos/common/src/main/snippets/developer-guide/network-connectivity.xml new file mode 100644 index 00000000000..dc477c99359 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/network-connectivity.xml @@ -0,0 +1,20 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::network-connectivity-xml-001[] + + + org.jmdns + jmdns + 3.5.9 + runtime + +// end::network-connectivity-xml-001[] + +// tag::network-connectivity-xml-002[] + + + + +// end::network-connectivity-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.properties b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.properties new file mode 100644 index 00000000000..406f255e3c1 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.properties @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::on-device-debugging-android-properties-001[] +codename1.arg.android.onDeviceDebug=true +// end::on-device-debugging-android-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh new file mode 100644 index 00000000000..8de07524d62 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh @@ -0,0 +1,33 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::on-device-debugging-android-bash-001[] +adb pair 192.168.1.42:37051 +# When prompted, enter the 6-digit code shown on the device. +// end::on-device-debugging-android-bash-001[] + +// tag::on-device-debugging-android-bash-002[] +adb connect 192.168.1.42:5555 +// end::on-device-debugging-android-bash-002[] + +// tag::on-device-debugging-android-bash-003[] +mvn cn1:android-on-device-debugging \ + -Dcn1.android.onDeviceDebug.wireless=192.168.1.42:5555 +// end::on-device-debugging-android-bash-003[] + +// tag::on-device-debugging-android-bash-004[] +adb tcpip 5555 +adb connect 192.168.1.42:5555 +// end::on-device-debugging-android-bash-004[] + +// tag::on-device-debugging-android-bash-005[] +# Terminal 1 — build the APK once +mvn cn1:buildAndroidOnDeviceDebug + +# Terminal 2 — install, launch, forward JDWP, tail logcat +mvn cn1:android-on-device-debugging +// end::on-device-debugging-android-bash-005[] + +// tag::on-device-debugging-android-bash-006[] +jdb -attach localhost:5005 \ + -sourcepath src/main/java:$HOME/.m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT-sources.jar +// end::on-device-debugging-android-bash-006[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.properties b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.properties new file mode 100644 index 00000000000..726d58c42a3 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.properties @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::on-device-debugging-properties-001[] +codename1.arg.ios.onDeviceDebug=true +codename1.arg.ios.onDeviceDebug.proxyHost=127.0.0.1 +codename1.arg.ios.onDeviceDebug.proxyPort=55333 +# Optional: block the app at startup until the IDE attaches. +codename1.arg.ios.onDeviceDebug.waitForAttach=true +// end::on-device-debugging-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.sh b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.sh new file mode 100644 index 00000000000..f8c0dc7fa68 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/on-device-debugging.sh @@ -0,0 +1,14 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::on-device-debugging-bash-001[] +# Terminal 1 — start the proxy +mvn cn1:ios-on-device-debugging + +# Terminal 2 — attach jdb +jdb -attach localhost:8000 +// end::on-device-debugging-bash-001[] + +// tag::on-device-debugging-bash-002[] +jdb -attach localhost:8000 \ + -sourcepath src/main/java:$HOME/.m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT-sources.jar +// end::on-device-debugging-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/performance.txt b/docs/demos/common/src/main/snippets/developer-guide/performance.txt new file mode 100644 index 00000000000..ea570d64382 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/performance.txt @@ -0,0 +1,6 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::performance-text-001[] +java.lang.IllegalArgumentException: SIMD array argument was not +allocated using Simd.alloc*(). objectId=… +// end::performance-text-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/push-notifications.xml b/docs/demos/common/src/main/snippets/developer-guide/push-notifications.xml new file mode 100644 index 00000000000..d413287bbb9 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/push-notifications.xml @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::push-notifications-xml-001[] + +// end::push-notifications-xml-001[] + +// tag::push-notifications-xml-002[] + +// end::push-notifications-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/skin-designer.properties b/docs/demos/common/src/main/snippets/developer-guide/skin-designer.properties new file mode 100644 index 00000000000..1e3043c482b --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/skin-designer.properties @@ -0,0 +1,38 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::skin-designer-properties-001[] +touch=true +platformName=ios +tablet=false +ppi=460 +pixelRatio=18.110236220472443 + +# roundScreen=true makes the simulator paint skin.png *over* the rendered +# UI rather than clipping the UI to non-frame pixels. That overlay step is +# what makes the Dynamic Island / punch-hole shapes appear "floating" on +# top of the iOS status bar instead of being carved out of the display. +roundScreen=true +displayX=89 +displayY=89 +displayWidth=1206 +displayHeight=2622 + +# Safe area in display-relative coordinates (origin = screen top-left). +safePortraitX=0 +safePortraitY=237 +safePortraitWidth=1206 +safePortraitHeight=2272 +safeLandscapeX=237 +safeLandscapeY=0 +safeLandscapeWidth=2272 +safeLandscapeHeight=1206 + +# These get composed by overrideNames(device) — they let users layer +# device-specific styling on top of the platform theme via +# UIManager.addThemeProps()-style theme inheritance. +overrideNames=phone,ios,iphone + +systemFontFamily=SF Pro +proportionalFontFamily=SF Pro +monospaceFontFamily=SF Mono +// end::skin-designer-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/skin-designer.txt b/docs/demos/common/src/main/snippets/developer-guide/skin-designer.txt new file mode 100644 index 00000000000..466b50d8b6d --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/skin-designer.txt @@ -0,0 +1,11 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::skin-designer-text-001[] +Apple-iPhone-16-Pro.skin/ + skin.png # portrait body (device frame + transparent screen + cutouts) + skin_l.png # 90° rotated portrait + skin_map.png # black rect = screen, white = frame, used for hit-testing + skin_map_l.png # rotated map + iOS7Theme.res # bundled native theme (or android_holo_light.res / winTheme.res) + skin.properties # platform metadata, safe-area, PPI, display rect +// end::skin-designer-text-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.sh b/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.sh new file mode 100644 index 00000000000..4a7f1bf1287 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::svg-transcoder-bash-001[] +mvn package +// end::svg-transcoder-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.xml b/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.xml new file mode 100644 index 00000000000..14a20c4e8d8 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/svg-transcoder.xml @@ -0,0 +1,11 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::svg-transcoder-xml-001[] + + transcode-svg + generate-sources + + transcode-svg + + +// end::svg-transcoder-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.sh b/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.sh new file mode 100644 index 00000000000..cfa8d1a8f45 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.sh @@ -0,0 +1,14 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::testing-with-junit-bash-001[] +mvn -pl javase test # all JUnit + cn1:test +mvn -pl javase test -Dtest=GreetingFormTest # one class +mvn -pl javase test -Dtest=GreetingFormTest#formShowsExpectedTitle # one method +// end::testing-with-junit-bash-001[] + +// tag::testing-with-junit-bash-002[] +mvn -pl javase test # both runners +mvn -pl javase test -DskipTests # skip Surefire, cn1:test still runs +mvn -pl javase test -Dtest=NoMatch # filter Surefire to nothing, + # cn1:test still runs +// end::testing-with-junit-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.xml b/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.xml new file mode 100644 index 00000000000..6544b42cff5 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/testing-with-junit.xml @@ -0,0 +1,26 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::testing-with-junit-xml-001[] + + + com.codenameone + codenameone-javase + test + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + +// end::testing-with-junit-xml-001[] + +// tag::testing-with-junit-xml-002[] + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + +// end::testing-with-junit-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/the-components-of-codename-one.sh b/docs/demos/common/src/main/snippets/developer-guide/the-components-of-codename-one.sh new file mode 100644 index 00000000000..29ce4eae190 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/the-components-of-codename-one.sh @@ -0,0 +1,8 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::the-components-of-codename-one-bash-001[] +#!/bin/bash +export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 +export LD_LIBRARY_PATH=$JAVA_HOME/lib:$JAVA_HOME/lib/server${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} +exec /path/to/your/ide "$@" +// end::the-components-of-codename-one-bash-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/tvplatforms.properties b/docs/demos/common/src/main/snippets/developer-guide/tvplatforms.properties new file mode 100644 index 00000000000..fb976b8a1a7 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/tvplatforms.properties @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::tvplatforms-properties-001[] +codename1.arg.android.tv=true +// end::tvplatforms-properties-001[] + +// tag::tvplatforms-properties-002[] +codename1.arg.tvNative.enabled=true +// end::tvplatforms-properties-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/wearables.properties b/docs/demos/common/src/main/snippets/developer-guide/wearables.properties new file mode 100644 index 00000000000..1a1cd0d41e4 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/wearables.properties @@ -0,0 +1,13 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::wearables-properties-001[] +watchNative.enabled=true +// end::wearables-properties-001[] + +// tag::wearables-properties-002[] +codename1.watchMain=com.mycompany.myapp.MyWatchMain +// end::wearables-properties-002[] + +// tag::wearables-properties-003[] +android.wear=true +// end::wearables-properties-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/wearables.xml b/docs/demos/common/src/main/snippets/developer-guide/wearables.xml new file mode 100644 index 00000000000..6ab30a0cc7f --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/wearables.xml @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::wearables-xml-001[] + +// end::wearables-xml-001[] + +// tag::wearables-xml-002[] + +// end::wearables-xml-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh b/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh new file mode 100644 index 00000000000..8d726e8f99f --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh @@ -0,0 +1,21 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-codenameone-sources-bash-001[] +$ git clone https://github.com/codenameone/CodenameOne +$ cd CodenameOne/maven +$ mvn install +// end::working-with-codenameone-sources-bash-001[] + +// tag::working-with-codenameone-sources-bash-002[] +$ git clone https://github.com/shannah/cn1-maven-archetypes +$ cd cn1-maven-archetypes +$ mvn install +// end::working-with-codenameone-sources-bash-002[] + +// tag::working-with-codenameone-sources-bash-003[] +$ mvn -pl core-unittests test +// end::working-with-codenameone-sources-bash-003[] + +// tag::working-with-codenameone-sources-bash-004[] +$ mvn -pl tests -am verify +// end::working-with-codenameone-sources-bash-004[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.xml b/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.xml new file mode 100644 index 00000000000..f2408e883f4 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.xml @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-codenameone-sources-xml-001[] + + + {cn1-snapshot-version} + {cn1-snapshot-version} + +// end::working-with-codenameone-sources-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-ios.xml b/docs/demos/common/src/main/snippets/developer-guide/working-with-ios.xml new file mode 100644 index 00000000000..7e29aefe4ec --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-ios.xml @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-ios-xml-001[] +NSAppTransportSecurityNSAllowsArbitraryLoads +// end::working-with-ios-xml-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-linux.sh b/docs/demos/common/src/main/snippets/developer-guide/working-with-linux.sh new file mode 100644 index 00000000000..6017fa5d534 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-linux.sh @@ -0,0 +1,15 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-linux-bash-001[] +mvn -pl common package -Dcodename1.platform=linux -Dcodename1.buildTarget=local-linux-device cn1:build +// end::working-with-linux-bash-001[] + +// tag::working-with-linux-bash-002[] +sudo apt-get install -y \ + cmake ninja-build pkg-config \ + libgtk-3-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libglib2.0-dev \ + libfontconfig1-dev libfreetype-dev libcurl4-openssl-dev \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + libwebkit2gtk-4.1-dev libsecret-1-dev libnotify-dev libgeoclue-2-dev \ + libepoxy-dev libegl1-mesa-dev libgles2-mesa-dev +// end::working-with-linux-bash-002[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh b/docs/demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh new file mode 100644 index 00000000000..1025e214573 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh @@ -0,0 +1,16 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-mac-os-x-shell-001[] +mvn -B -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-source package +// end::working-with-mac-os-x-shell-001[] + +// tag::working-with-mac-os-x-shell-002[] +mvn -B -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-os-x-native package +// end::working-with-mac-os-x-shell-002[] + +// tag::working-with-mac-os-x-shell-003[] +xcodebuild -exportArchive \ + -archivePath build/.xcarchive \ + -exportOptionsPlist dist/ExportOptions-AppStore-Mac.plist \ + -exportPath build/export +// end::working-with-mac-os-x-shell-003[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.properties b/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.properties new file mode 100644 index 00000000000..feb2ade11e4 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.properties @@ -0,0 +1,12 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-windows-properties-001[] +# codenameone_settings.properties +codename1.windows.signing.certificate=/secure/path/codesign.p12 +codename1.windows.signing.password=your-pkcs12-password +# optional signature metadata + timestamping (shown with their defaults): +codename1.arg.windows.signing.name=Acme Corp +codename1.arg.windows.signing.url=https://acme.example +codename1.arg.windows.signing.timestampUrl=http://timestamp.digicert.com +codename1.arg.windows.signing.digest=sha256 +// end::working-with-windows-properties-001[] diff --git a/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.sh b/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.sh new file mode 100644 index 00000000000..f1beb009283 --- /dev/null +++ b/docs/demos/common/src/main/snippets/developer-guide/working-with-windows.sh @@ -0,0 +1,5 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-windows-bash-001[] +mvn -pl common package -Dcodename1.platform=windows -Dcodename1.buildTarget=windows-device +// end::working-with-windows-bash-001[] diff --git a/docs/demos/common/src/test/java/com/codenameone/developerguide/DemoRegistryTest.java b/docs/demos/common/src/test/java/com/codenameone/developerguide/DemoRegistryTest.java index 42c0398ff97..ee38f388e7f 100644 --- a/docs/demos/common/src/test/java/com/codenameone/developerguide/DemoRegistryTest.java +++ b/docs/demos/common/src/test/java/com/codenameone/developerguide/DemoRegistryTest.java @@ -30,6 +30,16 @@ public boolean runTest() throws Exception { assertTrue(titles.contains("Slide Transitions"), "Slide Transitions demo should be registered."); assertTrue(titles.contains("Bubble Transition"), "Bubble Transition demo should be registered."); + assertFalse(DemoRegistry.getScreenshots().isEmpty(), "Screenshot registry should contain guide screenshots."); + for (GuideScreenshot screenshot : DemoRegistry.getScreenshots()) { + assertNotNull(screenshot.getId(), "Screenshot IDs must not be null."); + assertFalse(screenshot.getId().trim().isEmpty(), "Screenshot IDs must not be empty."); + assertNotNull(screenshot.getDemo(), "Screenshot demos must not be null."); + assertTrue(demos.contains(screenshot.getDemo()), "Screenshot demos must be registered demos."); + assertNotNull(screenshot.getFileName(), "Screenshot filenames must not be null."); + assertFalse(screenshot.getFileName().trim().isEmpty(), "Screenshot filenames must not be empty."); + } + boolean immutable = false; try { demos.add(new Demo() { diff --git a/docs/demos/common/src/test/java/com/codenameone/developerguide/animations/AnimationDemosScreenshotTest.java b/docs/demos/common/src/test/java/com/codenameone/developerguide/animations/AnimationDemosScreenshotTest.java index 0bda882b981..4b1f8244c09 100644 --- a/docs/demos/common/src/test/java/com/codenameone/developerguide/animations/AnimationDemosScreenshotTest.java +++ b/docs/demos/common/src/test/java/com/codenameone/developerguide/animations/AnimationDemosScreenshotTest.java @@ -4,31 +4,48 @@ import com.codename1.io.Util; import com.codename1.testing.AbstractTest; import com.codename1.testing.TestUtils; +import com.codename1.ui.Button; +import com.codename1.ui.CheckBox; +import com.codename1.ui.Component; +import com.codename1.ui.Container; import com.codename1.ui.Display; import com.codename1.ui.Form; +import com.codename1.ui.Graphics; import com.codename1.ui.Image; +import com.codename1.ui.TextField; +import com.codename1.ui.spinner.Picker; +import com.codename1.ui.plaf.Style; import com.codename1.ui.util.ImageIO; -import com.codenameone.developerguide.Demo; import com.codenameone.developerguide.DemoRegistry; +import com.codenameone.developerguide.GuideScreenshot; import java.io.IOException; import java.io.OutputStream; -import java.util.HashMap; +import java.util.ArrayList; import java.util.HashSet; -import java.util.Map; +import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; /** - * Captures screenshots for all animation demos and persists them using Storage so + * Captures screenshots for all registered guide demos and persists them using Storage so * that external tooling can compare them against the developer guide imagery. */ public class AnimationDemosScreenshotTest extends AbstractTest { private static final String HOST_TITLE = "Demo Test Host"; private static final long FORM_TIMEOUT_MS = 10000L; - private static final String STORAGE_PREFIX = "developer-guide.animations."; + private static final String STORAGE_PREFIX = "developer-guide.screenshots."; + private static final int TRANSITION_FRAME_COUNT = 6; + private static final int TRANSITION_FRAME_DELAY_MS = 250; + private static final int TRANSITION_DURATION_MS = 1500; + private static final int LAYOUT_FRAME_COUNT = 7; + private static final int LAYOUT_LABEL_COUNT = 10; + private static final int MAX_STATIC_SCREENSHOT_WIDTH = 720; + private static final int MAX_STRIP_FRAME_WIDTH = 360; + private static final int BACKGROUND_COLOR_TOLERANCE = 16; + private static final int STATIC_TRIM_BOTTOM_MARGIN = 56; - private static final Map SCREENSHOT_NAME_OVERRIDES = createScreenshotNameOverrides(); - private static final Set OVERRIDE_FILE_NAMES = new HashSet<>(SCREENSHOT_NAME_OVERRIDES.values()); + private static final Set SCREENSHOT_FILE_NAMES = createScreenshotFileNames(); private final Storage storage = Storage.getInstance(); @@ -36,24 +53,431 @@ public class AnimationDemosScreenshotTest extends AbstractTest { public boolean runTest() throws Exception { clearPreviousScreenshots(); - Form host = new Form(HOST_TITLE); - host.show(); - TestUtils.waitForFormTitle(HOST_TITLE, FORM_TIMEOUT_MS); + Form host = runOnEdt(() -> { + Form form = new Form(HOST_TITLE); + form.show(); + return form; + }); + waitForCurrentForm(host); - for (Demo demo : DemoRegistry.getDemos()) { - Form previous = Display.getInstance().getCurrent(); - demo.show(host); - Form demoForm = waitForFormChange(previous); - waitForFormReady(demoForm); + captureLayoutAnimationFrames(host); + captureSlideTransitionStrips(host); + captureBubbleTransitionStrip(host); + captureMorphTransitionStrip(host); + captureStaticGuideScreenshots(host); - Image screenshot = capture(demoForm); - saveScreenshot(storageKeyFor(demo.getTitle()), screenshot); + return true; + } - host.show(); - waitForHost(host); + private void captureStaticGuideScreenshots(Form host) throws IOException { + for (GuideScreenshot screenshot : DemoRegistry.getScreenshots()) { + if (isAnimationScreenshot(screenshot.getFileName())) { + continue; + } + showDemoWithoutParent(screenshot); + TestUtils.waitFor(100); + saveScreenshot(screenshot.getFileName(), + prepareStaticScreenshot(screenshot.getFileName(), captureStaticScreenshot(screenshot.getFileName()))); + returnToHost(host); } + } - return true; + private boolean isAnimationScreenshot(String fileName) { + return fileName.startsWith("layout-animation-") + || fileName.startsWith("transition-") + || "mighty-morphing-components-1.png".equals(fileName); + } + + private void captureLayoutAnimationFrames(Form host) throws IOException { + GuideScreenshot firstFrame = screenshot("layout-animation-1.png"); + showDemo(firstFrame, host); + Image baseFrame = cropLayoutAnimationFrame(captureDisplay()); + + for (int frame = 1; frame <= LAYOUT_FRAME_COUNT; frame++) { + saveScreenshot("layout-animation-" + frame + ".png", renderLayoutAnimationFrame(frame - 1, baseFrame)); + } + + returnToHost(host); + } + + private void captureSlideTransitionStrips(Form host) throws IOException { + captureSlideTransitionStrip(host, "transition-slide.png", "Slide", true); + captureSlideTransitionStrip(host, "transition-slide-vertical.png", "Slide", false); + captureSlideTransitionStrip(host, "transition-slide-fade.png", "SlideFade", true); + captureSlideTransitionStrip(host, "transition-cover.png", "Cover", true); + captureSlideTransitionStrip(host, "transition-uncover.png", "Uncover", true); + captureSlideTransitionStrip(host, "transition-fade.png", "Fade", true); + captureSlideTransitionStrip(host, "transition-flip.png", "Flip", true); + } + + private void captureSlideTransitionStrip(Form host, String fileName, String transitionName, boolean horizontal) + throws IOException { + GuideScreenshot screenshot = screenshot(fileName); + Form demoForm = showDemo(screenshot, host); + configureSlideDemo(demoForm, transitionName, horizontal); + TestUtils.waitFor(100); + Image sourceFrame = cropTransitionFrame(fileName, captureDisplay()); + Image destinationFrame = cropTransitionFrame(fileName, captureTransitionDestinationFrame()); + saveScreenshot(fileName, + renderDeterministicTransitionStrip(transitionName, horizontal, sourceFrame, destinationFrame)); + returnToHost(host); + } + + private void captureBubbleTransitionStrip(Form host) throws IOException { + GuideScreenshot screenshot = screenshot("transition-bubble.png"); + showDemo(screenshot, host); + Image sourceFrame = cropBubbleFrame(captureDisplay()); + saveScreenshot(screenshot.getFileName(), renderBubbleTransitionStrip(sourceFrame)); + returnToHost(host); + } + + private void captureMorphTransitionStrip(Form host) throws IOException { + GuideScreenshot screenshot = screenshot("mighty-morphing-components-1.png"); + showDemo(screenshot, host); + TestUtils.waitFor(50); + saveScreenshot(screenshot.getFileName(), renderMorphTransitionStrip(cropMorphFrame(captureDisplay()))); + returnToHost(host); + } + + private void configureSlideDemo(Form demoForm, String transitionName, boolean horizontal) { + runOnEdt(() -> { + Picker picker = componentByType(demoForm, Picker.class); + TextField duration = componentByType(demoForm, TextField.class); + CheckBox horizontalToggle = componentByType(demoForm, CheckBox.class); + picker.setSelectedString(transitionName); + duration.setText(String.valueOf(TRANSITION_DURATION_MS)); + horizontalToggle.setSelected(horizontal); + }); + } + + private Image renderLayoutAnimationFrame(int frameIndex, Image baseFrame) { + Image frame = Image.createImage(baseFrame.getWidth(), baseFrame.getHeight(), 0xffffffff); + Graphics graphics = frame.getGraphics(); + graphics.drawImage(baseFrame, 0, 0); + graphics.setColor(0x202020); + + double progress = (frameIndex + 1) / (double) LAYOUT_FRAME_COUNT; + int lineHeight = Math.max(42, baseFrame.getHeight() / 36); + int firstEndY = Math.max(lineHeight * 4, baseFrame.getHeight() / 5); + int x = Math.max(20, baseFrame.getWidth() / 28); + for (int iter = 0; iter < LAYOUT_LABEL_COUNT; iter++) { + int endY = firstEndY + (iter * lineHeight); + int y = endY - (int) Math.round((1.0 - progress) * lineHeight * 3); + graphics.drawString("Label " + iter, x, y); + } + return frame; + } + + private Image renderBubbleTransitionStrip(Image source) { + List frames = new ArrayList<>(); + for (int frame = 0; frame < TRANSITION_FRAME_COUNT; frame++) { + double progress = frame / (double) (TRANSITION_FRAME_COUNT - 1); + Image image = Image.createImage(source.getWidth(), source.getHeight(), 0xffffffff); + Graphics graphics = image.getGraphics(); + graphics.drawImage(source, 0, 0); + int centerX = Math.max(24, source.getWidth() / 12); + int centerY = Math.max(80, source.getHeight() / 3); + int radius = (int) Math.round(Math.sqrt(source.getWidth() * source.getWidth() + + source.getHeight() * source.getHeight()) * progress); + graphics.setAntiAliased(true); + graphics.setColor(0x006dff); + if (radius > 0) { + graphics.fillArc(centerX - radius, centerY - radius, radius * 2, radius * 2, 0, 360); + } + if (progress > 0.45) { + drawBubbleDialog(graphics, source.getWidth(), source.getHeight(), progress); + } + frames.add(image); + } + return createFrameStrip(frames); + } + + private void drawBubbleDialog(Graphics graphics, int width, int height, double progress) { + int dialogWidth = width * 4 / 5; + int dialogHeight = height / 2; + int dialogX = (width - dialogWidth) / 2; + int dialogY = Math.max(10, height / 8); + int previousAlpha = graphics.getAlpha(); + graphics.setAlpha((int) Math.round(255 * Math.min(1.0, (progress - 0.45) / 0.55))); + graphics.setColor(0x006dff); + graphics.fillRoundRect(dialogX, dialogY, dialogWidth, dialogHeight, 16, 16); + graphics.setColor(0xffffff); + graphics.drawString("Bubbled", dialogX + 24, dialogY + 24); + graphics.drawString("Dialog Body", dialogX + 24, dialogY + 64); + graphics.setAlpha(previousAlpha); + } + + private Image renderMorphTransitionStrip(Image source) { + List frames = new ArrayList<>(); + for (int frame = 0; frame < TRANSITION_FRAME_COUNT; frame++) { + double progress = frame / (double) (TRANSITION_FRAME_COUNT - 1); + Image image = Image.createImage(source.getWidth(), source.getHeight(), 0xffffffff); + Graphics graphics = image.getGraphics(); + graphics.drawImage(source, 0, 0); + drawMorphOverlay(graphics, source.getWidth(), source.getHeight(), progress); + frames.add(image); + } + return createFrameStrip(frames); + } + + private void drawMorphOverlay(Graphics graphics, int width, int height, double progress) { + int startSize = Math.max(24, width / 12); + int endWidth = Math.max(120, width / 2); + int endHeight = Math.max(56, height / 4); + int cardWidth = startSize + (int) Math.round((endWidth - startSize) * progress); + int cardHeight = startSize + (int) Math.round((endHeight - startSize) * progress); + int startX = Math.max(12, width / 16); + int startY = Math.max(112, height / 3); + int endX = (width - endWidth) / 2; + int endY = Math.max(80, height / 3); + int x = startX + (int) Math.round((endX - startX) * progress); + int y = startY + (int) Math.round((endY - startY) * progress); + + graphics.setAntiAliased(true); + graphics.setColor(0x1e88e5); + graphics.fillRoundRect(x, y, cardWidth, cardHeight, 12, 12); + graphics.setColor(0xffffff); + graphics.drawString("Demo", x + 12, y + Math.min(cardHeight - 18, 28)); + } + + private Image renderDeterministicTransitionStrip( + String transitionName, + boolean horizontal, + Image source, + Image destination) { + List frames = new ArrayList<>(); + for (int frame = 0; frame < TRANSITION_FRAME_COUNT; frame++) { + double progress = frame / (double) (TRANSITION_FRAME_COUNT - 1); + frames.add(renderTransitionFrame(transitionName, horizontal, source, destination, progress)); + } + return createFrameStrip(frames); + } + + private Form createTransitionDestinationForm() { + Form destination = new Form("Destination"); + Style bg = destination.getContentPane().getUnselectedStyle(); + bg.setBgTransparency(255); + bg.setBgColor(0xff); + destination.getToolbar().addCommandToLeftBar("Back", null, ignored -> { + }); + return destination; + } + + private Image captureTransitionDestinationFrame() { + Form previous = currentForm(); + runOnEdt(() -> createTransitionDestinationForm().show()); + Form destination = waitForFormChange(previous); + waitForFormReady(destination); + TestUtils.waitFor(300); + return captureDisplay(); + } + + private Image renderTransitionFrame( + String transitionName, + boolean horizontal, + Image source, + Image destination, + double progress) { + int width = source.getWidth(); + int height = source.getHeight(); + Image frame = Image.createImage(width, height, 0xffffffff); + Graphics graphics = frame.getGraphics(); + int travel = horizontal ? width : height; + int offset = (int) Math.round(progress * travel); + + switch (transitionName) { + case "Slide": + drawOffset(graphics, source, horizontal, -offset); + drawOffset(graphics, destination, horizontal, travel - offset); + break; + case "SlideFade": + drawOffset(graphics, source, horizontal, -offset); + drawImageWithAlpha(graphics, destination, horizontal ? travel - offset : 0, + horizontal ? 0 : travel - offset, (int) Math.round(progress * 255)); + break; + case "Cover": + graphics.drawImage(source, 0, 0); + drawOffset(graphics, destination, horizontal, travel - offset); + break; + case "Uncover": + graphics.drawImage(destination, 0, 0); + drawOffset(graphics, source, horizontal, -offset); + break; + case "Fade": + drawImageWithAlpha(graphics, source, 0, 0, (int) Math.round((1.0 - progress) * 255)); + drawImageWithAlpha(graphics, destination, 0, 0, (int) Math.round(progress * 255)); + break; + case "Flip": + renderFlipFrame(graphics, source, destination, progress); + break; + default: + throw new IllegalArgumentException("Unsupported transition: " + transitionName); + } + return frame; + } + + private Image prepareStaticScreenshot(String fileName, Image screenshot) { + Image prepared = screenshot; + if ("badge-floating-button.png".equals(fileName) + || "components-slider.png".equals(fileName) + || "components-floatinghint.png".equals(fileName) + || "graphics-glasspane.png".equals(fileName) + || "graphics-fontimage-fixed.png".equals(fileName) + || "graphics-fontimage-style.png".equals(fileName) + || "graphics-fontimage-material.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0, 1, 0.42); + } else if ("floating-action.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0, 1, 0.55); + } else if ("graphics-hiworld.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0, 1, 0.5); + } else if ("shaped-clipping.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0.18, 0.14, 0.64, 0.42); + } else if ("components-dialog-modal-south.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0.35, 1, 0.65); + } else if ("components-dialog-modal-bottom-half.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0, 1, 0.55); + } else if ("components-interaction-dialog.png".equals(fileName)) { + prepared = cropByRatio(screenshot, 0, 0, 1, 0.6); + } else if (shouldTrimBlankBottom(fileName)) { + prepared = trimBlankBottom(prepared, STATIC_TRIM_BOTTOM_MARGIN); + } + return scaleToMaxWidth(prepared, MAX_STATIC_SCREENSHOT_WIDTH); + } + + private boolean shouldTrimBlankBottom(String fileName) { + return fileName.startsWith("flow-layout") + || fileName.startsWith("box-layout") + || "components-button.png".equals(fileName) + || "components-link-button.png".equals(fileName) + || "raised-flat-buttons.png".equals(fileName) + || "components-radiobutton-checkbox.png".equals(fileName) + || "components-componentgroup.png".equals(fileName) + || "components-multibutton.png".equals(fileName) + || "components-spanbutton.png".equals(fileName) + || "components-spanlabel.png".equals(fileName) + || "components-onoffswitch.png".equals(fileName) + || "components-picker.png".equals(fileName) + || "components-accordion.png".equals(fileName); + } + + private Image trimBlankBottom(Image image, int margin) { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixels = image.getRGB(); + int minimumDifferenceCount = Math.max(2, width / 120); + + for (int y = height - 1; y >= 0; y--) { + int differenceCount = 0; + int row = y * width; + int background = pixels[row] & 0x00ffffff; + for (int x = 0; x < width; x++) { + if (!isSimilarColor(background, pixels[row + x] & 0x00ffffff)) { + differenceCount++; + if (differenceCount >= minimumDifferenceCount) { + int cropHeight = Math.min(height, y + margin + 1); + return image.subImage(0, 0, width, cropHeight, true); + } + } + } + } + return image; + } + + private boolean isSimilarColor(int first, int second) { + int redDifference = Math.abs(((first >> 16) & 0xff) - ((second >> 16) & 0xff)); + int greenDifference = Math.abs(((first >> 8) & 0xff) - ((second >> 8) & 0xff)); + int blueDifference = Math.abs((first & 0xff) - (second & 0xff)); + return redDifference <= BACKGROUND_COLOR_TOLERANCE + && greenDifference <= BACKGROUND_COLOR_TOLERANCE + && blueDifference <= BACKGROUND_COLOR_TOLERANCE; + } + + private Image cropLayoutAnimationFrame(Image frame) { + return scaleToMaxWidth(cropByRatio(frame, 0, 0, 1, 0.5), MAX_STATIC_SCREENSHOT_WIDTH); + } + + private Image cropTransitionFrame(String fileName, Image frame) { + if ("transition-bubble.png".equals(fileName)) { + return cropBubbleFrame(frame); + } + return scaleToMaxWidth(cropByRatio(frame, 0, 0, 1, 0.58), MAX_STRIP_FRAME_WIDTH); + } + + private Image cropBubbleFrame(Image frame) { + return scaleToMaxWidth(cropByRatio(frame, 0, 0, 1, 0.58), MAX_STRIP_FRAME_WIDTH); + } + + private Image cropMorphFrame(Image frame) { + return scaleToMaxWidth(cropByRatio(frame, 0, 0, 1, 0.48), MAX_STRIP_FRAME_WIDTH); + } + + private Image cropByRatio(Image image, double xRatio, double yRatio, double widthRatio, double heightRatio) { + int x = Math.max(0, Math.min(image.getWidth() - 1, (int) Math.round(image.getWidth() * xRatio))); + int y = Math.max(0, Math.min(image.getHeight() - 1, (int) Math.round(image.getHeight() * yRatio))); + int width = Math.max(1, (int) Math.round(image.getWidth() * widthRatio)); + int height = Math.max(1, (int) Math.round(image.getHeight() * heightRatio)); + width = Math.min(width, image.getWidth() - x); + height = Math.min(height, image.getHeight() - y); + return image.subImage(x, y, width, height, true); + } + + private Image scaleToMaxWidth(Image image, int maxWidth) { + if (image.getWidth() <= maxWidth) { + return image; + } + int height = Math.max(1, (int) Math.round(image.getHeight() * (maxWidth / (double) image.getWidth()))); + return image.scaled(maxWidth, height); + } + + private void renderFlipFrame(Graphics graphics, Image source, Image destination, double progress) { + int width = source.getWidth(); + int height = source.getHeight(); + boolean firstHalf = progress < 0.5; + double localProgress = firstHalf ? progress * 2.0 : (progress - 0.5) * 2.0; + int visibleWidth = Math.max(1, (int) Math.round(width * (firstHalf ? 1.0 - localProgress : localProgress))); + int x = (width - visibleWidth) / 2; + graphics.drawImage(firstHalf ? source : destination, x, 0, visibleWidth, height); + } + + private void drawOffset(Graphics graphics, Image image, boolean horizontal, int offset) { + graphics.drawImage(image, horizontal ? offset : 0, horizontal ? 0 : offset); + } + + private void drawImageWithAlpha(Graphics graphics, Image image, int x, int y, int alpha) { + int previousAlpha = graphics.getAlpha(); + graphics.setAlpha(Math.max(0, Math.min(255, alpha))); + graphics.drawImage(image, x, y); + graphics.setAlpha(previousAlpha); + } + + private Form showDemo(GuideScreenshot screenshotDemo, Form host) { + Form previous = currentForm(); + runOnEdt(() -> screenshotDemo.getDemo().show(host)); + Form demoForm = waitForFormChange(previous); + waitForFormReady(demoForm); + return demoForm; + } + + private Form showDemoWithoutParent(GuideScreenshot screenshotDemo) { + Form previous = currentForm(); + runOnEdt(() -> screenshotDemo.getDemo().show(null)); + Form demoForm = waitForFormChange(previous); + waitForFormReady(demoForm); + return demoForm; + } + + private void returnToHost(Form host) { + runOnEdt(host::show); + waitForHost(host); + } + + private GuideScreenshot screenshot(String fileName) { + for (GuideScreenshot screenshot : DemoRegistry.getScreenshots()) { + if (fileName.equals(screenshot.getFileName())) { + return screenshot; + } + } + throw new IllegalStateException("No guide screenshot registered for: " + fileName); } private void clearPreviousScreenshots() { @@ -65,18 +489,18 @@ private void clearPreviousScreenshots() { if (entry == null) { continue; } - if (entry.startsWith(STORAGE_PREFIX) || OVERRIDE_FILE_NAMES.contains(entry)) { + if (entry.startsWith(STORAGE_PREFIX) || SCREENSHOT_FILE_NAMES.contains(entry)) { storage.deleteStorageFile(entry); } } } - private String storageKeyFor(String title) { - String override = SCREENSHOT_NAME_OVERRIDES.get(title); - if (override != null && override.length() > 0) { - return override; + private String storageKeyFor(GuideScreenshot screenshotDemo) { + String fileName = screenshotDemo.getFileName(); + if (fileName != null && fileName.length() > 0) { + return fileName; } - return STORAGE_PREFIX + sanitizeFileName(title) + ".png"; + return STORAGE_PREFIX + screenshotDemo.getId() + ".png"; } private void saveScreenshot(String storageKey, Image screenshot) throws IOException { @@ -88,68 +512,208 @@ private void saveScreenshot(String storageKey, Image screenshot) throws IOExcept OutputStream out = null; try { out = storage.createOutputStream(storageKey); - io.save(screenshot, out, ImageIO.FORMAT_PNG, 1); + io.save(screenshot, out, imageFormat(storageKey), 1); } finally { Util.cleanup(out); } } - private Image capture(Form form) { - Image screenshot = Image.createImage(form.getWidth(), form.getHeight()); - form.paintComponent(screenshot.getGraphics(), true); - return screenshot; + private String imageFormat(String storageKey) { + if (storageKey.endsWith(".jpg") || storageKey.endsWith(".jpeg")) { + return ImageIO.FORMAT_JPEG; + } + return ImageIO.FORMAT_PNG; + } + + private Image captureDisplay() { + return runOnEdt(() -> { + Image screenshot = Display.getInstance().captureScreen(); + if (screenshot != null) { + return screenshot; + } + return captureCurrentFormPaint(); + }); + } + + private Image captureStaticScreenshot(String fileName) { + if ("graphics-glasspane.png".equals(fileName)) { + return runOnEdt(this::captureCurrentFormPaint); + } + return captureDisplay(); + } + + private Image captureCurrentFormPaint() { + Form form = Display.getInstance().getCurrent(); + Image fallback = Image.createImage(form.getWidth(), form.getHeight()); + form.paintComponent(fallback.getGraphics(), true); + return fallback; + } + + private Image captureFrameStrip(int frameCount, int frameDelayMs, ImageProcessor frameProcessor) { + List frames = new ArrayList<>(); + for (int frame = 0; frame < frameCount; frame++) { + frames.add(frameProcessor.process(captureDisplay())); + if (frame < frameCount - 1) { + TestUtils.waitFor(frameDelayMs); + } + } + return createFrameStrip(frames); + } + + private Image createFrameStrip(List frames) { + int width = 0; + int height = 0; + for (Image frame : frames) { + width += frame.getWidth(); + height = Math.max(height, frame.getHeight()); + } + Image strip = Image.createImage(width, height, 0xffffffff); + Graphics graphics = strip.getGraphics(); + int x = 0; + for (Image frame : frames) { + graphics.drawImage(frame, x, 0); + x += frame.getWidth(); + } + return strip; } private Form waitForFormChange(Form previous) { long deadline = System.currentTimeMillis() + FORM_TIMEOUT_MS; - while (Display.getInstance().getCurrent() == previous) { + while (currentForm() == previous) { TestUtils.waitFor(50); if (System.currentTimeMillis() > deadline) { fail("Timed out waiting for demo form to appear."); break; } } - return Display.getInstance().getCurrent(); + return currentForm(); } private void waitForFormReady(Form form) { long deadline = System.currentTimeMillis() + FORM_TIMEOUT_MS; - while ((form.getWidth() <= 0 || form.getHeight() <= 0) && System.currentTimeMillis() <= deadline) { + while ((!hasSize(form)) && System.currentTimeMillis() <= deadline) { TestUtils.waitFor(50); } assertTrue(form.getWidth() > 0, "Demo form width should be > 0 for screenshot capture."); assertTrue(form.getHeight() > 0, "Demo form height should be > 0 for screenshot capture."); - TestUtils.waitFor(200); + TestUtils.waitFor(100); } private void waitForHost(Form host) { + waitForCurrentForm(host); + TestUtils.waitFor(50); + } + + private void waitForCurrentForm(Form expected) { long deadline = System.currentTimeMillis() + FORM_TIMEOUT_MS; - while (Display.getInstance().getCurrent() != host) { + while (currentForm() != expected) { TestUtils.waitFor(50); if (System.currentTimeMillis() > deadline) { - fail("Timed out waiting to return to host form."); + fail("Timed out waiting for expected form: " + expected.getTitle()); break; } } - TestUtils.waitFor(200); } - private static Map createScreenshotNameOverrides() { - Map map = new HashMap<>(); - map.put("Layout Animations", "layout-animation-1.png"); - map.put("Slide Transitions", "transition-slide.png"); - map.put("Bubble Transition", "transition-bubble.png"); - map.put("Morph Transition", "mighty-morphing-components-1.png"); - return map; + private Form currentForm() { + return runOnEdt(() -> Display.getInstance().getCurrent()); + } + + private Button buttonByText(Form form, String text) { + Button button = findComponent(form, Button.class, component -> text.equals(((Button) component).getText())); + assertNotNull(button, "Expected button not found: " + text); + return button; + } + + private T componentByType(Form form, Class type) { + T component = findComponent(form, type, ignored -> true); + assertNotNull(component, "Expected component not found: " + type.getName()); + return component; } - private String sanitizeFileName(String value) { - String sanitized = value.toLowerCase().replaceAll("[^a-z0-9]+", "-").replaceAll("(^-|-$)", ""); - return sanitized.isEmpty() ? "demo-screenshot" : sanitized; + private T findComponent(Component root, Class type, ComponentMatcher matcher) { + if (type.isInstance(root) && matcher.matches(root)) { + return type.cast(root); + } + if (root instanceof Container) { + Container container = (Container) root; + for (int i = 0; i < container.getComponentCount(); i++) { + T found = findComponent(container.getComponentAt(i), type, matcher); + if (found != null) { + return found; + } + } + } + return null; + } + + private boolean hasSize(Form form) { + Boolean hasSize = runOnEdt(() -> form.getWidth() > 0 && form.getHeight() > 0); + return hasSize.booleanValue(); + } + + private T runOnEdt(UiSupplier supplier) { + AtomicReference result = new AtomicReference<>(); + AtomicReference failure = new AtomicReference<>(); + Display.getInstance().callSeriallyAndWait(() -> { + try { + result.set(supplier.get()); + } catch (Throwable ex) { + failure.set(ex); + } + }); + if (failure.get() != null) { + Throwable thrown = failure.get(); + if (thrown instanceof RuntimeException) { + throw (RuntimeException) thrown; + } + if (thrown instanceof Error) { + throw (Error) thrown; + } + throw new RuntimeException(thrown); + } + return result.get(); + } + + private void runOnEdt(Runnable runnable) { + runOnEdt(() -> { + runnable.run(); + return null; + }); + } + + private void runOnEdtAsync(Runnable runnable) { + Display.getInstance().callSerially(runnable); + } + + private static Set createScreenshotFileNames() { + Set names = new HashSet<>(); + for (GuideScreenshot screenshot : DemoRegistry.getScreenshots()) { + names.add(screenshot.getFileName()); + } + return names; } @Override public boolean shouldExecuteOnEDT() { - return true; + return false; + } + + @Override + public int getTimeoutMillis() { + return 600000; } + + private interface UiSupplier { + T get(); + } + + private interface ComponentMatcher { + boolean matches(Component component); + } + + private interface ImageProcessor { + Image process(Image image); + } + } diff --git a/docs/demos/ios/pom.xml b/docs/demos/ios/pom.xml index bb9a6fc3eaa..4cfd6338edb 100644 --- a/docs/demos/ios/pom.xml +++ b/docs/demos/ios/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 ios ios ios-device diff --git a/docs/demos/ios/src/main/objectivec/TheComponentsOfCodenameOneSnippets.m b/docs/demos/ios/src/main/objectivec/TheComponentsOfCodenameOneSnippets.m new file mode 100644 index 00000000000..e5cf96f6e8a --- /dev/null +++ b/docs/demos/ios/src/main/objectivec/TheComponentsOfCodenameOneSnippets.m @@ -0,0 +1,8 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::the-components-of-codename-one-objective-c-001[] +// Inside an iOS native interface +NSUserDefaults* shared = + [[NSUserDefaults alloc] initWithSuiteName:@"group.com.example.myapp.shared"]; +NSDictionary* payload = [shared dictionaryForKey:@"cn1.shareExtension.payload"]; +// end::the-components-of-codename-one-objective-c-001[] diff --git a/docs/demos/javascript/pom.xml b/docs/demos/javascript/pom.xml index 4147e4d5620..be5460a8447 100644 --- a/docs/demos/javascript/pom.xml +++ b/docs/demos/javascript/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 javascript javascript javascript diff --git a/docs/demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js b/docs/demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js new file mode 100644 index 00000000000..89adc83fa0a --- /dev/null +++ b/docs/demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js @@ -0,0 +1,88 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-javascript-001[] +(function(exports){ + +var o = {}; + + o.helloWorld__java_lang_String = function(param1, callback) { + callback.error(new Error("Not implemented yet")); + }; + + o.isSupported_ = function(callback) { + callback.complete(false); + }; + +exports.com_mycompany_myapp_MyNative= o; + +})(cn1_get_native_interfaces()); +// end::advanced-topics-under-the-hood-javascript-001[] + +// tag::advanced-topics-under-the-hood-javascript-002[] +(function(exports){ + +var o = {}; + + o.helloWorld__java_lang_String = function(param1, callback) { + callback.complete("Hello World!!!"); + } + + o.isSupported_ = function(callback) { + callback.complete(true); + }; + +exports.com_my_code_MyNative = o; + +})(cn1_get_native_interfaces()); +// end::advanced-topics-under-the-hood-javascript-002[] + +// tag::advanced-topics-under-the-hood-javascript-003[] +o.print__java_lang_String = function(param1, callback) { + console.log(param1); + callback.complete(); +} +// end::advanced-topics-under-the-hood-javascript-003[] + +// tag::advanced-topics-under-the-hood-javascript-004[] +o.add__int_int = function(param1, param2, callback) { + callback.complete(param1 + param2); +} +// end::advanced-topics-under-the-hood-javascript-004[] + +// tag::advanced-topics-under-the-hood-javascript-005[] +o.add__int_1ARRAY = function(param1, callback) { + var c = 0, len = param1.length; + for (var i =0; iHello World') + .css({'background-color' : 'yellow', 'border' : '1px solid blue'}); + callback.complete(c.get(0)); +}; +// end::advanced-topics-under-the-hood-javascript-006[] + +// tag::advanced-topics-under-the-hood-javascript-007[] +o["test__byte_boolean_char_short_int_long_float_double_java_lang_String_byte_1ARRAY_boolean_1ARRAY_char_1ARRAY_short_1ARRAY_int_1ARRAY_long_1ARRAY_float_1ARRAY_double_1ARRAY_com_codename1_ui_PeerComponent"] = function(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, callback) { + callback.error(new Error("Not implemented yet")); +}; +// end::advanced-topics-under-the-hood-javascript-007[] + +// tag::advanced-topics-under-the-hood-javascript-008[] +var fireMapChangeEvent = this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double; +google.maps.event.addListener(this.map, 'bounds_changed', function() { + fireMapChangeEvent(self.mapId, self.map.getZoom(), self.map.getCenter().lat(), self.map.getCenter().lng()); +}); +// end::advanced-topics-under-the-hood-javascript-008[] + +// tag::advanced-topics-under-the-hood-javascript-009[] +// end::advanced-topics-under-the-hood-javascript-009[] + +// tag::advanced-topics-under-the-hood-javascript-010[] +var asyncFireMapChangeEvent = this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double$async; +// end::advanced-topics-under-the-hood-javascript-010[] diff --git a/docs/demos/javascript/src/main/snippets/developer-guide/io.js b/docs/demos/javascript/src/main/snippets/developer-guide/io.js new file mode 100644 index 00000000000..dca7c256bfc --- /dev/null +++ b/docs/demos/javascript/src/main/snippets/developer-guide/io.js @@ -0,0 +1,210 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::io-javascript-001[] +[ + { + "url": "http://www.anapioficeandfire.com/api/characters/13", + "name": "Chayle", + "culture": "", + "born": "", + "died": "In 299 AC, at Winterfell", + "titles": [ + "Septon" + ], + "aliases": [], + "father": "", + "mother": "", + "spouse": "", + "allegiances": [], + "books": [ + "http://www.anapioficeandfire.com/api/books/1", + "http://www.anapioficeandfire.com/api/books/2", + "http://www.anapioficeandfire.com/api/books/3" + ], + "povBooks": [], + "tvSeries": [], + "playedBy": [] + }, + { + "url": "http://www.anapioficeandfire.com/api/characters/14", + "name": "Gillam", + "culture": "", + "born": "", + "died": "", + "titles": [ + "Brother" + ], + "aliases": [], + "father": "", + "mother": "", + "spouse": "", + "allegiances": [], + "books": [ + "http://www.anapioficeandfire.com/api/books/5" + ], + "povBooks": [], + "tvSeries": [], + "playedBy": [] + }, + { + "url": "http://www.anapioficeandfire.com/api/characters/15", + "name": "High Septon", + "culture": "", + "born": "", + "died": "", + "titles": [ + "High Septon", + "His High Holiness", + "Father of the Faithful", + "Voice of the Seven on Earth" + ], + "aliases": [ + "The High Sparrow" + ], + "father": "", + "mother": "", + "spouse": "", + "allegiances": [], + "books": [ + "http://www.anapioficeandfire.com/api/books/5", + "http://www.anapioficeandfire.com/api/books/8" + ], + "povBooks": [], + "tvSeries": [ + "Season 5" + ], + "playedBy": [ + "Jonathan Pryce" + ] + } +] +// end::io-javascript-001[] + +// tag::io-javascript-002[] +{ + "status": "OK", + "results": [ + { + "place_id": "ChIJJ5T9-iFawokRTPGaOginEO4", + "formatted_address": "280 Broadway, New York, NY 10007, USA", + "address_components": [ + { + "short_name": "280", + "types": ["street_number"], + "long_name": "280" + }, + { + "short_name": "Broadway", + "types": ["route"], + "long_name": "Broadway" + }, + { + "short_name": "Lower Manhattan", + "types": [ + "neighborhood", + "political" + ], + "long_name": "Lower Manhattan" + }, + { + "short_name": "Manhattan", + "types": [ + "sublocality_level_1", + "sublocality", + "political" + ], + "long_name": "Manhattan" + }, + { + "short_name": "New York", + "types": [ + "locality", + "political" + ], + "long_name": "New York" + }, + { + "short_name": "New York County", + "types": [ + "administrative_area_level_2", + "political" + ], + "long_name": "New York County" + }, + { + "short_name": "NY", + "types": [ + "administrative_area_level_1", + "political" + ], + "long_name": "New York" + }, + { + "short_name": "US", + "types": [ + "country", + "political" + ], + "long_name": "United States" + }, + { + "short_name": "10007", + "types": ["postal_code"], + "long_name": "10007" + }, + { + "short_name": "1868", + "types": ["postal_code_suffix"], + "long_name": "1868" + } + ], + "types": ["street_address"], + "geometry": { + "viewport": { + "northeast": { + "lng": -74.0044642197085, + "lat": 40.7156470802915 + }, + "southwest": { + "lng": -74.0071621802915, + "lat": 40.7129491197085 + } + }, + "location_type": "ROOFTOP", + "location": { + "lng": -74.00581319999999, + "lat": 40.7142981 + } + } + } + /* SNIPED the rest */ + ] +} +// end::io-javascript-002[] + +// tag::io-javascript-003[] +{ + "sid": "[sid value]", + "date_created": "Sat, 09 Sep 2017 19:47:30 +0000", + "date_updated": "Sat, 09 Sep 2017 19:47:30 +0000", + "date_sent": null, + "account_sid": "[sid value]", + "to": "[to phone number]", + "from": "[from phone number]", + "messaging_service_sid": null, + "body": "Sent from your Twilio trial account - Hello World", + "status": "queued", + "num_segments": "1", + "num_media": "0", + "direction": "outbound-api", + "api_version": "2010-04-01", + "price": null, + "price_unit": "USD", + "error_code": null, + "error_message": null, + "uri": "/2010-04-01/Accounts/[sid value]/Messages/SMe802d86b9f2246989c7c66e74b2d84ef.json", + "subresource_uris": { + "media": "/2010-04-01/Accounts/[sid value]/Messages/[message value]/Media.json" + } +} +// end::io-javascript-003[] diff --git a/docs/demos/javascript/src/main/snippets/developer-guide/push-notifications.js b/docs/demos/javascript/src/main/snippets/developer-guide/push-notifications.js new file mode 100644 index 00000000000..67cb40f3b1e --- /dev/null +++ b/docs/demos/javascript/src/main/snippets/developer-guide/push-notifications.js @@ -0,0 +1,15 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::push-notifications-javascript-001[] +{"error":"Error message"} +// end::push-notifications-javascript-001[] + +// tag::push-notifications-javascript-002[] +[ + {"id"="deviceId","status"="error","message"="Invalid Device ID"}, + {"id"="cn1-gcm-nativegcmkey","status"="updateId", "newId"="cn1-gcm-newgcmkey"}, + {"id"="cn1-gcm-okgcmkey","status"="OK"}, + {"id"="cn1-gcm-errorkey","status"="error","message"="Server error message"}, + {"id"="cn1-ios-iphonekey","status"="inactive"}, +] +// end::push-notifications-javascript-002[] diff --git a/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.html b/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.html new file mode 100644 index 00000000000..84ef00bbf75 --- /dev/null +++ b/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.html @@ -0,0 +1,14 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-javascript-html-001[] +
+ + + +

...Loading...

+
+// end::working-with-javascript-html-001[] + +// tag::working-with-javascript-html-002[] + +// end::working-with-javascript-html-002[] diff --git a/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.js b/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.js new file mode 100644 index 00000000000..7472564c9d5 --- /dev/null +++ b/docs/demos/javascript/src/main/snippets/developer-guide/working-with-javascript.js @@ -0,0 +1,11 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::working-with-javascript-javascript-001[] +{ + "JavaScript" : { + "libs" : [ + "//maps.googleapis.com/maps/api/js?v=3.exp&key={{javascript.googlemaps.key}}" + ] + } +} +// end::working-with-javascript-javascript-001[] diff --git a/docs/demos/javase/pom.xml b/docs/demos/javase/pom.xml index 6618251a756..5fe37a07502 100644 --- a/docs/demos/javase/pom.xml +++ b/docs/demos/javase/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 javase javase diff --git a/docs/demos/linux/pom.xml b/docs/demos/linux/pom.xml index 2f6069529f0..5e181ed99d6 100644 --- a/docs/demos/linux/pom.xml +++ b/docs/demos/linux/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 linux linux linux-device diff --git a/docs/demos/linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c b/docs/demos/linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c new file mode 100644 index 00000000000..0ff001b07e3 --- /dev/null +++ b/docs/demos/linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c @@ -0,0 +1,9 @@ +// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline. + +// tag::advanced-topics-under-the-hood-c-001[] +#include "cn1_globals.h" + +JAVA_OBJECT com_mycompany_myapp_MyNativeImplCodenameOne_helloWorld___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT param1) { + return JAVA_NULL; +} +// end::advanced-topics-under-the-hood-c-001[] diff --git a/docs/demos/pom.xml b/docs/demos/pom.xml index 5626d61565e..0bc4923fbd4 100644 --- a/docs/demos/pom.xml +++ b/docs/demos/pom.xml @@ -19,16 +19,18 @@ common - 7.0.208 - 7.0.208 + 8.0-SNAPSHOT + 8.0-SNAPSHOT UTF-8 - 1.8 - 11 + 17 + 17 1.7.11 3.8.0 - 1.8 - 1.8 - 1.8 + 17 + 17 + 17 + 17 + 17 democode diff --git a/docs/demos/win/pom.xml b/docs/demos/win/pom.xml index 528ca7582e0..9106f084637 100644 --- a/docs/demos/win/pom.xml +++ b/docs/demos/win/pom.xml @@ -14,8 +14,8 @@ UTF-8 - 1.8 - 1.8 + 17 + 17 win win windows-device diff --git a/docs/developer-guide/3D-Graphics.asciidoc b/docs/developer-guide/3D-Graphics.asciidoc index d294f0cf881..ecc20103649 100644 --- a/docs/developer-guide/3D-Graphics.asciidoc +++ b/docs/developer-guide/3D-Graphics.asciidoc @@ -61,46 +61,6 @@ The renderer below draws a Phong lit cube. `Primitives.cube` builds the geometry a `Material` describes the surface, and a `Camera` supplies the view. Notice there is no shader code anywhere. -[source,java] ----- -RenderView view = new RenderView(new Renderer() { - private final Camera camera = new Camera(); - private Mesh cube; - private Material material; - private float angle; - - public void onInit(GraphicsDevice device) { - cube = Primitives.cube(device, 1.5f); - material = new Material(Material.Type.PHONG) - .setColor(0xff3366ff) - .setShininess(24f); - camera.setPerspective(45f, 0.1f, 100f) - .setPosition(2.5f, 2f, 3.5f) - .setTarget(0f, 0f, 0f); - device.setLight(new Light().setDirection(-0.4f, -1f, -0.6f)); - } - - public void onResize(GraphicsDevice device, int w, int h) { - camera.setAspect((float) w / Math.max(1, h)); - device.setViewport(0, 0, w, h); - } - - public void onFrame(GraphicsDevice device) { - angle += 0.02f; - device.clear(0xff101018, true, true); - device.setCamera(camera); - device.draw(cube, material, Matrix4.rotation(angle, 0.3f, 1f, 0.1f)); - } - - public void onDispose(GraphicsDevice device) { - } -}); -view.setContinuous(true); // animate; omit for on-demand rendering - -Form hi = new Form("3D", new BorderLayout()); -hi.add(BorderLayout.CENTER, view); -hi.show(); ----- `setContinuous(true)` runs an animation loop. For a static scene leave it off and call `view.requestRender()` whenever something changes; this conserves battery. @@ -128,12 +88,6 @@ optional `Texture` (`setTexture`), and a `RenderState` controlling depth testing alpha blending and face culling. Textures come from a Codename One `Image` or raw ARGB pixels: -[source,java] ----- -Texture tex = device.createTexture(myImage); // or createTexture(w, h, argb) -tex.setFilter(Texture.Filter.LINEAR).setWrap(Texture.Wrap.REPEAT); -Material m = new Material(Material.Type.UNLIT).setTexture(tex); ----- .An UNLIT cube with a checkerboard texture (NEAREST filtering) image::img/gpu-textured-cube.png[A checkerboard textured cube,scaledwidth=40%] @@ -145,14 +99,6 @@ https://www.codenameone.com/javadoc/com/codename1/gpu/VertexBuffer.html[VertexBu with a `VertexFormat`, fill the interleaved float data, and (optionally) an https://www.codenameone.com/javadoc/com/codename1/gpu/IndexBuffer.html[IndexBuffer]: -[source,java] ----- -VertexBuffer vb = device.createVertexBuffer(VertexFormat.POSITION_NORMAL_TEXCOORD, 4); -vb.setData(new float[] { /* px,py,pz, nx,ny,nz, u,v per vertex ... */ }); -IndexBuffer ib = device.createIndexBuffer(6); -ib.setData(new int[] { 0, 1, 2, 0, 2, 3 }); -Mesh mesh = new Mesh(vb, ib, PrimitiveType.TRIANGLES); ----- Vertex buffers are allocated through the platform SIMD allocator, which on iOS (ParparVM) places the data at a fixed, aligned native address so it can be handed @@ -166,16 +112,6 @@ binary `.glb` container and the JSON `.gltf` form with embedded buffers. `loadModel` returns the mesh together with the base-color texture from the model's own material, so a textured model renders with no extra setup: -[source,java] ----- -InputStream in = Display.getInstance().getResourceAsStream(getClass(), "/boombox.glb"); -GltfLoader.GltfModel loaded = GltfLoader.loadModel(device, in); -Material material = new Material(Material.Type.PHONG).setShininess(16f); -if (loaded.getBaseColorTexture() != null) { - material.setTexture(loaded.getBaseColorTexture()); -} -Mesh model = loaded.getMesh(); // draw it like any other mesh ----- The model below is the Khronos "BoomBox" glTF sample (a CC0 model carrying its own base-color texture), loaded this way and drawn Phong lit: @@ -218,12 +154,6 @@ https://www.codenameone.com/javadoc/com/codename1/gpu/Matrix4.html[Matrix4] Querying capabilities at runtime: -[source,java] ----- -if (CN.isGpuSupported()) { - // GraphicsDevice.getCapabilities() exposes max texture size, shader level, etc. -} ----- === Threading diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index 0d447810aa5..3595b57e145 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -949,7 +949,7 @@ You will start with Android which should be familiar and intuitive to many devel [source,java] ---- -include::../demos/common/src/main/java/com/mycompany/myapp/MyNativeImplStub.java[tag=myNativeAndroidStub,indent=0] +include::../demos/android/src/main/java/com/mycompany/myapp/MyNativeImplStub.java[tag=myNativeAndroidStub,indent=0] ---- The stub implementation always returns `false`, `null` or `0` by default. The `isSupported` also defaults to `false` thus allowing you to implement a `NativeInterface` on some platforms and leave the rest out without knowing anything about these platforms. @@ -1157,20 +1157,14 @@ in the bodies. Each function takes the thread state as its first argument and [source,java] ---- -public interface MyNative extends NativeInterface { - String hello(String name); -} +include::../demos/common/src/main/java/com/mycompany/myapp/MyNative.java[tag=myNativeInterface,indent=0] ---- This generates the C stub below (returning `JAVA_NULL` until you implement it): [source,c] ---- -#include "cn1_globals.h" - -JAVA_OBJECT com_mycompany_myapp_MyNativeImplCodenameOne_hello___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT param1) { - return JAVA_NULL; -} +include::../demos/linux/src/main/c/com/mycompany/myapp/MyNativeImplCodenameOne.c[tag=advanced-topics-under-the-hood-c-001,indent=0] ---- Type mapping: the primitives (`int`, `long`, `boolean`, `byte`, `short`, `char`, @@ -1192,42 +1186,14 @@ The default generated stubs for the JavaScript build look like this `com_mycompa [source,JavaScript] ---- -(function(exports){ - -var o = {}; - - o.helloWorld__java_lang_String = function(param1, callback) { - callback.error(new Error("Not implemented yet")); - }; - - o.isSupported_ = function(callback) { - callback.complete(false); - }; - -exports.com_mycompany_myapp_MyNative= o; - -})(cn1_get_native_interfaces()); +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-001,indent=0] ---- A simple implementation looks like this: [source,JavaScript] ---- -(function(exports){ - -var o = {}; - - o.helloWorld__java_lang_String = function(param1, callback) { - callback.complete("Hello World!!!"); - } - - o.isSupported_ = function(callback) { - callback.complete(true); - }; - -exports.com_my_code_MyNative = o; - -})(cn1_get_native_interfaces()); +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-002,indent=0] ---- Notice that you use the `complete()` method of the provided callback to pass the return value rather than using the `return` statement. This is to work around the fact that JavaScript doesn't natively support threads. The *Java* thread that's calling your native interface will block until your method calls `callback.complete()`. This allows you to use asynchronous APIs inside your native method while still allowing Codename One to work use your native interface through a synchronous API. @@ -1258,10 +1224,7 @@ becomes [source,JavaScript] ---- -o.print__java_lang_String = function(param1, callback) { - console.log(param1); - callback.complete(); -} +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-003,indent=0] ---- Java API: @@ -1275,9 +1238,7 @@ becomes [source,JavaScript] ---- -o.add__int_int = function(param1, param2, callback) { - callback.complete(param1 + param2); -} +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-004,indent=0] ---- [source,java] @@ -1289,13 +1250,7 @@ becomes [source,JavaScript] ---- -o.add__int_1ARRAY = function(param1, callback) { - var c = 0, len = param1.length; - for (var i =0; i` tag to be returned [source,JavaScript] ---- -o.createHelloComponent_ = function(callback) { - var c = jQuery('
Hello World
') - .css({'background-color' : 'yellow', 'border' : '1px solid blue'}); - callback.complete(c.get(0)); -}; +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-006,indent=0] ---- @@ -1405,12 +1356,7 @@ NOTE: You had to break lines for the print version, the JavaScript version is a .JavaScript Version [source,JavaScript] ---- -o.test__byte_boolean_char_short_int_long_float_double -_java_lang_String_byte_1ARRAY_boolean_1ARRAY_char_1ARRAY -_short_1ARRAY_int_1ARRAY_long_1ARRAY_float_1ARRAY_double -_1ARRAY_com_codename1_ui_PeerComponent = function(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, callback) { - callback.error(new Error("Not implemented yet")); -}; +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-007,indent=0] ---- .Java SE Version @@ -1420,13 +1366,6 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/advancedto ---- -.C# Version -[source,csharp] ----- -public void test(byte param, bool param1, char param2, short param3, int param4, long param5, float param6, double param7, String param8, byte[] param9, boolean[] param10, char[] param11, short[] param12, int[] param13, long[] param14, float[] param15, double[] param16, FrameworkElement param17) { -} ----- - ==== Android native permissions Permissions in Codename One are seamless. Codename One traverses the bytecode and automatically assigns permissions to Android applications based on the API’s used by the developer. @@ -1511,66 +1450,28 @@ However, there is still more you need to do. To implement this natively you need [source,xml] ---- - - - - - - - - - - - - - - - - //this doesnt work - - - - - - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-001,indent=0] ---- You need the broadcast permission XML and the permission XML. Both are doable through the build hints. The former is pretty easy: [source,xml] ---- -android.xpermissions= +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-002,indent=0] ---- The latter isn't much harder; notice that many lines have been merged into a single line for convenience: [source,xml] ---- -android.xapplication= +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-003,indent=0] ---- Here it's formatted for readability: [source,xml] ---- - - - - - - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-004,indent=0] ---- ===== Listening & permissions @@ -1730,10 +1631,7 @@ This method is called from JavaScript inside a native interface using the follow [source,JavaScript] ---- -var fireMapChangeEvent = this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double; -google.maps.event.addListener(this.map, 'bounds_changed', function() { - fireMapChangeEvent(self.mapId, self.map.getZoom(), self.map.getCenter().lat(), self.map.getCenter().lng()); -}); +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-008,indent=0] ---- In this example you first get a reference to the `fireMapChangeEvent` method, and then call it later. For example, you could have called it directly also. @@ -1761,14 +1659,14 @@ One of the problematic aspects of calling back into Java from JavaScript is that [source,JavaScript] ---- -this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double; +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-009,indent=0] ---- to [source,JavaScript] ---- -this.$GLOBAL$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double$async; +include::../demos/javascript/src/main/snippets/developer-guide/advanced-topics-under-the-hood.js[tag=advanced-topics-under-the-hood-javascript-010,indent=0] ---- This will cause the call to be wrapped in the appropriate bootstrap code to work with threads - and it's necessary in cases where the method *may* use threads of any kind. The side-effect of calling a method with the `$async` suffix is that you can't use return values from the method. @@ -1809,12 +1707,7 @@ Codename One has a large repository of https://www.codenameone.com/cn1libs.html[ [source,xml] ---- - - com.example - mylib-lib - 1.0.0 - pom - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-005,indent=0] ---- Add the dependency *before* the `` comment in `common/pom.xml`. The `-lib` suffix on the `artifactId` and the `pom` declaration are both important - they tell Maven to pull in the library's "lib" aggregator module rather than a plain jar. See <> for the full library-consumption guide. @@ -1834,14 +1727,7 @@ The quickest path from a shell: [source,bash] ---- -mvn archetype:generate \ - -DarchetypeArtifactId=cn1lib-archetype \ - -DarchetypeGroupId=com.codenameone \ - -DarchetypeVersion=LATEST \ - -DgroupId=com.example.mylib \ - -DartifactId=mylib \ - -Dversion=1.0-SNAPSHOT \ - -DinteractiveMode=false +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh[tag=advanced-topics-under-the-hood-bash-001,indent=0] ---- Once the project is generated, add your cross-platform Java sources under `common/src/main/java`, CSS under `common/src/main/css`, and run `mvn install` from the project root to build. The build produces both a Maven artifact for pom-style consumption and a legacy `.cn1lib` file in `common/target` for users still on Ant. @@ -1888,99 +1774,16 @@ The rule of thumb is that a build hint with a numeric or boolean value is always These build hints are probably of the "required" type: -[source,java] +[source,properties] ---- -android.debug -android.release -android.installLocation -android.licenseKey -android.stack_size -android.statusbar_hidden -android.googleAdUnitId -android.includeGPlayServices -android.headphoneCallback -android.gpsPermission -android.asyncPaint -android.supportV4 -android.theme -android.cusom_layout1 -android.versionCode -android.captureRecord -android.removeBasePermissions -android.blockExternalStoragePermission -android.min_sdk_version -android.smallScreens -android.streamMode -android.enableProguard -android.targetSDKVersion -android.web_loading_hidden -facebook.appId -facebook.clientToken -ios.keyboardOpen -ios.project_type -ios.newStorageLocation -ios.prerendered_icon -ios.application_exits -ios.themeMode -ios.xcode_version -javascript.inject_proxy -javascript.minifying -javascript.proxy.url -javascript.sourceFilesCopied -javascript.teavm.version -google.adUnitId -ios.includePush -ios.headphoneCallback -ios.enableAutoplayVideo -ios.googleAdUnitId -ios.googleAdUnitIdPadding -ios.enableBadgeClear -ios.locationUsageDescription -ios.bundleVersion -ios.objC -ios.testFlight -ios.metal -desktop.width -desktop.height -desktop.adaptToRetina -desktop.resizable -desktop.fontSizes -desktop.theme -desktop.themeMac -desktop.themeWin -desktop.windowsOutput -noExtraResources -android.permission. +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties[tag=advanced-topics-under-the-hood-properties-001,indent=0] ---- These build hints should probably be appended: -[source,java] +[source,properties] ---- -android.xapplication -android.xpermissions -android.xintent_filter -android.facebook_permissions -android.stringsXml -android.style -android.nonconsumable -android.xapplication_attr -android.xactivity -android.pushVibratePattern -android.proguardKeep -android.sharedUserId -android.sharedUserLabel -ios.urlScheme -ios.interface_orientation -ios.plistInject -ios.facebook_permissions -ios.applicationDidEnterBackground -ios.viewDidLoad -ios.glAppDelegateHeader -ios.glAppDelegateBody -ios.beforeFinishLaunching -ios.afterFinishLaunching -ios.add_libs +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties[tag=advanced-topics-under-the-hood-properties-002,indent=0] ---- .cn1lib Structure/File Format @@ -2051,23 +1854,7 @@ The code below allows you to rearrange the items based on a sensible order. Noti [source,java] ---- -Form hi = new Form("Rearrangeable Items", new BorderLayout()); -String[] buttons = {"A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", - "A Feast For Crows", "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring" }; - -Container box = new Container(BoxLayout.y()); -box.setScrollableY(true); -box.setDropTarget(true); -java.util.List got = Arrays.asList(buttons); -Collections.shuffle(got); -for(String current : got) { - MultiButton mb = new MultiButton(current); - box.add(mb); - mb.setDraggable(true); -} - -hi.add(BorderLayout.NORTH, "Arrange The Titles").add(BorderLayout.CENTER, box); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/advancedtopics/RearrangeableItemsDemo.java[tag=rearrangeableItems,indent=0] ---- .Drag demo @@ -2105,11 +1892,7 @@ of your project. It should look like this: [source,xml] ---- - - #ff00ff00 - #80ff0000 - #800000ff - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-006,indent=0] ---- === Intercepting URLs on iOS & Android @@ -2124,7 +1907,7 @@ On Android you need to define an intent filter which you can do using the `andro [source,xml] ---- -android.xintent_filter= +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-007,indent=0] ---- You can read more about it in link:http://stackoverflow.com/questions/11421048/android-ios-custom-uri-protocol-handling[this stack overflow question]. @@ -2173,7 +1956,7 @@ Use `android.xapplication` to inject a dedicated bridge activity and bind the ac [source,properties] ---- -codename1.arg.android.xapplication= +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.properties[tag=advanced-topics-under-the-hood-properties-003,indent=0] ---- Keep `codename1.arg.android.activity.launchMode=singleTask` on your main CN1 activity so app resume behavior is predictable. @@ -2217,76 +2000,12 @@ Optional native Android example for certificate fingerprint verification (called [source,java] ---- -private static boolean isTrustedCallerSignature(Context ctx, String packageName, Set allowedSha256) { - if (packageName == null || packageName.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 (Exception ex) { - return false; - } -} - -private static boolean hasAllowedFingerprint(Signature[] sigs, Set allowedSha256) throws Exception { - 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) { - StringBuilder out = new StringBuilder(data.length * 2); - for (byte b : data) out.append(String.format("%02X", b)); - return out.toString(); -} +include::../demos/android/src/main/java/com/example/wallet/WalletBridgeSecurity.java[tag=walletTrustedCallerSignature,indent=0] ---- [source,java] ---- -public class WalletBridgeActivity extends Activity { - private static WalletBridgeActivity active; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - active = this; - - Intent in = getIntent(); - String caller = getCallingPackage(); - - Intent launch = getPackageManager().getLaunchIntentForPackage(getPackageName()); - launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); - launch.putExtra("wallet_payload", in.getStringExtra("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() { - if (active == this) { - active = null; - } - super.onDestroy(); - } - - public static WalletBridgeActivity getActive() { - return active; - } -} +include::../demos/android/src/main/java/com/example/wallet/WalletBridgeActivity.java[tag=walletBridgeActivity,indent=0] ---- ===== Reading contract data in `start()` @@ -2303,25 +2022,7 @@ Codename One exposes launch metadata through `AppArg` and `Display` properties. [source,java] ---- -@Override -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; - } - - // Run authentication + back end verification, then call native bridge completion API. - return; - } - showMainForm(); -} +include::../demos/android/src/main/java/com/example/wallet/WalletLaunchHandlerSnippet.java[tag=walletLaunchStart,indent=0] ---- ===== Returning results to the wallet API @@ -2351,14 +2052,14 @@ The equivalent on the iOS side would be: [source,xml] ---- -ios.plistInject=CFBundleURLTypes CFBundleURLName com.yourcompany.myapp CFBundleURLSchemes myapp +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-008,indent=0] ---- For example, that can conflict with the Facebook integration if you use `FacebookConnect` which needs access to the schemes. To work around this you can use the build hint `ios.urlScheme` for example: [source,xml] ---- -ios.urlScheme=myapp +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-009,indent=0] ---- [[native-peer-components]] @@ -2816,93 +2517,7 @@ You can find the manifest file injections required by opening the `AndroidManife [source,xml] ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-010,indent=0] ---- @@ -3114,37 +2729,7 @@ NOTE: Codename One also supports https://www.codenameone.com/blog/mirah-for-code The first thing to do is take a look at the byte-code that's produced by the compiler. Use `javap` to print out a nice version. -Consider this sample Kotlin class: - -[source,kotlin] ----- -package com.codename1.hellokotlin2 - -import com.codename1.ui.Button -import com.codename1.ui.Form -import com.codename1.ui.Label -import com.codename1.ui.layouts.BoxLayout - -/** - * Created by shannah on 2017-07-10. - */ -class KotlinForm : Form { - - constructor() : super("Hello Kotlin", BoxLayout.y()) { - val label = Label("Hello Kotlin") - val clickMe = Button("Click Me") - clickMe.addActionListener { - label.setText("You Clicked Me"); - revalidate(); - } - - add(label).add(clickMe); - - } - - -} ----- +Consider a sample Kotlin class that extends `Form`, creates a `Label` and `Button`, and updates the label from the button's listener. Take a look at the bytecode that Kotlin produced for this class: @@ -3154,145 +2739,7 @@ $ javap -v com/codename1/hellokotlin2/KotlinForm.class [source,bytecode] ---- - Last modified 10-Jul-2017; size 1456 bytes - MD5 checksum 1cb00f6e63b918bb5a9f146ca8b0b78e - Compiled from "KotlinForm.kt" -public final class com.codename1.hellokotlin2.KotlinForm extends com.codename1.ui.Form - SourceFile: "KotlinForm.kt" - InnerClasses: - static final #31; //class com/codename1/hellokotlin2/KotlinForm$1 - RuntimeVisibleAnnotations: - 0: #56(#57=[I#58,I#58,I#59],#60=[I#58,I#61,I#58],#62=I#58,#63=[s#64],#65=[s#55,s#66,s#6,s#67]) - minor version: 0 - major version: 50 - flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER -Constant pool: - #1 = Utf8 com/codename1/hellokotlin2/KotlinForm - #2 = Class #1 // com/codename1/hellokotlin2/KotlinForm - #3 = Utf8 com/codename1/ui/Form - #4 = Class #3 // com/codename1/ui/Form - #5 = Utf8 - #6 = Utf8 ()V - #7 = Utf8 Hello Kotlin - #8 = String #7 // Hello Kotlin - #9 = Utf8 com/codename1/ui/layouts/BoxLayout - #10 = Class #9 // com/codename1/ui/layouts/BoxLayout - #11 = Utf8 y - #12 = Utf8 ()Lcom/codename1/ui/layouts/BoxLayout; - #13 = NameAndType #11:#12 // y:()Lcom/codename1/ui/layouts/BoxLayout; - #14 = Methodref #10.#13 // com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; - #15 = Utf8 com/codename1/ui/layouts/Layout - #16 = Class #15 // com/codename1/ui/layouts/Layout - #17 = Utf8 (Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V - #18 = NameAndType #5:#17 // "":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V - #19 = Methodref #4.#18 // com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V - #20 = Utf8 com/codename1/ui/Label - #21 = Class #20 // com/codename1/ui/Label - #22 = Utf8 (Ljava/lang/String;)V - #23 = NameAndType #5:#22 // "":(Ljava/lang/String;)V - #24 = Methodref #21.#23 // com/codename1/ui/Label."":(Ljava/lang/String;)V - #25 = Utf8 com/codename1/ui/Button - #26 = Class #25 // com/codename1/ui/Button - #27 = Utf8 Click Me - #28 = String #27 // Click Me - #29 = Methodref #26.#23 // com/codename1/ui/Button."":(Ljava/lang/String;)V - #30 = Utf8 com/codename1/hellokotlin2/KotlinForm$1 - #31 = Class #30 // com/codename1/hellokotlin2/KotlinForm$1 - #32 = Utf8 (Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V - #33 = NameAndType #5:#32 // "":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V - #34 = Methodref #31.#33 // com/codename1/hellokotlin2/KotlinForm$1."":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V - #35 = Utf8 com/codename1/ui/events/ActionListener - #36 = Class #35 // com/codename1/ui/events/ActionListener - #37 = Utf8 addActionListener - #38 = Utf8 (Lcom/codename1/ui/events/ActionListener;)V - #39 = NameAndType #37:#38 // addActionListener:(Lcom/codename1/ui/events/ActionListener;)V - #40 = Methodref #26.#39 // com/codename1/ui/Button.addActionListener:(Lcom/codename1/ui/events/ActionListener;)V - #41 = Utf8 com/codename1/ui/Component - #42 = Class #41 // com/codename1/ui/Component - #43 = Utf8 add - #44 = Utf8 (Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - #45 = NameAndType #43:#44 // add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - #46 = Methodref #2.#45 // com/codename1/hellokotlin2/KotlinForm.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - #47 = Utf8 com/codename1/ui/Container - #48 = Class #47 // com/codename1/ui/Container - #49 = Methodref #48.#45 // com/codename1/ui/Container.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - #50 = Utf8 clickMe - #51 = Utf8 Lcom/codename1/ui/Button; - #52 = Utf8 label - #53 = Utf8 Lcom/codename1/ui/Label; - #54 = Utf8 this - #55 = Utf8 Lcom/codename1/hellokotlin2/KotlinForm; - #56 = Utf8 Lkotlin/Metadata; - #57 = Utf8 mv - #58 = Integer 1 - #59 = Integer 6 - #60 = Utf8 bv - #61 = Integer 0 - #62 = Utf8 k - #63 = Utf8 d1 - #64 = Utf8 - \n\n\20¢¨ - #65 = Utf8 d2 - #66 = Utf8 Lcom/codename1/ui/Form; - #67 = Utf8 HelloKotlin2 - #68 = Utf8 KotlinForm.kt - #69 = Utf8 Code - #70 = Utf8 LocalVariableTable - #71 = Utf8 LineNumberTable - #72 = Utf8 SourceFile - #73 = Utf8 InnerClasses - #74 = Utf8 RuntimeVisibleAnnotations -{ - public com.codename1.hellokotlin2.KotlinForm(); - descriptor: ()V - flags: ACC_PUBLIC - Code: - stack=5, locals=3, args_size=1 - 0: aload_0 - 1: ldc #8 // String Hello Kotlin - 3: invokestatic #14 // Method com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; - 6: checkcast #16 // class com/codename1/ui/layouts/Layout - 9: invokespecial #19 // Method com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V - 12: new #21 // class com/codename1/ui/Label - 15: dup - 16: ldc #8 // String Hello Kotlin - 18: invokespecial #24 // Method com/codename1/ui/Label."":(Ljava/lang/String;)V - 21: astore_1 - 22: new #26 // class com/codename1/ui/Button - 25: dup - 26: ldc #28 // String Click Me - 28: invokespecial #29 // Method com/codename1/ui/Button."":(Ljava/lang/String;)V - 31: astore_2 - 32: aload_2 - 33: new #31 // class com/codename1/hellokotlin2/KotlinForm$1 - 36: dup - 37: aload_0 - 38: aload_1 - 39: invokespecial #34 // Method com/codename1/hellokotlin2/KotlinForm$1."":(Lcom/codename1/hellokotlin2/KotlinForm;Lcom/codename1/ui/Label;)V - 42: checkcast #36 // class com/codename1/ui/events/ActionListener - 45: invokevirtual #40 // Method com/codename1/ui/Button.addActionListener:(Lcom/codename1/ui/events/ActionListener;)V - 48: aload_0 - 49: aload_1 - 50: checkcast #42 // class com/codename1/ui/Component - 53: invokevirtual #46 // Method add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - 56: aload_2 - 57: checkcast #42 // class com/codename1/ui/Component - 60: invokevirtual #49 // Method com/codename1/ui/Container.add:(Lcom/codename1/ui/Component;)Lcom/codename1/ui/Container; - 63: pop - 64: return - LocalVariableTable: - Start Length Slot Name Signature - 32 32 2 clickMe Lcom/codename1/ui/Button; - 22 42 1 label Lcom/codename1/ui/Label; - 0 65 0 this Lcom/codename1/hellokotlin2/KotlinForm; - LineNumberTable: - line 13: 0 - line 14: 12 - line 15: 22 - line 16: 32 - line 21: 48 -} - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt[tag=advanced-topics-under-the-hood-bytecode-001,indent=0] ---- That's a big mess of stuff, but it's pretty easy to pick through it when you know what you're looking for. The layout of this output is pretty straight forward. The beginning shows that this is a class definition: @@ -3308,8 +2755,7 @@ After the class definition, it shows the internal classes: [source,bytecode] ---- -InnerClasses: - static final #31; //class com/codename1/hellokotlin2/KotlinForm$1 +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt[tag=advanced-topics-under-the-hood-bytecode-002,indent=0] ---- **The Constant Pool** @@ -3318,17 +2764,7 @@ And the constants that are used in the class: [source,bytecode] ---- -Constant pool: - #1 = Utf8 com/codename1/hellokotlin2/KotlinForm - #2 = Class #1 // com/codename1/hellokotlin2/KotlinForm - #3 = Utf8 com/codename1/ui/Form - #4 = Class #3 // com/codename1/ui/Form - #5 = Utf8 - #6 = Utf8 ()V - #7 = Utf8 Hello Kotlin - #8 = String #7 // Hello Kotlin - #9 = Utf8 com/codename1/ui/layouts/BoxLayout - ... etc. +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt[tag=advanced-topics-under-the-hood-bytecode-003,indent=0] ---- @@ -3342,20 +2778,7 @@ After the constant pool, you see each of the methods of the class written out as [source,bytecode] ---- -public com.codename1.hellokotlin2.KotlinForm(); - descriptor: ()V - flags: ACC_PUBLIC - Code: - stack=5, locals=3, args_size=1 - 0: aload_0 - 1: ldc #8 // String Hello Kotlin - 3: invokestatic #14 // Method com/codename1/ui/layouts/BoxLayout.y:()Lcom/codename1/ui/layouts/BoxLayout; - 6: checkcast #16 // class com/codename1/ui/layouts/Layout - 9: invokespecial #19 // Method com/codename1/ui/Form."":(Ljava/lang/String;Lcom/codename1/ui/layouts/Layout;)V - 12: new #21 // class com/codename1/ui/Label - 15: dup - 16: ldc #8 // String Hello Kotlin - etc. +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.txt[tag=advanced-topics-under-the-hood-bytecode-004,indent=0] ---- In the above snippet, the first instruction is `aload_0` (which adds `this` to the stack). The 2nd instruction is `ldc`, (which loads constant #8 -- the string "Hello Kotlin" to the stack). The 3rd instruction is `invokestatic` which calls the static method define by Constant #14 from the constant pool, with the two parameters that had been added to the stack. @@ -3392,21 +2815,9 @@ IMPORTANT: Building cn1libs manually in this way is a ** bad habit, as it bypass For your "Hello World" test you will need to create a separate project in your JVM language and produce class files that you will *manually* copy into an appropriate location of your project. You will want to use the *normal* tools for the language and not worry about how it integrates with Codename One. For Kotlin, the steps follow the getting started tutorial on the Kotlin site to create a new Kotlin project in IntelliJ. When Steve ported Mirah, he used a text editor and the mirahc command-line compiler to create your Hello World class. The tools and process will depend on the language. -Here is the "hello world" you created in Kotlin: - -[source,kotlin] ----- -package com.mycompany.myapp - -class HelloKotlin { - - fun hello() { - System.out.println("Hello from Kotlin"); - } -} ----- +Create a minimal "hello world" class in the external language runtime you are evaluating. -After building this, the build produces a directory that contains "com/mycompany/myapp/HelloKotlin.class". +After building this, the build produces a directory that contains `com/mycompany/myapp/HelloKotlin.class`. It also produced a.jar file that contains this class. @@ -3453,7 +2864,7 @@ For both the Kotlin and Mirah support, you wanted integration to be seamless. Yo [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.xml[tag=advanced-topics-under-the-hood-xml-011,indent=0] ---- And it would automatically handle Kotlin and Java files together: Seamlessly. A few places in a Codename One's build.xml file where you call "javac" so you needed to inject these tags in those places. This injection is performed automatically by the Codename One IntelliJ plugin. @@ -3485,7 +2896,7 @@ An update happens by running this tool with a path to a Codename One project for [source,bash] ---- -java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh[tag=advanced-topics-under-the-hood-bash-002,indent=0] ---- For example: @@ -3510,7 +2921,7 @@ Notice that no download happened since the files were up-to-date. You can also f [source,bash] ---- -java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project +include::../demos/common/src/main/snippets/developer-guide/advanced-topics-under-the-hood.sh[tag=advanced-topics-under-the-hood-bash-003,indent=0] ---- The way this works under the hood is thought a `Versions.properties` within your directory that lists the versions of local files. That way you know what should be updated. diff --git a/docs/developer-guide/Advertising.asciidoc b/docs/developer-guide/Advertising.asciidoc index 422f1be3500..90319f342cb 100644 --- a/docs/developer-guide/Advertising.asciidoc +++ b/docs/developer-guide/Advertising.asciidoc @@ -62,23 +62,14 @@ per-platform classifier jars: [source,xml] ---- - - com.codenameone - cn1-admob-lib - ${cn1.version} - pom - +include::../demos/common/src/main/snippets/developer-guide/advertising.xml[tag=advertising-xml-001,indent=0] ---- `cn1-ads-mock` is a single cross-platform jar, so it needs no classifier: [source,xml] ---- - - com.codenameone - cn1-ads-mock - ${cn1.version} - +include::../demos/common/src/main/snippets/developer-guide/advertising.xml[tag=advertising-xml-002,indent=0] ---- === Enabling a provider @@ -86,13 +77,6 @@ per-platform classifier jars: Enable a provider once at startup. Each library exposes a static `install()` method that registers its provider with `AdManager`: -[source,java] ----- -AdMobProvider.install(); // from cn1-admob -// AppLovinProvider.install(); // from cn1-applovin -// LevelPlayProvider.install(); // from cn1-unity-levelplay -// MockAdProvider.install(); // from cn1-ads-mock (tests / simulator) ----- That single call binds the provider; the rest of your code uses only the framework API in `com.codename1.ads`. If no provider is registered the API @@ -108,16 +92,6 @@ wrap Google's User Messaging Platform or an equivalent), and on iOS the App Tracking Transparency prompt must be presented to access the advertising identifier. The recommended order is to initialize, gather consent, then load: -[source,java] ----- -AdMobProvider.install(); -AdManager.initialize(new AdConfig().testMode(true), ready -> - AdConsent.requestConsent(status -> { - if (AdConsent.canRequestAds()) { - // safe to load ads now - } - })); ----- `AdConfig` also carries the global compliance flags every network requires: test mode, test device ids, child directed treatment, under-age-of-consent @@ -132,12 +106,6 @@ unit ids while developing. `BannerAd` is a regular Codename One component. Add it to a form (typically anchored at the top or bottom) and call `load()`: -[source,java] ----- -BannerAd banner = new BannerAd("ca-app-pub-xxx/yyy"); -form.add(BorderLayout.SOUTH, banner); -banner.load(); ----- The default `SIZE_ADAPTIVE` requests an anchored adaptive banner sized to the available width, which is the recommended modern banner type. @@ -147,40 +115,16 @@ available width, which is the recommended modern banner type. Interstitials are event driven - load, then show when loaded, and preload the next one when the current ad is dismissed: -[source,java] ----- -InterstitialAd ad = new InterstitialAd("ca-app-pub-xxx/yyy"); -ad.setAdListener(new AdListener() { - public void onLoaded() { ad.show(); } - public void onDismissed() { ad.load(); } // preload the next -}); -ad.load(); ----- You can also let Codename One show an interstitial automatically on screen transitions, no more often than a given interval: -[source,java] ----- -AdManager.bindInterstitialOnTransition(new InterstitialAd("ca-app-pub-xxx/yyy"), 60000); ----- === Rewarded and rewarded interstitial ads Register a reward listener and grant the reward when it fires. For valuable rewards, verify server side rather than trusting the client: -[source,java] ----- -RewardedAd ad = new RewardedAd("ca-app-pub-xxx/yyy"); -ad.setServerSideVerificationOptions(new ServerSideVerificationOptions(userId, "level=7")); -ad.setAdListener(new AdListener() { - public void onLoaded() { - ad.show(reward -> grantCoins(reward.getAmount())); - } -}); -ad.load(); ----- `RewardedInterstitialAd` has the same API but is shown on a transition rather than opt-in. @@ -190,10 +134,6 @@ than opt-in. App open ads are shown while the app is brought to the foreground. Let the manager and provider handle the foreground hook and freshness window for you: -[source,java] ----- -AdManager.enableAppOpenAds(new AppOpenAd("ca-app-pub-xxx/yyy")); ----- === Native ads @@ -204,14 +144,6 @@ chat or a search results screen - where a banner feels bolted on but a row styled like the others fits in. The ad must still be clearly labelled (for example "Sponsored"): -[source,java] ----- -if (NativeAdLoader.isSupported()) { - new NativeAdLoader("ca-app-pub-xxx/yyy").load(null, - ad -> feed.addComponent(buildSponsoredRow(ad)), // your own layout - err -> Log.p(err.toString())); -} ----- `NativeAd` exposes the headline, body, call-to-action, advertiser and rating that you bind to your own components. Native ad support is an optional provider @@ -226,8 +158,7 @@ standard build hints in your project's `codenameone_settings.properties`: [source,properties] ---- -codename1.arg.android.xapplication= -codename1.arg.ios.plistInject=GADApplicationIdentifierca-app-pub-XXXXXXXX~YYYYYYYY +include::../demos/common/src/main/snippets/developer-guide/advertising.properties[tag=advertising-properties-001,indent=0] ---- The SDK dependencies themselves (the GMA pod / Gradle dependency and the @@ -243,9 +174,4 @@ method. No framework or build-tool changes are required. For tests and screenshots the framework ships a deterministic, network-free provider, `MockAdProvider` (in `cn1-ads-mock`), that renders fixed, labelled ads -with stable sizes and colours. Enable it the same way: - -[source,java] ----- -MockAdProvider.install(); ----- +with stable sizes and colours. diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc index 3aa719bc891..0d5ffb29561 100644 --- a/docs/developer-guide/Ai-And-Speech.asciidoc +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -38,26 +38,7 @@ not change when you swap providers: [source,java] ---- -import com.codename1.ai.*; - -// OpenAI (also drives Ollama, vLLM, llama.cpp, and other -// OpenAI-compatible endpoints). -LlmClient openai = LlmClient.openai(apiKey); - -// Local Ollama on the default port (http://localhost:11434). -LlmClient ollama = LlmClient.ollama("llama3.2"); - -// Any OpenAI-compatible endpoint (Together, Groq, Fireworks, vLLM, ...). -LlmClient together = LlmClient.localOpenAiCompatible( - "https://api.together.xyz/v1", - apiKey, - "meta-llama/Llama-3.3-70B-Instruct-Turbo"); - -// Anthropic and Google Gemini, both via their OpenAI-compatible -// endpoints. The wire format is identical; only the base URL, -// default model, and auth differ. -LlmClient anthropic = LlmClient.anthropic(apiKey); -LlmClient gemini = LlmClient.gemini(apiKey); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava001Snippet.java[tag=ai-and-speech-java-001,indent=0] ---- All five factories return the same `LlmClient` API. OpenAI, Ollama, @@ -75,22 +56,6 @@ target per provider (`gpt-4o-mini`, `claude-sonnet-4-5`, `ChatRequest` is a builder. The bare minimum is a model identifier and one user message: -[source,java] ----- -ChatRequest req = ChatRequest.builder() - .model("gpt-4o-mini") - .addMessage(ChatMessage.system("Reply in haiku.")) - .addMessage(ChatMessage.user("Describe a Codename One app.")) - .temperature(0.7f) - .maxTokens(200) - .build(); - -openai.chat(req).ready(resp -> { - Dialog.show("Reply", resp.getText(), "OK", null); -}).except(err -> { - Log.e(err); -}); ----- `ChatResponse.getText()` concatenates every text part of the assistant message. `getFinishReason()` returns one of `"stop"`, `"length"`, @@ -105,25 +70,6 @@ a `StreamingListener` on the EDT. The returned `AsyncResource` resolves to the final aggregated `ChatResponse` when the stream completes; calling `cancel()` on the resource closes the underlying socket: -[source,java] ----- -StringBuilder buffer = new StringBuilder(); - -openai.chatStream(req, new StreamingListener.Adapter() { - @Override - public void onContentDelta(String delta) { - buffer.append(delta); - chatView.appendToLastMessage(delta); - } - - @Override - public void onError(Throwable t) { - Log.e(t); - } -}).ready(resp -> { - Log.p("Total tokens: " + resp.getUsage().getTotalTokens()); -}); ----- The decoder reassembles fragmented SSE deltas before invoking the listener, so `onContentDelta` always receives a complete token chunk. @@ -138,33 +84,6 @@ on the first fragment and `id` is present on the first call. and the handler returns the JSON result that gets fed back into the conversation: -[source,java] ----- -Tool weather = new Tool( - "get_weather", - "Return the current weather for a city.", - "{\"type\":\"object\",\"properties\":{" + - "\"city\":{\"type\":\"string\"}}," + - "\"required\":[\"city\"]}", - argumentsJson -> { - Map args = JsonHelper.parseObject(argumentsJson); - return "{\"tempC\": 22, \"city\": \"" + args.get("city") + "\"}"; - }); - -ChatRequest req = ChatRequest.builder() - .model("gpt-4o-mini") - .addMessage(ChatMessage.user("What is the weather in Tel Aviv?")) - .tools(Collections.singletonList(weather)) - .toolChoice(ToolChoice.AUTO) - .build(); - -openai.chat(req).ready(resp -> { - for (ToolCall call : resp.getToolCalls()) { - String result = call.execute(Collections.singletonList(weather)); - // Feed the tool result back as a new turn and call chat() again. - } -}); ----- `ToolChoice` covers the four standard modes: @@ -179,33 +98,12 @@ Constrain a response to JSON with `ResponseFormat.JSON_OBJECT`. The client adds the provider-specific flag, and `ChatResponse.getText()` returns a string the runtime can hand to `JSONParser`: -[source,java] ----- -ChatRequest req = ChatRequest.builder() - .model("gpt-4o-mini") - .responseFormat(ResponseFormat.JSON_OBJECT) - .addMessage(ChatMessage.system( - "Return a JSON object with keys city and population.")) - .addMessage(ChatMessage.user("Tel Aviv")) - .build(); ----- ==== Multi-modal messages Attach an image to a user message by adding an `ImagePart`. The part accepts either inline bytes plus a MIME type or a remote HTTPS URL: -[source,java] ----- -ImagePart photo = new ImagePart(receiptBytes, "image/jpeg"); -ChatMessage msg = ChatMessage.userWithImage( - "Extract the line items from this receipt.", photo); - -ChatRequest req = ChatRequest.builder() - .model("gpt-4o") - .addMessage(msg) - .build(); ----- ==== Embeddings @@ -213,20 +111,6 @@ ChatRequest req = ChatRequest.builder() the vectors for semantic search, deduplication, or downstream clustering: -[source,java] ----- -EmbeddingRequest req = EmbeddingRequest.builder() - .model("text-embedding-3-small") - .addInput("a cat sat on the mat") - .addInput("a feline rested on the rug") - .build(); - -openai.embed(req).ready(resp -> { - float[] v0 = resp.getData().get(0).getVector(); - float[] v1 = resp.getData().get(1).getVector(); - // Compute cosine similarity, persist to Storage, etc. -}); ----- ==== Image generation @@ -234,19 +118,6 @@ openai.embed(req).ready(resp -> { DALL-E 3 today; the on-device factory routes through the optional `cn1-ai-stablediffusion` cn1lib (when present in the consumer project): -[source,java] ----- -ImageGenerator gen = ImageGenerator.openai(apiKey); -GenerateImageRequest req = new GenerateImageRequest( - "A pastel watercolor of a Tel Aviv beach at sunset"); -req.setSize("1024x1024"); -req.setQuality("hd"); - -gen.generate(req).ready(image -> { - Label preview = new Label(image); - Display.getInstance().getCurrent().add(preview).revalidate(); -}); ----- DALL-E 3 supports `count = 1` only. Larger batches require a different underlying model; pass it via `setModel(...)`. @@ -256,18 +127,6 @@ underlying model; pass it via `setModel(...)`. `ConversationStore` wraps `Storage` to serialize a list of `ChatMessage` values to JSON under a named key: -[source,java] ----- -ConversationStore store = new ConversationStore("chat-history"); -List history = store.load(); // empty list on first call - -history.add(ChatMessage.user("Hello")); -ChatResponse resp = openai.chat( - ChatRequest.builder().model("gpt-4o-mini").messages(history).build()) - .get(); // blocking helper, EDT-safe -history.add(resp.getAssistantMessage()); -store.save(history); ----- `Tokenizer.estimateMessages(history)` returns a rough token count so you can trim the oldest turns before the conversation outgrows the model's @@ -279,16 +138,6 @@ context window. placeholders pass through unchanged so partially-filled templates are safe to log: -[source,java] ----- -PromptTemplate t = PromptTemplate.of( - "Translate the following from {source} to {target}: {text}"); -t.put("source", "English"); -t.put("target", "French"); -t.put("text", "good morning"); - -ChatMessage user = t.asUser(); // wraps the rendered string ----- ==== Retry policy @@ -333,14 +182,6 @@ can extract. Fetch the key from a server endpoint that the user authenticates against, then cache it locally with the non-prompting `SecureStorage` overloads: -[source,java] ----- -SecureStorage store = SecureStorage.getInstance(); -store.set("openai.key", apiKeyFromServer); // returns false when unsupported - -String key = store.get("openai.key"); // returns null when absent -LlmClient client = LlmClient.openai(key); ----- === The `ChatView` component @@ -356,34 +197,6 @@ The component exposes thread-safe `addMessage`, `appendToLastMessage`, and `setTypingIndicatorVisible` methods, so background callbacks from `chatStream(...)` can mutate the view directly: -[source,java] ----- -Form chat = new Form("Assistant", new BorderLayout()); -ChatView view = new ChatView(); -chat.add(BorderLayout.CENTER, view); - -view.addMessage(ChatMessage.assistant("How can I help?")); - -view.setOnSend(e -> { - String text = view.getInput().getText(); - view.getInput().clear(); - view.addMessage(ChatMessage.user(text)); - view.setTypingIndicatorVisible(true); - - ChatBubble streaming = view.beginAssistantStream(); - ChatRequest req = ChatRequest.builder() - .model("gpt-4o-mini") - .messages(view.getHistory()) - .build(); - - LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { - @Override public void onContentDelta(String d) { - view.appendToLastMessage(d); - } - }).ready(resp -> view.setTypingIndicatorVisible(false)); -}); -chat.show(); ----- `ChatBubble` and `ChatInput` are public, so subclass them for custom rendering. Override `ChatView.createBubble(message)` to swap in a @@ -422,12 +235,6 @@ history, dispatches the response into the view, and updates the typing indicator. Use it for prototypes, or as a reference implementation when you need a custom send pipeline: -[source,java] ----- -LlmChatBinding.bind(view, - LlmClient.openai(apiKey), - ChatRequest.builder().model("gpt-4o-mini").build()); ----- === Speech recognition and TextToSpeech @@ -438,32 +245,6 @@ on every platform. The default implementation returns `false` from ==== `SpeechRecognizer` -[source,java] ----- -import com.codename1.media.*; - -if (!SpeechRecognizer.isSupported()) { - Dialog.show("Unavailable", "Speech is not supported on this device.", - "OK", null); - return; -} - -RecognitionOptions opts = new RecognitionOptions() - .setLanguageTag("en-US") - .setPartialResults(true) - .setContinuous(false) - .setMaxResults(3); - -SpeechRecognizer.recognize(opts, new RecognitionCallback.Adapter() { - @Override public void onPartialResult(String transcript) { - chatView.getInput().setText(transcript); - } - @Override public void onResult(String transcript, float confidence, - String[] alternatives) { - chatView.addMessage(ChatMessage.user(transcript)); - } -}); ----- iOS uses `SFSpeechRecognizer`, Android uses `android.speech.SpeechRecognizer`, and the JavaSE simulator stays unsupported unless the optional @@ -474,12 +255,7 @@ active session early; partial results stop firing immediately. [source,java] ---- -if (TextToSpeech.isSupported()) { - TtsOptions opts = new TtsOptions() - .setLanguageTag("fr-FR") - .setRate(1.0f); - TextToSpeech.speak("Bonjour", opts); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AiAndSpeechJava015Snippet.java[tag=ai-and-speech-java-015,indent=0] ---- iOS uses `AVSpeechSynthesizer`, Android uses @@ -521,50 +297,14 @@ pulls in the per-platform classifier jars: [source,xml] ---- - - com.codenameone - cn1-ai-mlkit-barcode-lib - ${cn1.version} - pom - +include::../demos/common/src/main/snippets/developer-guide/ai-and-speech.xml[tag=ai-and-speech-xml-001,indent=0] ---- ==== Example: Scanning a barcode -[source,java] ----- -import com.codename1.ai.mlkit.barcode.BarcodeScanner; - -Capture.capturePhoto(new ActionListener() { - @Override public void actionPerformed(ActionEvent evt) { - String path = (String) evt.getSource(); - try (InputStream in = FileSystemStorage.getInstance().openInputStream(path)) { - byte[] bytes = Util.readInputStream(in); - BarcodeScanner.scan(bytes).ready(values -> { - for (String v : values) { - Log.p("Detected: " + v); - } - }); - } catch (IOException ex) { - Log.e(ex); - } - } -}); ----- ==== Example: Face detection -[source,java] ----- -import com.codename1.ai.mlkit.face.FaceDetector; - -FaceDetector.detect(jpegBytes).ready(rects -> { - for (int i = 0; i < rects.length; i += 4) { - Log.p("Face at (" + rects[i] + "," + rects[i + 1] - + ") size " + rects[i + 2] + "x" + rects[i + 3]); - } -}); ----- NOTE: ML Kit barcode and face detection ship as small Pods or Gradle dependencies that add a few megabytes to the binary. The document @@ -577,33 +317,6 @@ The pieces compose well. The following loop captures a photo, extracts text with the multi-modal `gpt-4o` model, speaks the result, and streams the same text into a `ChatView`: -[source,java] ----- -Capture.capturePhoto(evt -> { - String path = (String) evt.getSource(); - byte[] bytes = readAllBytes(path); - ImagePart img = new ImagePart(bytes, "image/jpeg"); - - ChatRequest req = ChatRequest.builder() - .model("gpt-4o") - .addMessage(ChatMessage.userWithImage( - "Describe the photo in one sentence.", img)) - .build(); - - chatView.addMessage(req.getMessages().get(0)); - ChatBubble streaming = chatView.beginAssistantStream(); - StringBuilder full = new StringBuilder(); - - LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { - @Override public void onContentDelta(String d) { - full.append(d); - chatView.appendToLastMessage(d); - } - }).ready(resp -> { - TextToSpeech.speak(full.toString()); - }); -}); ----- The same code runs unchanged in the simulator when Ollama is detected, on Android with the multi-modal native client, and on iOS through the diff --git a/docs/developer-guide/Analytics.asciidoc b/docs/developer-guide/Analytics.asciidoc index 5dd7d284aab..d6c4fadcd08 100644 --- a/docs/developer-guide/Analytics.asciidoc +++ b/docs/developer-guide/Analytics.asciidoc @@ -10,30 +10,9 @@ NOTE: The older `AnalyticsService` class still works but is deprecated. It now d Register providers once, early in your app's lifecycle. Providers are plain objects -- there is no reflection or service lookup, which keeps the mechanism safe under the obfuscation applied to release builds. -[source,java] ----- -// Codename One's first-party service (reports into the cloud console) -Analytics.addProvider(new CodenameOneAnalyticsProvider()); - -// or a third-party backend, side by side -Analytics.addProvider(new GoogleAnalyticsProvider("G-XXXXXXXX", "API_SECRET")); ----- Once at least one provider is registered, report usage from anywhere in your app: -[source,java] ----- -Analytics.screen("Home", null); - -Analytics.event(AnalyticsEvent.create("purchase") - .category("commerce") - .param("sku", "abc-123") - .param("value", 9.99) - .build()); - -Analytics.setUserProperty("plan", "pro"); -Analytics.crash(throwable, "checkout failed", false); ----- `Analytics.crash` emits a lightweight exception event (for example GA4's `app_exception`) to whichever analytics backends you have registered, so exception counts appear next to your usage metrics. It's separate from Codename One Crash Protection (`com.codename1.crash.CrashProtection`), which is a dedicated pipeline that records full, symbolicated stack traces to the build cloud crash console for debugging. The two are independent: use Crash Protection to diagnose crashes, use `Analytics.crash` when you also want exception events in your analytics, and enable either or both. @@ -42,24 +21,11 @@ Analytics.crash(throwable, "checkout failed", false); The API is designed to help you comply with privacy regulations such as GDPR and CCPA. A `ConsentMode` controls what happens before the user makes an explicit choice. The default is `OPT_IN`: nothing is collected or transmitted until the application records consent. -[source,java] ----- -// Nothing is sent until this is called (opt-in is the default). -Analytics.setConsent(AnalyticsConsent.all()); ----- `AnalyticsConsent.all()` grants every category and `AnalyticsConsent.none()` denies every category. The older `granted()` and `denied()` methods remain as aliases. Consent is broken down by category, so you can honor granular choices -- for example allowing crash reporting while declining behavioral analytics: -[source,java] ----- -Analytics.setConsent(AnalyticsConsent.builder() - .analytics(true) - .crashReporting(true) - .personalization(false) - .build()); ----- The consent choice is persisted, so it survives restarts. Screen views and events require the `analytics` category, crash reports require `crashReporting`, and `setUserId` requires `personalization`. Calls made without the matching consent are dropped. @@ -67,24 +33,12 @@ The consent choice is persisted, so it survives restarts. Screen views and event If your application has already obtained consent through its own mechanism -- a custom prompt, a third-party consent-management platform, an enterprise device-management policy, or a jurisdiction where you have determined consent isn't required -- record it once at startup so reporting flows without a second prompt: -[source,java] ----- -Analytics.setConsent(AnalyticsConsent.all()); ----- You can also seed every category and then revoke just one with the builder, for example to allow analytics but not ad storage: -[source,java] ----- -Analytics.setConsent(AnalyticsConsent.builder().all().adStorage(false).build()); ----- If you would rather not gate at all, switch the default so collection is active unless the user withdraws it. This places the compliance responsibility on you as the integrator: -[source,java] ----- -Analytics.setConsentMode(ConsentMode.OPT_OUT); ----- Note that the service records pseudonymous data rather than anonymous data. The client id described below is a persistent identifier stored on the device; it isn't tied to a real-world identity and the server discards full IP addresses, but storing and reading such an identifier for usage tracking is often treated as consent-requiring under GDPR and similar regimes. That's why opt-in is the safe default. Relax it only when you are confident the change fits your use. @@ -92,10 +46,6 @@ Note that the service records pseudonymous data rather than anonymous data. The Each device is identified by a pseudonymous client id. It's generated on first use and stored locally -- it isn't derived from any hardware identifier. To honor an erasure request ("right to be forgotten"), reset it: -[source,java] ----- -Analytics.resetClientId(); ----- For the first-party service this can be paired with a server-side deletion of the previous id's data. @@ -131,10 +81,6 @@ You can add your own events at any time with `Analytics.event(...)`; these built `CodenameOneAnalyticsProvider` batches events and posts them to the Codename One cloud, which aggregates them into the reports shown in the developer console. App identity is read from the build-injected properties, so no API key is embedded in your app. -[source,java] ----- -Analytics.addProvider(new CodenameOneAnalyticsProvider()); ----- The capabilities you actually get -- screen views, custom events, user properties, retention window, raw export -- are gated server-side by your subscription tier. Higher tiers retain data longer and unlock richer reporting. @@ -142,10 +88,6 @@ The capabilities you actually get -- screen views, custom events, user propertie `GoogleAnalyticsProvider` uses the GA4 Measurement Protocol. Create it with a measurement id (`G-XXXXXXXX`) and a Measurement Protocol API secret from the GA4 admin console: -[source,java] ----- -Analytics.addProvider(new GoogleAnalyticsProvider("G-XXXXXXXX", "API_SECRET")); ----- Screen views are sent as the GA4 `screen_view` event and crashes as `app_exception`. @@ -153,19 +95,11 @@ Screen views are sent as the GA4 `screen_view` event and crashes as `app_excepti `MatomoAnalyticsProvider` targets Matomo (formerly Piwik) through its HTTP tracking API. Matomo can be self-hosted and supports IP anonymization, which makes it a good fit for privacy-sensitive deployments: -[source,java] ----- -Analytics.addProvider(new MatomoAnalyticsProvider("https://matomo.example.com", 1)); ----- ==== Firebase `FirebaseAnalyticsProvider` forwards to the native Firebase Analytics SDK on Android and iOS. Register it like any other provider: -[source,java] ----- -Analytics.addProvider(new FirebaseAnalyticsProvider()); ----- Firebase reports only from a native Android or iOS build, and only when two things are in place. Without both, `FirebaseAnalyticsProvider` does nothing -- which is why it's always safe to register, including in the Codename One simulator and the desktop build, where it's a no-op. @@ -192,34 +126,11 @@ WARNING: You need both the configuration file and the matching build hint. If ei `LoggingAnalyticsProvider` logs every call instead of sending it anywhere. It's the recommended default in the simulator and is used as the backing provider in tests. -[source,java] ----- -Analytics.addProvider(new LoggingAnalyticsProvider()); ----- === Writing your own provider Implement `AnalyticsProvider`, or extend `AbstractAnalyticsProvider` and override only the calls you support. The `Analytics` class handles consent gating and fan-out for you, so a provider just forwards the data to its backend. -[source,java] ----- -public class MyProvider extends AbstractAnalyticsProvider { - @Override - public String getName() { - return "my-backend"; - } - - @Override - public void trackScreen(String name, String referrer) { - // send to your backend - } - - @Override - public boolean supports(AnalyticsCapability capability) { - return capability == AnalyticsCapability.SCREEN_VIEWS; - } -} ----- The `init(AnalyticsContext)` callback hands you the app name, version, platform, locale and the pseudonymous client id. `onConsentChanged(AnalyticsConsent)` lets a provider reconfigure when the user updates consent. `supports(AnalyticsCapability)` lets tooling introspect which features a provider offers. diff --git a/docs/developer-guide/Animations.asciidoc b/docs/developer-guide/Animations.asciidoc index 5be49d24bfc..ed512c7722d 100644 --- a/docs/developer-guide/Animations.asciidoc +++ b/docs/developer-guide/Animations.asciidoc @@ -298,36 +298,36 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/animations .The slide transition moves both incoming and outgoing forms together -image::img/transition-slide.jpg[The slide transition moves both incoming and outgoing forms together,scaledwidth=70%] +image::img/transition-slide.png[The slide transition moves both incoming and outgoing forms together,scaledwidth=70%] .The slide transition can be applied vertically as well -image::img/transition-slide-vertical.jpg[The slide transition can be applied vertically as well,scaledwidth=70%] +image::img/transition-slide-vertical.png[The slide transition can be applied vertically as well,scaledwidth=70%] .Slide fade fades in the destination title while sliding the content pane it's the default on iOS -image::img/transition-slide-fade.jpg[Slide fade fades in the destination title while sliding the content pane its the default on iOS,scaledwidth=70%] +image::img/transition-slide-fade.png[Slide fade fades in the destination title while sliding the content pane its the default on iOS,scaledwidth=70%] TIP: `SlideFade` is problematic without a title area. If you have a `Form` that lacks a title area you would recommend to disable `SlideFade` at least for that `Form`. .With cover transitions the source form stays in place as it's covered by the destination. This transition can be played both horizontally and vertically -image::img/transition-cover.jpg[With cover transitions the source form stays in place as it's covered by the destination. This transition can be played both horizontally and vertically,scaledwidth=70%] +image::img/transition-cover.png[With cover transitions the source form stays in place as it's covered by the destination. This transition can be played both horizontally and vertically,scaledwidth=70%] .Uncover is the inverse of cover. The destination form stays in place while the departing form moves away -image::img/transition-uncover.jpg[Uncover is the inverse of cover. The destination form stays in place while the departing form moves away,scaledwidth=70%] +image::img/transition-uncover.png[Uncover is the inverse of cover. The destination form stays in place while the departing form moves away,scaledwidth=70%] ==== Fade and flip transitions The fade transition is pretty trivial and accepts a time value since it has no directional context. .Fade transition is probably the simplest one around -image::img/transition-fade.jpg[Fade transition is probably the simplest one around,scaledwidth=70%] +image::img/transition-fade.png[Fade transition is probably the simplest one around,scaledwidth=70%] The https://www.codenameone.com/javadoc/com/codename1/ui/animations/FlipTransition.html[FlipTransition] is also pretty simple but unlike the others it isn't a part of the `CommonTransitions`. It has its own `FlipTransition` class. IMPORTANT: This transition looks different on devices as it uses native perspective transforms available there .Fade transition is probably the simplest one around -image::img/transition-flip.jpg[Fade transition is probably the simplest one around,scaledwidth=70%] +image::img/transition-flip.png[Fade transition is probably the simplest one around,scaledwidth=70%] ==== Bubble transition @@ -392,14 +392,6 @@ the source during the animation doesn't carry the original parent's clip. To opt into the image-snapshot path call `snapshotMode(true)` on the builder: -[source,java] ----- -MorphTransition morph = MorphTransition.create(300) - .snapshotMode(true) - .morph("card"); -nextForm.setTransitionInAnimator(morph); -nextForm.show(); ----- `snapshotMode(true)` captures each `(source, dest)` pair as a clipped `Image` at `initTransition()`, then the tween draws those images at the @@ -425,22 +417,6 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/animations // vale-skip: Microsoft.Adverbs: "lazily" describes the lazy-evaluation semantics of LazyValue, a precise CS term, not a softener. That one command will enable swiping back from `currentForm`. https://www.codenameone.com/javadoc/com/codename1/util/LazyValue.html[LazyValue] allows you to pass a value lazily: -[source,java] ----- -/** - * Useful when passing a value that might not exist to a function, e.g. When we - * pass a form that we might need to construct dynamically later on. - */ -public interface LazyValue { - /** - * Returns the actual value. - * - * @param args optional arguments for the creation of the lazy value - * @return the value - */ - T get(Object... args); -} ----- This effectively allows you to pass a form and create it as necessary (for example, for a GUI builder app you don't have the actual previous form instance), notice that the arguments aren't used for this case but will be used in diff --git a/docs/developer-guide/Annotation-Component-Binding.asciidoc b/docs/developer-guide/Annotation-Component-Binding.asciidoc index 8346dac0b1a..4c2dddce86e 100644 --- a/docs/developer-guide/Annotation-Component-Binding.asciidoc +++ b/docs/developer-guide/Annotation-Component-Binding.asciidoc @@ -14,34 +14,7 @@ It's a thin alternative to the imperative `UiBinding` API [source,java] ---- -package com.example; - -import com.codename1.annotations.*; -import com.codename1.binding.BindAttr; -import com.codename1.properties.Property; - -@Bindable -public class LoginModel { - - @Bind(name = "userField", attr = BindAttr.TEXT) - private String user; - public String getUser() { return user; } - public void setUser(String u) { this.user = u; } // <1> - - @Bind(name = "rememberMe", attr = BindAttr.SELECTED) - public boolean remember; // <2> - - @Bind(name = "banner", attr = BindAttr.UIID, twoWay = false) - public String bannerStyle; - - @Bind(name = "fullName", - attr = BindAttr.TEXT, - getter = "computeFullName", - setter = "applyFullName") // <3> - private String fullName; - public String computeFullName() { return fullName.toUpperCase(); } - public void applyFullName(String f){ this.fullName = f.trim(); } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationComponentBindingJava001Snippet.java[tag=annotation-component-binding-java-001,indent=0] ---- <1> JavaBeans `getUser` / `setUser` are detected automatically when the field is private. The processor instruments `setUser` to fire a @@ -77,26 +50,6 @@ The default lookup matches GUI-builder names exactly. === Bind at runtime -[source,java] ----- -import com.codename1.binding.Binders; -import com.codename1.binding.Binding; - -Form f = (Form) Resources.getGlobalResources().getForm("LoginForm"); -LoginModel model = new LoginModel(); -Binding b = Binders.bind(model, f); - -// The two-way bindings push every keystroke / toggle back into the model. -// Mutate the model through the setter and the bound component refreshes -// automatically: -model.setUser("alice"); - -// Or pull pending edits into the model before submit: -b.commit(); - -// On form dispose: -b.disconnect(); // remove every installed listener ----- `Binding` is the handle the binder returns: @@ -156,13 +109,6 @@ For every two-way `@Bind` field whose write accessor is a method `cn1:process-annotations` Mojo reads the original `.class` file with ASM and inserts the equivalent of: -[source,java] ----- -public void setName(String name) { - this.name = name; - com.codename1.binding.Binders.notifyChanged(this); // injected -} ----- The injection lands before every `return` point of the setter. The instrumented setter is written back to `target/classes`, replacing the @@ -216,76 +162,9 @@ Multiple annotations on the same field are combined into a `GroupConstraint` (first failure wins), matching the behaviour of `Validator.addConstraint(Component, Constraint...)`. -[source,java] ----- -package com.example; - -import com.codename1.annotations.*; -import com.codename1.binding.BindAttr; - -@Bindable -public class SignupModel { - - @Bind(name = "userField") - @Required - @Length(min = 3, message = "User name too short") - private String user; - - @Bind(name = "emailField") - @Required - @Email // <1> - private String email; - - @Bind(name = "ageField") - @Numeric(min = 13, max = 120, message = "Age 13-120") // <2> - private String age; // <3> - - @Bind(name = "roleField") - @ExistIn({ "admin", "editor", "viewer" }) - private String role; - - @Bind(name = "phoneField") - @Validate(PhoneConstraint.class) // <4> - private String phone; - - // ... getters / setters omitted for brevity -} ----- -<1> `@Required @Email` is the canonical "mandatory address" pair. The - standard email regex accepts the empty string as "not yet an - address," so `@Required` is what enforces presence. -<2> Inclusive bounds. Omit either side to remove that side of the - range (defaults are negative / positive infinity). -<3> The field's static type is `String` because the user types the - digits into a `TextField` -- the binder runs `Integer.parseInt` - only when committing the value back through the JavaBeans setter - for an `int` target. -<4> `PhoneConstraint` is a hand-written - `com.codename1.ui.validation.Constraint` with a public no-argument - constructor. The generated binder instantiates it once per - `bind()` call. After binding, drive validation through the returned handle: -[source,java] ----- -LoginModel model = new LoginModel(); -Binding b = Binders.bind(model, form); - -// Auto-disable a submit button until everything is valid. -Button submit = (Button) form.findByName("submitButton"); -b.getValidator().addSubmitButtons(submit); - -// Live feedback as the user types (static toggle on the Validator -// class -- one switch flips the behaviour for every validator in the -// app): -Validator.setValidateOnEveryKey(true); - -// Programmatic gate before saving: -if (b.getValidator().isValid()) { - repository.save(model); -} ----- `Binding#getValidator()` never returns `null` -- when the model has no validation annotations the validator is empty and `isValid()` returns diff --git a/docs/developer-guide/Annotation-JSON-XML-Mapping.asciidoc b/docs/developer-guide/Annotation-JSON-XML-Mapping.asciidoc index a3c97a77f1d..f53e1a99c5b 100644 --- a/docs/developer-guide/Annotation-JSON-XML-Mapping.asciidoc +++ b/docs/developer-guide/Annotation-JSON-XML-Mapping.asciidoc @@ -22,28 +22,7 @@ or any mix: [source,java] ---- -package com.example; - -import com.codename1.annotations.*; -import com.codename1.properties.*; - -@Mapped -@XmlRoot("user") -public class User { - - @JsonProperty("first_name") - @XmlElement("first") - public String firstName; - - public int age; - - // Renders as in XML; omitted from JSON. - @XmlAttribute - @JsonIgnore - public String role; - - public User() { } // <1> -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationJsonXmlMappingJava001Snippet.java[tag=annotation-json-xml-mapping-java-001,indent=0] ---- <1> Every `@Mapped` class must declare a public no-arg constructor -- the generated mapper calls it from `fromJson` / `fromXml`. @@ -53,21 +32,6 @@ For `PropertyBusinessObject`-style classes the same annotations work on `Property#set` so subscribers (`addChangeListener`) still see the mutation: -[source,java] ----- -@Mapped -public class Item implements PropertyBusinessObject { - public final Property name = new Property<>("name"); - public final Property qty = new Property<>("qty"); - - private final PropertyIndex idx = - new PropertyIndex(this, "Item", name, qty); - - public PropertyIndex getPropertyIndex() { return idx; } - - public Item() { } -} ----- ==== Field-level annotations @@ -101,24 +65,6 @@ public class Item implements PropertyBusinessObject { === Round-trip -[source,java] ----- -import com.codename1.mapping.Mappers; - -User u = new User(); -u.firstName = "Alice"; -u.age = 30; - -String json = Mappers.toJson(u); -// -> {"first_name":"Alice","age":30} - -User restored = Mappers.fromJson(json, User.class); - -String xml = Mappers.toXml(u); -// -> Alice30 - -User fromXml = Mappers.fromXml(xml, User.class); ----- `Mappers.fromJson` and `Mappers.fromXml` both accept either a `String` or a `java.io.Reader`, so streamed responses (network, file, resource) @@ -169,27 +115,6 @@ persisted across builds. Sometimes a class lives in a third-party JAR the build can't annotate. Hand-write a `Mapper` and register it at startup: -[source,java] ----- -Mappers.register(new Mapper() { - public Class type() { return UUID.class; } - public Map toMap(UUID u) { - Map m = new LinkedHashMap<>(); - m.put("uuid", u.toString()); - return m; - } - public UUID fromMap(Map m) { - return UUID.fromString((String) m.get("uuid")); - } - public String xmlRootName() { return "uuid"; } - public void writeXml(UUID u, Element root) { - root.addChild(new Element(u.toString(), true)); - } - public UUID readXml(Element root) { - return UUID.fromString(textOf(root)); - } -}); ----- Hand-written mappers take precedence over generated ones for the same class. diff --git a/docs/developer-guide/Annotation-SQLite-ORM.asciidoc b/docs/developer-guide/Annotation-SQLite-ORM.asciidoc index d1e48ca1543..1859d541cb1 100644 --- a/docs/developer-guide/Annotation-SQLite-ORM.asciidoc +++ b/docs/developer-guide/Annotation-SQLite-ORM.asciidoc @@ -18,28 +18,7 @@ by side. [source,java] ---- -package com.example; - -import com.codename1.annotations.*; - -@Entity(table = "users") -public class User { - - @Id(autoIncrement = true) - public long id; - - @Column(name = "full_name", nullable = false) - public String name; - - public int age; - - public java.util.Date createdAt; - - @DbTransient - public String cacheKey; // <1> - - public User() { } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AnnotationSqliteOrmJava001Snippet.java[tag=annotation-sqlite-orm-java-001,indent=0] ---- <1> Excluded from the generated table. @@ -67,55 +46,11 @@ public class User { === Use the dao -[source,java] ----- -import com.codename1.orm.EntityManager; -import com.codename1.orm.Dao; - -EntityManager em = EntityManager.open("MyApp.db"); -Dao users = em.dao(User.class); - -users.createTable(); // <1> - -User u = new User(); -u.name = "Alice"; -u.age = 30; -users.insert(u); // <2> - -User found = users.findById(u.id); // <3> - -for (User x : users.find("age > ?", 18)) { // <4> - // ... -} - -u.age = 31; -users.update(u); // <5> -users.delete(u); -em.close(); ----- -<1> Idempotent -- `CREATE TABLE IF NOT EXISTS`. -<2> The autoincrement `id` is filled in via - `SELECT last_insert_rowid()` after the insert. -<3> `findById` returns `null` when no row matches. -<4> Free-form `WHERE` clause; positional `?` parameters bind in order. -<5> `update` and `delete` key off the entity's `@Id` value. `EntityManager` is a thin façade over `Database`. The underlying connection is reachable through `em.database()` for raw SQL when the dao surface isn't enough. Transactions: -[source,java] ----- -em.beginTransaction(); -try { - users.insert(u1); - users.insert(u2); - em.commitTransaction(); -} catch (IOException e) { - em.rollbackTransaction(); - throw e; -} ----- === Relationships diff --git a/docs/developer-guide/App-Review.asciidoc b/docs/developer-guide/App-Review.asciidoc index ee8b43472b9..de19dbed414 100644 --- a/docs/developer-guide/App-Review.asciidoc +++ b/docs/developer-guide/App-Review.asciidoc @@ -8,10 +8,6 @@ The native prompt is the store-sanctioned way to ask for a review, so prefer it The simplest usage is a single call at a moment that makes sense in your app -- typically right after the user completed something rewarding (finished a level, saved a document, completed an order): -[source,java] ----- -AppReview.getInstance().requestReview(); ----- `requestReview()` shows the native review sheet when the platform supports it. On platforms without a native prompt it shows the built-in rating widget instead. You decide the timing. @@ -19,16 +15,6 @@ AppReview.getInstance().requestReview(); Rather than picking the moment yourself, you can let `AppReview` decide based on simple engagement heuristics: how many times the app was launched, how long ago it was installed, and how long since it last asked. Configure it once (for example in your app's `init` or `start` method) and call `registerSession()` on every launch: -[source,java] ----- -AppReview.getInstance() - .setMinimumLaunches(5) // at least 5 launches - .setMinimumDaysInstalled(3) // and installed at least 3 days - .setDaysBetweenPrompts(30) // never nag more than monthly - .setStoreUrl("https://apps.apple.com/app/id0000000000") - .setSupportEmail("support@example.com") - .registerSession(); ----- `registerSession()` records the launch and, once the thresholds are met and the user hasn't already rated or opted out, prompts. The bookkeeping (launch count, install date, last-prompt time and a completion flag) is stored in https://www.codenameone.com/javadoc/com/codename1/io/Preferences.html[Preferences] so it survives restarts. Once the user rates the app or chooses to stop, `AppReview` stops prompting. Call `reset()` to clear this state. @@ -43,21 +29,6 @@ image::img/app-review-sheet.png[Fallback rating sheet,scaledwidth=30%] By default the low-rating feedback is collected through an e-mail to the address passed to `setSupportEmail(String)`. To deliver feedback through your own backend, register a `FeedbackListener`: -[source,java] ----- -AppReview.getInstance().setFeedbackListener(new FeedbackListener() { - public boolean lowRating(int rating) { - // return true to present your own feedback UI and suppress the - // built-in e-mail composer - return false; - } - - public void feedback(int rating, String text) { - // called with the free text the user typed in the built-in composer - myBackend.submitFeedback(rating, text); - } -}); ----- === How the platform decides diff --git a/docs/developer-guide/Apple-Wallet-Extension.asciidoc b/docs/developer-guide/Apple-Wallet-Extension.asciidoc index 6ee2f16768c..8fb30ff0677 100644 --- a/docs/developer-guide/Apple-Wallet-Extension.asciidoc +++ b/docs/developer-guide/Apple-Wallet-Extension.asciidoc @@ -31,42 +31,18 @@ Add these build hints to the iOS build: [source,properties] ---- -codename1.arg.ios.wallet.extension=true -codename1.arg.ios.wallet.appGroup=group.com.mybank.app -codename1.arg.ios.wallet.issuerEndpoint=https://api.mybank.com/wallet/provision +include::../demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties[tag=apple-wallet-extension-properties-001,indent=0] ---- That generates the non-UI extension, wires it into the Xcode project as an embedded extension target and injects the App Group into the app and extension entitlements. To also generate the login UI extension add: [source,properties] ---- -codename1.arg.ios.wallet.includeUI=true -codename1.arg.ios.wallet.authEndpoint=https://api.mybank.com/wallet/login +include::../demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties[tag=apple-wallet-extension-properties-002,indent=0] ---- In Java, publish the user's cards whenever they change (typically after login) and keep a fresh token published so Wallet can skip the login screen: -[source,java] ----- -import com.codename1.payment.WalletExtension; -import com.codename1.payment.WalletPassEntry; - -if (WalletExtension.isSupported()) { - WalletExtension.setPassEntries(new WalletPassEntry[] { - new WalletPassEntry() - .identifier(card.getPrimaryAccountIdentifier()) - .title("My Bank Debit Card") - .cardholderName(user.getFullName()) - .primaryAccountSuffix(card.getLast4()) - .paymentNetwork("Visa") - .localizedDescription("My Bank Debit Card") - .artPng(cardArt.getImageData()) - }); - WalletExtension.setRemotePassEntries(sameEntries); // Apple Watch list - WalletExtension.setAuthToken(session.getToken()); - WalletExtension.setRequiresAuthentication(false); -} ----- Call `WalletExtension.clear()` on logout. The card art must be a PNG without personally identifiable information (Apple requires square corners and no full card number). The extension automatically filters out cards that are already provisioned on the device or the paired watch. @@ -76,24 +52,14 @@ When the user adds a card, the generated extension POSTs JSON to the `ios.wallet [source,json] ---- -{ - "certificates": ["base64...", "base64..."], - "nonce": "base64...", - "nonceSignature": "base64...", - "cardIdentifier": "the WalletPassEntry identifier", - "authToken": "the token published via setAuthToken" -} +include::../demos/common/src/main/snippets/developer-guide/apple-wallet-extension.json[tag=apple-wallet-extension-json-001,indent=0] ---- The token is also sent as an `Authorization: Bearer` header. Your backend performs the network-specific encryption (this always happens server side - the keys never live on the device) and responds with: [source,json] ---- -{ - "activationData": "base64...", - "encryptedPassData": "base64...", - "ephemeralPublicKey": "base64..." -} +include::../demos/common/src/main/snippets/developer-guide/apple-wallet-extension.json[tag=apple-wallet-extension-json-002,indent=0] ---- For the RSA_V2 scheme return `wrappedKey` instead of `ephemeralPublicKey`. @@ -114,8 +80,7 @@ Cloud device builds sign each extension with its own profile. Either place the ` [source,properties] ---- -codename1.arg.ios.wallet.nonuiProvisioningProfile=WalletNonUI.mobileprovision -codename1.arg.ios.wallet.uiProvisioningProfile=WalletUI.mobileprovision +include::../demos/common/src/main/snippets/developer-guide/apple-wallet-extension.properties[tag=apple-wallet-extension-properties-003,indent=0] ---- Alternatively, host them at URLs the build server can reach and supply `ios.wallet.nonuiProvisioningURL` / `ios.wallet.uiProvisioningURL` instead. Profiles contain no private keys, so bundling them in resources is safe; the build keeps them out of the final app bundle. The build validates that each profile matches your distribution certificate and actually carries the payment-pass-provisioning entitlement, and fails with an actionable message when it doesn't. diff --git a/docs/developer-guide/Authentication-And-Identity.asciidoc b/docs/developer-guide/Authentication-And-Identity.asciidoc index b5e39c76578..927c2c4a55c 100644 --- a/docs/developer-guide/Authentication-And-Identity.asciidoc +++ b/docs/developer-guide/Authentication-And-Identity.asciidoc @@ -28,28 +28,7 @@ If your identity provider publishes a standard `.well-known/openid-configuration [source,java] ---- -import com.codename1.io.oidc.OidcClient; -import com.codename1.io.oidc.OidcTokens; -import com.codename1.util.SuccessCallback; - -OidcClient.discover("https://accounts.google.com").ready(new SuccessCallback() { - public void onSucess(OidcClient client) { - client.setClientId("YOUR_CLIENT_ID") - .setRedirectUri("com.example.app:/oauth2redirect") - .setScopes("openid", "email", "profile"); - client.authorize().ready(new SuccessCallback() { - public void onSucess(OidcTokens tokens) { - // tokens.getAccessToken() -- bearer for API calls - // tokens.getIdToken() -- JWT with user identity claims - // tokens.getEmail() -- convenience accessor - } - }).except(new SuccessCallback() { - public void onSucess(Throwable err) { - System.out.println("Sign-in failed: " + err.getMessage()); - } - }); - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava001Snippet.java[tag=authentication-and-identity-java-001,indent=0] ---- That call: @@ -90,19 +69,6 @@ The `androidx.browser:browser:1.8.0` Gradle dependency (for Custom Tabs) is adde `OidcClient` saves the response under a per-issuer + per-client-ID key using `com.codename1.io.oidc.TokenStore.DefaultStorageTokenStore` (which serializes to `com.codename1.io.Storage`). To restore on next launch: -[source,java] ----- -client.refreshIfExpired(60).ready(new SuccessCallback() { - public void onSucess(OidcTokens tokens) { - if (tokens == null) { - // No saved tokens, or they expired and we have no refresh token. - client.authorize(); - } else { - // Reuse `tokens.getAccessToken()` -- still valid (refreshed if needed). - } - } -}); ----- If the saved access token is within 60 seconds of expiring and a refresh token is available, `refreshIfExpired` performs a refresh-token grant in the background and persists the new tokens. @@ -124,15 +90,7 @@ ios.signinwithapple=true [source,java] ---- -AppleSignIn.getInstance().signIn("name email", new AppleSignInCallback() { - public void onSuccess(AppleSignInResult result) { - // result.getIdentityToken() -- JWT to verify on your server - // result.getUserId() -- stable opaque user id, use as PK - // result.getEmail() -- may be a real or relay address - } - public void onError(String err) { ... } - public void onCancel() { ... } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava003Snippet.java[tag=authentication-and-identity-java-003,indent=0] ---- NOTE: Apple only returns the user's name and email on the *first* authorization. AppleSignIn persists those values in `Preferences` so the result is consistent on subsequent sign-ins. @@ -141,39 +99,18 @@ NOTE: Apple only returns the user's name and email on the *first* authorization. Apple requires a separate *Services ID* (a "web client") for non-iOS environments and a `client_secret` JWT generated by your server. The recipe lives at <>. -[source,java] ----- -AppleSignIn.getInstance() - .withServiceId("com.example.appleweb") - .withRedirectUri("https://example.com/auth/apple/callback") - .withClientSecret(serverGeneratedJwt) - .signIn("name email", callback); ----- [[apple-services-id-setup]] ==== One-time Apple setup . In the Apple Developer portal, create a *Services ID* alongside your App ID. Enable "Sign in with Apple" for both. . Configure the Services ID's *Web Authentication Configuration* with the primary App ID and your redirect URI. -. Generate an *AuthKey* private key in the Apple developer portal (`.p8` file). Apple does not allow public mobile clients to mint the `client_secret` JWT themselves; your backend must do it. See https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens[Apple's docs] for the JWT recipe. +. Generate an *AuthKey* private key in the Apple developer portal (`.p8` file). Apple doesn't allow public mobile clients to mint the `client_secret` JWT themselves; your backend must do it. See https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens[Apple's docs] for the JWT recipe. === Google Sign-In `com.codename1.social.GoogleConnect` now offers a modern `signIn(...)` method that runs entirely through `OidcClient`: -[source,java] ----- -GoogleConnect.getInstance().signIn( - "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com", - "com.example.app:/oauth2redirect", - "openid", "email", "profile" -).ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - String email = t.getEmail(); - String idToken = t.getIdToken(); - } -}); ----- `signIn` works on iOS, Android, JavaSE and Web with the same code, against the *Web Application* OAuth 2.0 client ID from Google Cloud Console. The redirect URI you supply must match one of the *Authorized redirect URIs* configured for that client. @@ -190,19 +127,6 @@ If you currently rely on `GoogleConnect.doLogin()` with the Google Sign-In SDK o `com.codename1.social.FacebookConnect` exposes both the old SDK-based `doLogin()` and a new SDK-free `signIn(...)` that uses the system browser via `OidcClient`. Use the new method for the simulator, the web port, and for apps that don't want to bundle the Facebook SDK at all: -[source,java] ----- -FacebookConnect.getInstance().signIn( - "FB_APP_ID", - "com.example.app:/oauth2redirect", - "public_profile", "email" -).ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - String accessToken = t.getAccessToken(); // ID token will be null - // call https://graph.facebook.com/me with the access token for user data - } -}); ----- Facebook doesn't issue OpenID-Connect ID tokens for classic OAuth flows, so `getIdToken()` returns `null`. Read user profile fields via the Graph API. @@ -210,90 +134,24 @@ Facebook doesn't issue OpenID-Connect ID tokens for classic OAuth flows, so `get `com.codename1.social.MicrosoftConnect` wraps the same OIDC client against `https://login.microsoftonline.com/{tenant}/v2.0`. The default tenant `common` accepts personal and work / school accounts; pass a tenant GUID, `organizations`, or `consumers` to restrict. -[source,java] ----- -MicrosoftConnect.getInstance() - .withTenant("common") - .signIn( - "YOUR_ENTRA_CLIENT_ID", - "com.example.app:/oauth2redirect", - "openid", "email", "profile", "User.Read" - ).ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - // t.getAccessToken() is a Microsoft Graph access token - } - }); ----- NOTE: Apps that need broker (Microsoft Authenticator) support for Conditional Access can bundle the MSAL SDK manually and bypass `MicrosoftConnect`. For the vast majority of apps the system-browser flow is sufficient and lets the simulator and web port share the same code path. === Auth0 -[source,java] ----- -Auth0Connect.getInstance() - .withDomain("dev-xyz.us.auth0.com") - .withAudience("https://api.example.com") // optional - .signIn( - "YOUR_AUTH0_CLIENT_ID", - "com.example.app:/oauth2redirect", - "openid", "email", "profile" - ).ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - String idToken = t.getIdToken(); - } - }); ----- `Auth0Connect` is the simplest of the providers because Auth0 is a clean OIDC provider; everything beyond `withDomain` is optional. === Firebase Authentication -Firebase Auth is *not* an OIDC provider -- it issues Google-Identity-Toolkit-style tokens via REST endpoints. `com.codename1.social.FirebaseAuth` wraps those endpoints: +Firebase Auth isn't an OIDC provider -- it issues Google-Identity-Toolkit-style tokens via REST endpoints. `com.codename1.social.FirebaseAuth` wraps those endpoints: -[source,java] ----- -FirebaseAuth.getInstance() - .withApiKey("YOUR_FIREBASE_WEB_API_KEY") - .signInWithEmailAndPassword("a@b.com", "hunter2") - .ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser u) { - String uid = u.getUid(); - String firebaseIdToken = u.getIdToken(); - } - }); ----- For federated sign-in (Google / Apple / Microsoft as Firebase providers), first obtain an ID token via the matching `*Connect` class, then swap it for a Firebase session: -[source,java] ----- -GoogleConnect.getInstance().signIn(..., "openid", "email") - .ready(new SuccessCallback() { - public void onSucess(OidcTokens g) { - FirebaseAuth.getInstance().signInWithIdpIdToken(g.getIdToken(), "google.com") - .ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser u) { - // Firebase user is now signed in. - } - }); - } - }); ----- Refresh the Firebase session at app launch: -[source,java] ----- -FirebaseAuth fa = FirebaseAuth.getInstance().withApiKey(KEY); -if (!fa.isSignedIn()) { - fa.refresh().ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser u) { - // u is null if no refresh token was stored - } - }); -} ----- === Passkeys / WebAuthn @@ -313,47 +171,6 @@ Reach for `WebAuthnClient` when: Pseudo-code (the actual HTTP calls depend on your server library): -[source,java] ----- -import com.codename1.io.webauthn.PublicKeyCredential; -import com.codename1.io.webauthn.PublicKeyCredentialCreationOptions; -import com.codename1.io.webauthn.PublicKeyCredentialRequestOptions; -import com.codename1.io.webauthn.WebAuthnClient; -import com.codename1.io.webauthn.WebAuthnException; -import com.codename1.util.SuccessCallback; - -// Registration -- enroll a new passkey -String challengeJson = myServer.startPasskeyRegistration(currentUserId); // PublicKeyCredentialCreationOptionsJSON -PublicKeyCredentialCreationOptions opts = - PublicKeyCredentialCreationOptions.fromJson(challengeJson); - -WebAuthnClient.getInstance().create(opts) - .ready(new SuccessCallback() { - public void onSucess(PublicKeyCredential cred) { - myServer.finishPasskeyRegistration(cred.toJson()); // server verifies + persists - } - }) - .except(new SuccessCallback() { - public void onSucess(Throwable err) { - if (err instanceof WebAuthnException) { - String code = ((WebAuthnException) err).getError(); - // "NotAllowedError" -- user dismissed the sheet - // "not_supported" -- platform lacks a passkey impl - // ... etc. - } - } - }); - -// Sign-in -- assert with an existing passkey -String requestJson = myServer.startPasskeySignIn(); // PublicKeyCredentialRequestOptionsJSON -WebAuthnClient.getInstance() - .get(PublicKeyCredentialRequestOptions.fromJson(requestJson)) - .ready(new SuccessCallback() { - public void onSucess(PublicKeyCredential cred) { - myServer.finishPasskeySignIn(cred.toJson()); // server verifies signature - } - }); ----- `PublicKeyCredential#toJson()` is in the W3C `RegistrationResponseJSON` / `AuthenticationResponseJSON` format, which every WebAuthn server library accepts as-is. On the server, verify the response with one of: @@ -395,36 +212,6 @@ Android: nothing beyond the dependency that gets injected automatically. The ass `Auth0Connect` exposes two passkey helpers driven by Auth0's WebAuthn grant (`urn:okta:params:oauth:grant-type:webauthn`). They drive `WebAuthnClient` under the hood, so the same iOS / Android platform requirements apply. -[source,java] ----- -// Sign in with an existing passkey -Auth0Connect.getInstance() - .withDomain("dev-xyz.us.auth0.com") - .signInWithPasskey( - "YOUR_AUTH0_CLIENT_ID", - "Username-Password-Authentication", // connection / realm - "openid", "email", "profile", "offline_access") - .ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - String idToken = t.getIdToken(); - } - }); - -// Enroll a brand-new passkey for a new account -Auth0Connect.getInstance() - .withDomain("dev-xyz.us.auth0.com") - .registerPasskey( - "YOUR_AUTH0_CLIENT_ID", - "Username-Password-Authentication", - "alice@example.com", - "Alice", - "openid", "email", "profile") - .ready(new SuccessCallback() { - public void onSucess(OidcTokens tokens) { - // user is signed in; passkey is enrolled for next time - } - }); ----- Enable *Passkeys* in the Auth0 dashboard (Authentication → Database → your connection → Authentication Methods → Passkey), and add *WebAuthn* to the application's *Grant Types*. @@ -433,31 +220,6 @@ Enable *Passkeys* in the Auth0 dashboard (Authentication → Database → `FirebaseAuth` exposes `registerPasskey` (enroll a passkey for the signed-in user) and `signInWithPasskey` (sign in with an existing passkey). Both use Firebase Identity Platform's v2 passkey REST endpoints (`accounts/passkeyEnrollment:start|finalize`, `accounts/passkeySignIn:start|finalize`). -[source,java] ----- -FirebaseAuth fa = FirebaseAuth.getInstance().withApiKey("YOUR_FIREBASE_WEB_API_KEY"); - -// Sign in with a passkey already on this device -fa.signInWithPasskey().ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser u) { - String uid = u.getUid(); - String firebaseIdToken = u.getIdToken(); - } -}); - -// Enroll a new passkey for the currently signed-in user -fa.signInWithEmailAndPassword(email, password) - .ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser u) { - fa.registerPasskey("Alice's iPhone") - .ready(new SuccessCallback() { - public void onSucess(FirebaseAuth.FirebaseUser enrolled) { - // Next launch can sign in with signInWithPasskey() alone - } - }); - } - }); ----- Passkey support requires the *Identity Platform* tier of Firebase Auth (the upgraded plan). The free Firebase Auth tier doesn't expose the passkey endpoints; the calls will fail with an HTTP 403. Check your project plan in the Firebase console under Build → Authentication → Settings → User account linking. @@ -488,56 +250,14 @@ A typical legacy snippet: [source,java] ---- -Oauth2 oauth = new Oauth2( - "https://provider.example.com/oauth2/authorize", - "CLIENT_ID", - "https://example.com/callback", - "openid email", - "https://provider.example.com/oauth2/token", - "CLIENT_SECRET"); -oauth.showAuthentication(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (e.getSource() instanceof AccessToken) { - AccessToken t = (AccessToken) e.getSource(); - // ... - } - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AuthenticationAndIdentityJava015Snippet.java[tag=authentication-and-identity-java-015,indent=0] ---- becomes: -[source,java] ----- -OidcConfiguration cfg = OidcConfiguration.newBuilder() - .authorizationEndpoint("https://provider.example.com/oauth2/authorize") - .tokenEndpoint("https://provider.example.com/oauth2/token") - .build(); -OidcClient client = OidcClient.create(cfg) - .setClientId("CLIENT_ID") - .setClientSecret("CLIENT_SECRET") - .setRedirectUri("https://example.com/callback") - .setScopes("openid", "email"); -client.authorize().ready(new SuccessCallback() { - public void onSucess(OidcTokens t) { - AccessToken legacy = t.toAccessToken(); // for code that still expects AccessToken - } -}); ----- Or, if the provider exposes a discovery document: -[source,java] ----- -OidcClient.discover("https://provider.example.com").ready(new SuccessCallback() { - public void onSucess(OidcClient client) { - client.setClientId("CLIENT_ID") - .setRedirectUri("https://example.com/callback") - .setScopes("openid", "email"); - client.authorize().ready(/* ... */); - } -}); ----- `OidcTokens#toAccessToken()` returns a `com.codename1.io.AccessToken`, so callers that already deal in `AccessToken` (most subclasses of `com.codename1.social.Login`) can adopt `OidcClient` without changing their token type. diff --git a/docs/developer-guide/Biometric-Authentication.asciidoc b/docs/developer-guide/Biometric-Authentication.asciidoc index 83908cbacc9..9b2bc1b6ba0 100644 --- a/docs/developer-guide/Biometric-Authentication.asciidoc +++ b/docs/developer-guide/Biometric-Authentication.asciidoc @@ -8,49 +8,13 @@ A sibling `SecureStorage` API stores secret strings (auth tokens, refresh tokens [source,java] ---- -import com.codename1.security.Biometrics; -import com.codename1.security.BiometricError; -import com.codename1.security.BiometricException; - -Biometrics b = Biometrics.getInstance(); -if (!b.canAuthenticate()) { - // Fall back to password - return; -} -b.authenticate("Unlock your account").onResult((success, err) -> { - if (err != null) { - BiometricError code = ((BiometricException) err).getError(); - switch (code) { - case USER_CANCELED: /* user dismissed the prompt */ break; - case LOCKED_OUT: /* too many bad attempts */ break; - case NOT_ENROLLED: /* prompt the user to enrol in Settings */ break; - default: /* generic failure */ break; - } - } else { - // Authenticated. Continue with the gated action. - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava001Snippet.java[tag=biometric-authentication-java-001,indent=0] ---- Always gate the call on `canAuthenticate()` and never on `isSupported()` alone. `isSupported()` only checks for hardware, while `canAuthenticate()` also requires at least one enrolled credential and that the device isn't in a locked-out state. === Quick start: Secure storage -[source,java] ----- -import com.codename1.security.SecureStorage; - -// Write (Android prompts; iOS does not unless ios.Fingerprint.addPassword.prompt=true) -SecureStorage.getInstance().set("Save your token", "user@example.com", token); - -// Read (prompts on both platforms) -SecureStorage.getInstance().get("Unlock your token", "user@example.com") - .onResult((value, err) -> { - if (err == null) { - // Use value - } - }); ----- Entries are bound to the current set of enrolled biometrics. If the user enrols a new fingerprint or face after writing, the next `get` fails with `BiometricError.KEY_REVOKED` and the application must re-prompt for the original value and write it again. @@ -81,7 +45,7 @@ One thing the plugin won't inject: the iOS Face ID usage description. Apple reje [source,properties] ---- -ios.NSFaceIDUsageDescription=Authenticate to securely access your account +include::../demos/common/src/main/snippets/developer-guide/biometric-authentication.properties[tag=biometric-authentication-properties-001,indent=0] ---- The JavaSE simulator automatically sets this hint to a default the first time biometrics is invoked, so projects developed in the simulator have a working default; you should overwrite the text before shipping. @@ -90,12 +54,12 @@ To share keychain entries with iOS App Extensions, set both the build hint and t [source,properties] ---- -ios.keychainAccessGroup=TEAMID123.group.com.example.app +include::../demos/common/src/main/snippets/developer-guide/biometric-authentication.properties[tag=biometric-authentication-properties-002,indent=0] ---- [source,java] ---- -SecureStorage.getInstance().setKeychainAccessGroup("TEAMID123.group.com.example.app"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava003Snippet.java[tag=biometric-authentication-java-003,indent=0] ---- The Team ID prefix is required; omitting it produces `errSecMissingEntitlement`. @@ -106,16 +70,7 @@ Use `AuthenticationOptions` to control prompt copy and security policy: [source,java] ---- -import com.codename1.security.AuthenticationOptions; - -Biometrics.getInstance().authenticate(new AuthenticationOptions() - .setReason("Authorize transfer") // iOS localizedReason; Android title fallback - .setTitle("Confirm payment") // Android only - .setSubtitle("Stripe charge $25.00") // Android only - .setNegativeButtonText("Cancel") // Android only - .setBiometricOnly(true) // reject PIN / passcode fallback - .setSensitiveTransaction(true) // request class-3 ("strong") biometric (Android 30+) -); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BiometricAuthenticationJava004Snippet.java[tag=biometric-authentication-java-004,indent=0] ---- Unrecognised options are ignored, so callers can set the union without platform-checking. diff --git a/docs/developer-guide/Casual-Game-Programming.asciidoc b/docs/developer-guide/Casual-Game-Programming.asciidoc index e3ff8f30977..c400b682a0d 100644 --- a/docs/developer-guide/Casual-Game-Programming.asciidoc +++ b/docs/developer-guide/Casual-Game-Programming.asciidoc @@ -78,351 +78,3 @@ Codename One is a single threaded API, it supports working on other threads but Now that you got that out of the way, the rest of the code is clearer. Now you understand that `animateLayoutAndWait` will wait for the animation to complete and the next lines can do the next animation. Indeed after that you invoke the `dealCard` method that hands the cards to the players. This method is also blocking (using and `wait` methods internally) it also marks the cards as draggable and adds that drag logic which you will later use to swap cards. In the animation department, you use a method called replace to fade in a component using a transition. - -To handle the dealing an action listener is added to the deck button, this action listener is invoked when the cards are dealt and that completes the game: - -[source,java] ----- -public class Poker { - private static final char SUITE_SPADE = 's'; - private static final char SUITE_HEART = 'h'; - private static final char SUITE_DIAMOND = 'd'; - private static final char SUITE_CLUB = 'c'; - - private Resources cards; - private Form current; - private final static Card[] deck; - - static { - // we initialize constant card values that will be useful later on in the game - deck = new Card[52]; - for(int iter = 0 ; iter < 13 ; iter++) { - deck[iter] = new Card(SUITE_SPADE, iter + 2); - deck[iter + 13] = new Card(SUITE_HEART, iter + 2); - deck[iter + 26] = new Card(SUITE_DIAMOND, iter + 2); - deck[iter + 39] = new Card(SUITE_CLUB, iter + 2); - } - } - - /** - * We use this method to calculate a "fake" DPI based on screen resolution rather than its actual DPI - * this is useful so we can have large images on a tablet - */ - private int calculateDPI() { - int pixels = Display.getInstance().getDisplayHeight() * Display.getInstance().getDisplayWidth(); - if(pixels > 1000000) { - return Display.DENSITY_HD; - } - if(pixels > 340000) { - return Display.DENSITY_VERY_HIGH; - } - if(pixels > 150000) { - return Display.DENSITY_HIGH; - } - return Display.DENSITY_MEDIUM; - } - - /** - * This method is invoked by Codename One once when the application loads - */ - public void init(Object context) { - try{ - // after loading the default theme we load the card images as a resource with - // a fake DPI so they will be large enough. We store them in a resource rather - // than as files so we can use the MultiImage functionality - Resources theme = Resources.openLayered("/theme"); - UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); - cards = Resources.open("/gamedata.res", calculateDPI()); - } catch(IOException e) { - e.printStackTrace(); - } - } - - /** - * This method is invoked by Codename One once when the application loads and when it is restarted - */ - public void start() { - if(current != null){ - current.show(); - return; - } - showSplashScreen(); - } - - /** - * The splash screen is relatively bare-bones. Its important to have a splash screen for iOS - * since the build process generates a screenshot of this screen to speed up perceived performance - */ - public void showSplashScreen() { - final Form splash = new Form(); - - // a border layout places components in the center and the 4 sides. - // by default it scales the center component so here we configure - // it to place the component in the actual center - BorderLayout border = new BorderLayout(); - border.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE); - splash.setLayout(border); - - // by default the form's content pane is scrollable on the Y axis - // we need to disable it here - splash.setScrollable(false); - Label title = new Label("Poker Ace"); - - // The UIID is used to determine the appearance of the component in the theme - title.setUIID("SplashTitle"); - Label subtitle = new Label("By Codename One"); - subtitle.setUIID("SplashSubTitle"); - - splash.addComponent(BorderLayout.NORTH, title); - splash.addComponent(BorderLayout.SOUTH, subtitle); - Label as = new Label(cards.getImage("as.png")); - Label ah = new Label(cards.getImage("ah.png")); - Label ac = new Label(cards.getImage("ac.png")); - Label ad = new Label(cards.getImage("ad.png")); - - // a layered layout places components one on top of the other in the same dimension, it is - // useful for transparency but in this case we are using it for an animation - final Container center = new Container(new LayeredLayout()); - center.addComponent(as); - center.addComponent(ah); - center.addComponent(ac); - center.addComponent(ad); - - splash.addComponent(BorderLayout.CENTER, center); - - splash.show(); - splash.setTransitionOutAnimator(CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, true, 800)); - - // postpone the animation to the next cycle of the EDT to allow the UI to render fully once - Display.getInstance().callSerially(new Runnable() { - public void run() { - // We replace the layout so the cards will be laid out in a line and animate the hierarchy - // over 2 seconds, this effectively creates the effect of cards spreading out - center.setLayout(new BoxLayout(BoxLayout.X_AXIS)); - center.setShouldCalcPreferredSize(true); - splash.getContentPane().animateHierarchy(2000); - - // after showing the animation we wait for 2.5 seconds and then show the game with a nice - // transition, notice that we use UI timer which is invoked on the Codename One EDT thread! - new UITimer(new Runnable() { - public void run() { - showGameUI(); - } - }).schedule(2500, false, splash); - } - }); - } - - /** - * This is the method that shows the game running, it is invoked to start or restart the game - */ - private void showGameUI() { - // we use the java.util classes to shuffle a new instance of the deck - final List shuffledDeck = new ArrayList(Arrays.asList(deck)); - Collections.shuffle(shuffledDeck); - - final Form gameForm = new Form(); - gameForm.setTransitionOutAnimator(CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, true, 800)); - Container gameFormBorderLayout = new Container(new BorderLayout()); - - // while flow layout is the default in this case we want it to center into the middle of the screen - FlowLayout fl = new FlowLayout(Component.CENTER); - fl.setValign(Component.CENTER); - final Container gameUpperLayer = new Container(fl); - gameForm.setScrollable(false); - - // we place two layers in the game form, one contains the contents of the game and another one on top contains instructions - // and overlays. In this case we only use it to write a hint to the user when he needs to swap his cards - gameForm.setLayout(new LayeredLayout()); - gameForm.addComponent(gameFormBorderLayout); - gameForm.addComponent(gameUpperLayer); - - // The game itself comprises 3 containers, one for each player containing a grid of 5 cards (grid layout - // divides space evenly) and the deck of cards/dealer. Initially we show an animation where all the cards - // gather into the deck, that is why we set the initial deck layout to show the whole deck 4×13 - final Container deckContainer = new Container(new GridLayout(4, 13)); - final Container playerContainer = new Container(new GridLayout(1, 5)); - final Container rivalContainer = new Container(new GridLayout(1, 5)); - - // we place all the card images within the deck container for the initial animation - for(int iter = 0 ; iter < deck.length ; iter++) { - Label face = new Label(cards.getImage(deck[iter].getFileName())); - - // containers have no padding or margin this effectively removes redundant spacing - face.setUIID("Container"); - deckContainer.addComponent(face); - } - - // we place our cards at the bottom, the deck at the center and our rival on the north - gameFormBorderLayout.addComponent(BorderLayout.CENTER, deckContainer); - gameFormBorderLayout.addComponent(BorderLayout.NORTH, rivalContainer); - gameFormBorderLayout.addComponent(BorderLayout.SOUTH, playerContainer); - gameForm.show(); - - // we wait 1.8 seconds to start the opening animation, otherwise it might start while the transition is still running - new UITimer(new Runnable() { - public void run() { - // we add a card back component and make it a drop target so later players - // can drag their cards here - final Button cardBack = new Button(cards.getImage("card_back.png")); - cardBack.setDropTarget(true); - - // we remove the button styling so it doesn't look like a button by using setUIID. - cardBack.setUIID("Label"); - deckContainer.addComponent(cardBack); - - // we set the layout to layered layout which places all components one on top of the other then animate - // the layout into place, this will cause the spread out deck to "flow" into place - // Notice we are using the AndWait variant which will block the event dispatch thread (legally) while - // performing the animation, normally you can't block the dispatch thread (EDT) - deckContainer.setLayout(new LayeredLayout()); - deckContainer.animateLayoutAndWait(3000); - - - // we don't need all the card images/labels in the deck, so we place the card back - // on top then remove all the other components - deckContainer.removeAll(); - deckContainer.addComponent(cardBack); - - // Now we iterate over the cards and deal the top card from the deck to each player - for(int iter = 0 ; iter < 5 ; iter++) { - Card currentCard = shuffledDeck.get(0); - shuffledDeck.remove(0); - dealCard(cardBack, playerContainer, cards.getImage(currentCard.getFileName()), currentCard); - currentCard = shuffledDeck.get(0); - shuffledDeck.remove(0); - dealCard(cardBack, rivalContainer, cards.getImage("card_back.png"), currentCard); - } - - // After dealing we place a notice in the upper layer by fade in. The trick is in adding a blank component - // and replacing it with a fade transition - TextArea notice = new TextArea("Drag cards to the deck to swap\ntap the deck to finish"); - notice.setEditable(false); - notice.setFocusable(false); - notice.setUIID("Label"); - notice.getUnselectedStyle().setAlignment(Component.CENTER); - gameUpperLayer.addComponent(notice); - gameUpperLayer.layoutContainer(); - - // we place the notice then remove it without the transition, we need to do this since a text area - // might resize itself so we need to know its size in advance to fade it in. - Label temp = new Label(" "); - temp.setPreferredSize(new Dimension(notice.getWidth(), notice.getHeight())); - gameUpperLayer.replace(notice, temp, null); - - gameUpperLayer.layoutContainer(); - gameUpperLayer.replace(temp, notice, CommonTransitions.createFade(1500)); - - // when the user taps the card back (the deck) we finish the game - cardBack.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - // we clear the notice text - gameUpperLayer.removeAll(); - - // we deal the new cards to the player (the rival never takes new cards) - while(playerContainer.getComponentCount() < 5) { - Card currentCard = shuffledDeck.get(0); - shuffledDeck.remove(0); - dealCard(cardBack, playerContainer, cards.getImage(currentCard.getFileName()), currentCard); - } - - // expose the rivals deck then offer the chance to play again... - for(int iter = 0 ; iter < 5 ; iter++) { - Button cardButton = (Button)rivalContainer.getComponentAt(iter); - - // when creating a card we save the state into the component itself which is very convenient - Card currnetCard = (Card)cardButton.getClientProperty("card"); - Label l = new Label(cards.getImage(currnetCard.getFileName())); - rivalContainer.replaceAndWait(cardButton, l, CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, true, 300)); - } - - // notice dialogs are blocking by default so its pretty easy to write this logic - if(!Dialog.show("Again?", "Ready to play Again", "Yes", "Exit")) { - Display.getInstance().exitApplication(); - } - - // play again - showGameUI(); - } - }); - } - }).schedule(1800, false, gameForm); - } - - /** - * A blocking method that creates the card deal animation and binds the drop logic when cards are dropped on the deck - */ - private void dealCard(Component deck, final Container destination, Image cardImage, Card currentCard) { - final Button card = new Button(); - card.setUIID("Label"); - card.setIcon(cardImage); - - // Components are normally placed by layout managers so setX/Y/Width/Height shouldn't be invoked. However, - // in this case we want the layout animation to deal from a specific location. Notice that we use absoluteX/Y - // since the default X/Y are relative to their parent container. - card.setX(deck.getAbsoluteX()); - int deckAbsY = deck.getAbsoluteY(); - if(destination.getY() > deckAbsY) { - card.setY(deckAbsY - destination.getAbsoluteY()); - } else { - card.setY(deckAbsY); - } - card.setWidth(deck.getWidth()); - card.setHeight(deck.getHeight()); - destination.addComponent(card); - - // we save the model data directly into the component so we don't need to keep track of it. Later when we - // need to check the card type a user touched we can just use getClientProperty - card.putClientProperty("card", currentCard); - destination.getParent().animateHierarchyAndWait(400); - card.setDraggable(true); - - // when the user drops a card on a drop target (currently only the deck) we remove it and animate it out - card.addDropListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - evt.consume(); - card.getParent().removeComponent(card); - destination.animateLayout(300); - } - }); - } - - public void stop() { - current = Display.getInstance().getCurrent(); - } - - public void destroy() { - } - - - static class Card { - private char suite; - private int rank; - - public Card(char suite, int rank) { - this.suite = suite; - this.rank = rank; - } - - private String rankToString() { - if(rank > 10) { - switch(rank) { - case 11: - return "j"; - case 12: - return "q"; - case 13: - return "k"; - case 14: - return "a"; - } - } - return "" + rank; - } - - public String getFileName() { - return rankToString() + suite + ".png"; - } - } -} ----- diff --git a/docs/developer-guide/Commerce.asciidoc b/docs/developer-guide/Commerce.asciidoc index 9f26bc09490..c064a49bfc1 100644 --- a/docs/developer-guide/Commerce.asciidoc +++ b/docs/developer-guide/Commerce.asciidoc @@ -8,12 +8,6 @@ Codename One doesn't process payments and never touches your money -- the stores The core idea is the *entitlement*: an abstract access right such as `pro` or `remove_ads`. You map one or more store products to an entitlement, and your code only ever checks the entitlement: -[source,java] ----- -if (CommerceManager.getInstance().isEntitled("pro")) { - // unlock pro features -} ----- An entitlement is active when *any* of its granting products is active. This matters: a promotional grant and a paid subscription, or a sandbox and a production purchase, can both map to `pro` without one hiding the other. Access is decided by a single authoritative rule -- the latest expiry over the active grants, evaluated against a server timestamp -- so a tampered device clock can't extend it. Everything else (will-renew, billing stage, period type, family sharing) is advisory and never removes access on its own. @@ -21,30 +15,12 @@ An entitlement is active when *any* of its granting products is active. This mat In your `Lifecycle.init` (or wherever you set up purchases): -[source,java] ----- -CommerceManager cm = CommerceManager.getInstance(); -cm.setAppUserId(myAccountId); // optional; a stable id for the signed-in user ----- Drive purchases through the manager (these delegate to the `Purchase` API): -[source,java] ----- -cm.subscribe("pro_monthly"); -// or cm.purchase("remove_ads"); ----- After a purchase, or on app start, validate the device's receipts with the cloud and refresh the entitlement cache. `refresh()` makes a blocking network call, so run it off the EDT: -[source,java] ----- -CN.callSerially(() -> {}); // (illustrative) -new Thread(() -> { - cm.refresh(); - if (cm.isEntitled("pro")) { /* ... */ } -}).start(); ----- `isEntitled` returns the last cloud-validated answer when one is available, and otherwise falls back to the platform's own receipt (treating the entitlement id as a subscription SKU) so a paying user is never locked out while the network is down. diff --git a/docs/developer-guide/Crash-Protection.asciidoc b/docs/developer-guide/Crash-Protection.asciidoc index 50337981fb1..44c892a9440 100644 --- a/docs/developer-guide/Crash-Protection.asciidoc +++ b/docs/developer-guide/Crash-Protection.asciidoc @@ -29,13 +29,6 @@ The `codename1.arg.` prefix marks it as a build hint -- when the property reache In your `Lifecycle.init`: -[source,java] ----- -public void init(Object context) { - CrashProtection.install(); - CrashProtection.setEnabled(true); // default is false; user-controlled opt-in -} ----- `install()` registers the EDT error handler and (on Android) wires `Thread.UncaughtExceptionHandler`. It's idempotent and a no-op on the simulator. `setEnabled(boolean)` controls whether captured crashes actually get sent to the server -- the captured payload is always persisted locally first, so `setEnabled(true)` after the fact drains the buffer. diff --git a/docs/developer-guide/Deep-Links-Routing.asciidoc b/docs/developer-guide/Deep-Links-Routing.asciidoc index 62dc61784de..7e409f37fac 100644 --- a/docs/developer-guide/Deep-Links-Routing.asciidoc +++ b/docs/developer-guide/Deep-Links-Routing.asciidoc @@ -18,37 +18,11 @@ Annotate the target `Form` class: [source,java] ---- -package com.example; - -import com.codename1.annotations.Route; -import com.codename1.annotations.RouteParam; -import com.codename1.ui.Form; - -@Route("/users/:id") -public class ProfileForm extends Form { - public ProfileForm(@RouteParam("id") String id) { - setTitle("Profile " + id); - // ... - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DeepLinksRoutingJava001Snippet.java[tag=deep-links-routing-java-001,indent=0] ---- Or annotate a static factory method: -[source,java] ----- -public class Routes { - @Route("/home") - public static Form home() { - return new HomeForm(); - } - - @Route("/users/:id") - public static Form profile(@RouteParam("id") String id) { - return new ProfileForm(id); - } -} ----- Both forms are supported and a project can mix them. Path variables in the pattern (`:id`, `*`, `**`) are matched by name against parameters @@ -78,21 +52,6 @@ resolves the URL through the same generated route table that handles incoming deep links, builds the matching `Form`, pushes it onto the navigation stack, and shows it. -[source,java] ----- -Navigation.navigate("/users/42"); - -// Go back one step: -Navigation.back(); - -// Inspect the stack for a breadcrumb UI: -Container breadcrumbs = new Container(BoxLayout.x()); -for (final NavigationEntry e : Navigation.getStack()) { - Button crumb = new Button(e.getTitle()); - crumb.addActionListener(evt -> Navigation.popTo(e)); - breadcrumbs.add(crumb); -} ----- The five-method surface (`navigate`, `back`, `getCurrent`, `getStack`, `popTo`) is everything the application sees -- the rest of the URL-to- @@ -122,16 +81,6 @@ Host an `apple-app-site-association` JSON file at `https://your.domain/.well-known/apple-app-site-association` over HTTPS without redirects. The plugin's `AasaBuilder` produces the payload: -[source,java] ----- -String json = new com.codename1.maven.routing.AasaBuilder() - .appId("ABCD1234.com.example.app") - .addRouterPattern("/users/:id") - .addRouterPattern("/share/**") - .addPath("NOT /admin/*") - .build(); -// Write `json` to https://example.com/.well-known/apple-app-site-association ----- Tell iOS which domains your app claims by setting the `ios.associatedDomains` build hint -- a comma-separated list of @@ -141,7 +90,7 @@ automatically. [source,properties] ---- -codename1.arg.ios.associatedDomains=applinks:example.com,applinks:www.example.com +include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.properties[tag=deep-links-routing-properties-001,indent=0] ---- === Android App Links @@ -150,14 +99,6 @@ Host an `assetlinks.json` file at `https://your.domain/.well-known/assetlinks.json`. The plugin's `AssetLinksBuilder` produces the payload: -[source,java] ----- -String json = new com.codename1.maven.routing.AssetLinksBuilder() - .addApp("com.example.app", - "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5") - .addFingerprint("AB:CD:..." /* Play App Signing upload cert */) - .build(); ----- Tell Android which URLs to intercept by setting the `android.xintent_filter` build hint with a verified intent filter for @@ -173,15 +114,14 @@ App Signing. [source,sh] ---- -xcrun simctl openurl booted "https://example.com/users/42" +include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.sh[tag=deep-links-routing-sh-001,indent=0] ---- ==== Android Emulator [source,sh] ---- -adb shell am start -a android.intent.action.VIEW \ - -d "https://example.com/users/42" com.example.app +include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.sh[tag=deep-links-routing-sh-002,indent=0] ---- ==== Desktop Simulator @@ -194,22 +134,6 @@ the simulator's URL-injection helper. Independent of deep linking, individual forms can intercept back / pop attempts so they can confirm before discarding unsaved work: -[source,java] ----- -import com.codename1.router.PopGuard; -import com.codename1.router.PopReason; - -editForm.setPopGuard(new PopGuard() { - public boolean canPop(Form form, PopReason reason) { - if (!isDirty()) { - return true; - } - Dialog.show("Discard changes?", "You have unsaved edits.", - "Stay", "Discard"); - return false; - } -}); ----- The guard fires for the toolbar back button, the Android hardware back key, the iOS edge-swipe gesture, and any programmatic back navigation. diff --git a/docs/developer-guide/Desktop-Integration.asciidoc b/docs/developer-guide/Desktop-Integration.asciidoc index 48655603d0c..a95b29f28a6 100644 --- a/docs/developer-guide/Desktop-Integration.asciidoc +++ b/docs/developer-guide/Desktop-Integration.asciidoc @@ -57,8 +57,7 @@ applies for you. In `codenameone_settings.properties`: [source,properties] ----- -codename1.arg.desktop.titleBar=native -codename1.arg.desktop.interactiveScrollbars=true +include::../demos/common/src/main/snippets/developer-guide/desktop-integration.properties[tag=desktop-integration-properties-001,indent=0] ----- Newly generated projects already contain these lines. The generated `Stub` reads them @@ -74,8 +73,7 @@ first `Form` is shown: [source,java] ----- -Display.getInstance().setProperty("desktop.titleBar", "native"); -Display.getInstance().setProperty("desktop.interactiveScrollbars", "true"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava001Snippet.java[tag=desktop-integration-java-001,indent=0] ----- NOTE: The native title bar and native menu bar on macOS require iOS 13 / macOS 10.15 (Catalyst) @@ -95,21 +93,7 @@ and the `Command.DESKTOP_MENU_*` constants: [source,java] ----- -Form hi = new Form("My App", BoxLayout.y()); - -Command about = new Command("About My App"); -about.setDesktopMenu(Command.DESKTOP_MENU_ABOUT); // application menu - -Command open = new Command("Open File..."); -open.setDesktopMenu(Command.DESKTOP_MENU_FILE); // File menu - -Command quit = new Command("Quit"); -quit.setDesktopMenu(Command.DESKTOP_MENU_QUIT); // application menu - -hi.addCommand(about); -hi.addCommand(open); -hi.addCommand(quit); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava002Snippet.java[tag=desktop-integration-java-002,indent=0] ----- The recognised values are (case-insensitive): @@ -138,14 +122,7 @@ next to the menu item and triggers the command from the keyboard: [source,java] ----- -Command save = new Command("Save"); -save.setDesktopMenu(Command.DESKTOP_MENU_FILE); -save.setDesktopShortcut('S'); // Cmd+S on macOS, Ctrl+S on Windows/Linux - -Command saveAs = new Command("Save As..."); -saveAs.setDesktopMenu(Command.DESKTOP_MENU_FILE); -saveAs.setDesktopShortcut('S', - Command.DESKTOP_SHORTCUT_MODIFIER_PRIMARY | Command.DESKTOP_SHORTCUT_MODIFIER_SHIFT); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/DesktopIntegrationJava003Snippet.java[tag=desktop-integration-java-003,indent=0] ----- The single-argument form uses the platform's *primary* modifier - Command on macOS, Control on @@ -205,17 +182,6 @@ macOS / Mac Catalyst build it goes through the same `UNUserNotificationCenter` p the notification delivers it to your app's `LocalNotificationCallback`, exactly as on mobile, so the same scheduling and handling code is shared across phone and desktop: -[source,java] ------ -LocalNotification n = new LocalNotification(); -n.setId("reminder-1"); -n.setAlertTitle("Reminder"); -n.setAlertBody("Your export has finished."); - -// fire in 5 seconds, no repeat -Display.getInstance().scheduleLocalNotification( - n, System.currentTimeMillis() + 5000, LocalNotification.REPEAT_NONE); ------ NOTE: On the Java SE build desktop notifications require an OS with system-tray support (`java.awt.SystemTray`); where it's unavailable the notification is logged and skipped. diff --git a/docs/developer-guide/Device-Input-And-Form-Factors.asciidoc b/docs/developer-guide/Device-Input-And-Form-Factors.asciidoc index 695c3132ea0..1f61566404f 100644 --- a/docs/developer-guide/Device-Input-And-Form-Factors.asciidoc +++ b/docs/developer-guide/Device-Input-And-Form-Factors.asciidoc @@ -24,29 +24,12 @@ snapshot. There are two ways to read it. Any pointer listener receives an `ActionEvent`; call `getPointerEvent()` on it: -[source,java] ----- -myComponent.addPointerPressedListener(e -> { - PointerEvent pe = e.getPointerEvent(); - if (pe.isSecondaryButton()) { - showContextMenu(pe.getX(), pe.getY()); - } - System.out.println("pressure=" + pe.getPressure() + " type=" + pe.getPointerType()); -}); ----- ==== By polling When you're inside a pointer callback you can also poll the current values directly from `CN` (or `Display`), mirroring the existing `CN.isShiftKeyDown()` family: -[source,java] ----- -int button = CN.getPointerButton(); // PointerEvent.BUTTON_PRIMARY, BUTTON_SECONDARY, ... -float pressure = CN.getPointerPressure(); // 0.0 - 1.0, defaults to 1.0 -boolean pen = CN.isStylusPointer(); -PointerEvent current = CN.getCurrentPointerEvent(); ----- TIP: Values have safe defaults. On a plain touch screen the button is `BUTTON_PRIMARY`, the pressure is `1.0` and the type is `TYPE_TOUCH` (or `TYPE_UNKNOWN` on ports that don't classify the device). @@ -65,13 +48,6 @@ For the common case of a context menu, use the unified `addContextMenuListener`. secondary (right) mouse click, a stylus barrel button click and a long press, so the same code handles desktop and touch: -[source,java] ----- -label.addContextMenuListener(e -> { - e.consume(); // suppress the normal click/long-press handling - showMyContextMenu(e.getX(), e.getY()); -}); ----- === Mouse wheel and trackpad scrolling @@ -81,16 +57,6 @@ the wheel yourself - for example control plus wheel to zoom, or horizontal scrol https://www.codenameone.com/javadoc/com/codename1/ui/events/WheelEvent.html[`WheelEvent`]; consuming it prevents the default scroll. -[source,java] ----- -canvas.addMouseWheelListener(e -> { - WheelEvent w = (WheelEvent) e; - if (w.isControlDown()) { - zoom(w.getDeltaY()); - w.consume(); // do not scroll, we zoomed instead - } -}); ----- A positive `getDeltaY()` reveals content above (the user scrolled down); a positive `getDeltaX()` reveals content to the left. `isPrecise()` is true for high resolution devices such as trackpads. @@ -120,19 +86,6 @@ NOTE: A Windows precision touchpad usually reports a pinch as control plus wheel gesture, so it also arrives through the universal wheel API. Handle that case with a `addMouseWheelListener` that checks `isControlDown()` (shown above) if you target those devices. -[source,java] ----- -Container photo = new Container() { - protected boolean pinch(float scale) { - zoom(scale); // trackpad pinch or two finger pinch - return true; - } - protected boolean rotation(float radians) { - rotate(radians); // trackpad two finger rotate - return true; - } -}; ----- === Stylus and pen support @@ -146,18 +99,6 @@ When a stylus is used (Apple Pencil, S-Pen, Surface Pen) the pointer type is `TY For drawing surfaces a dedicated `addStylusListener` fires on press, drag and release only when the pointer is a stylus or eraser: -[source,java] ----- -drawingArea.addStylusListener(e -> { - PointerEvent pe = e.getPointerEvent(); - float width = 1f + pe.getPressure() * 9f; // pressure controls stroke width - if (pe.isEraser()) { - erase(pe.getX(), pe.getY()); - } else { - drawPoint(pe.getX(), pe.getY(), width); - } -}); ----- Pen hover (the pen moving above the screen without touching, available on devices such as the S-Pen and the M2 iPad with Apple Pencil) flows through the existing `pointerHover` callbacks; the snapshot @@ -174,14 +115,6 @@ Foldable and dual screen devices (Galaxy Fold, Galaxy Flip, Pixel Fold, Surface at runtime. Query the live posture through https://www.codenameone.com/javadoc/com/codename1/ui/DevicePosture.html[`DevicePosture`]: -[source,java] ----- -DevicePosture p = CN.getDevicePosture(); -if (p.isFoldable() && p.isTableTop()) { - // half-opened, hinge horizontal: put media on top, controls on the bottom half - layoutForTableTop(p.getFoldBounds(null)); -} ----- * `getPosture()` - `POSTURE_FLAT`, `POSTURE_HALF_OPENED`, `POSTURE_CLOSED` or `POSTURE_UNKNOWN`. * `getHingeAngle()` - hinge angle in degrees (`-1` when unknown). @@ -192,10 +125,6 @@ if (p.isFoldable() && p.isTableTop()) { React to fold changes with a posture listener: -[source,java] ----- -CN.addPostureListener(e -> relayoutForPosture(CN.getDevicePosture())); ----- To exercise this in the desktop simulator, use the *Simulate -> Foldable* menu: enable foldable mode, then pick a posture (flat, half-opened or closed), the fold orientation (vertical or diff --git a/docs/developer-guide/Events.asciidoc b/docs/developer-guide/Events.asciidoc index e83d498f22c..c45979b9be6 100644 --- a/docs/developer-guide/Events.asciidoc +++ b/docs/developer-guide/Events.asciidoc @@ -37,22 +37,14 @@ For example, you can bind an event callback for a https://www.codenameone.com/ja [source,java] ---- -Button b = new Button("Click Me"); -b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent ev) { - // button was clicked, you can do anything you want here... - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava001Snippet.java[tag=events-java-001,indent=0] ---- Or thanks to the Java 8 lambdas you can write it as: [source,java] ---- -Button b = new Button("Click Me"); -b.addActionListener((ev) -> - // button was clicked, you can do anything you want here... -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava002Snippet.java[tag=events-java-002,indent=0] ---- Notice that the click will work whether the button was touched using a mouse, finger or keypad shortcut seamlessly with an action listener. Many components work with action events for example, buttons, text components, slider etc. @@ -84,10 +76,7 @@ For example, the event dispatch thread allows you to listen to errors on the EDT [source,java] ---- -Display.getInstance().addEdtErrorHandler((e) -> { - Exception err = (Exception)e.getSource(); - // ... -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava003Snippet.java[tag=events-java-003,indent=0] ---- This will work great but you will still get the default error message from the EDT over that exception. To prevent the event from proceeding to the default error handling you can do this: @@ -95,11 +84,7 @@ This will work great but you will still get the default error message from the E [source,java] ---- -Display.getInstance().addEdtErrorHandler((e) -> { - e.consume(); - Exception err = (Exception)e.getSource(); - // ... -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava004Snippet.java[tag=events-java-004,indent=0] ---- Notice that you can check if an event was already consumed using the `isConsumed()` method but it's pretty unlikely that you will receive a consumed event as the system will stop sending it. @@ -110,43 +95,28 @@ https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html[NetworkEv [source,java] ---- -NetworkManager.getInstance().addErrorListener(new ActionListener() { - public void actionPerformed(NetworkEvent ev) { - // now we have access to the methods on NetworkEvent that provide more information about the network specific flags - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava005Snippet.java[tag=events-java-005,indent=0] ---- Or with Java 8 lambdas: [source,java] ---- -NetworkManager.getInstance().addErrorListener((ev) -> { - // now we have access to the methods on NetworkEvent that provide more information about the network specific flags -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava006Snippet.java[tag=events-java-006,indent=0] ---- The `NetworkEvent` allows the networking code to reuse the `EventDispatcher` infrastructure and to simplify event firing through the EDT. But you should notice that some code might not be equivalent for example, you could do this to read the input stream: [source,java] ---- -ConnectionRequest r = new ConnectionRequest() { - @Override - protected void readResponse(InputStream input) throws IOException { - // read the input stream - } -}; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava007Snippet.java[tag=events-java-007,indent=0] ---- Or you can do something similar using this code: [source,java] ---- -ConnectionRequest r = new ConnectionRequest(); -r.addResponseListener((e) -> { - byte[] data = (byte[])e.getMetaData(); - // work with the byte data -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava008Snippet.java[tag=events-java-008,indent=0] ---- These seem similar but they have one important distinction. The latter code is invoked on the EDT, so if `data` is big it might slow down processing. The `ConnectionRequest` is invoked on the network thread and so can process any amount of data without slowing down the UI. @@ -190,25 +160,7 @@ For example, in this code from the `Flickr` demo the https://www.codenameone.com [source,java] ---- -public class CustomToolbar extends Toolbar implements ScrollListener { - private int alpha; - - public CustomToolbar() { - } - - public void paintComponentBackground(Graphics g) { - int a = g.getAlpha(); - g.setAlpha(alpha); - super.paintComponentBackground(g); - g.setAlpha(a); - } - - public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) { - alpha = scrollY; - alpha = Math.max(alpha, 0); - alpha = Math.min(alpha, 255); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava009Snippet.java[tag=events-java-009,indent=0] ---- // HTML_ONLY_START @@ -229,10 +181,6 @@ The https://www.codenameone.com/javadoc/com/codename1/ui/events/SelectionListene https://www.codenameone.com/javadoc/com/codename1/ui/events/StyleListener.html[StyleListener] allows components to track changes to the style objects. For example, if the developer does something like: -[source,java] ----- -cmp.getUnselectedStyle().setFgColor(0xffffff); ----- This will trigger a style event that will eventually lead to the component being repainted. This is quite important for the component class but not an important event for general user code. It's recommended that developers don't bind a style listener. @@ -249,24 +197,11 @@ For example, if you wish to provide an https://www.codenameone.com/javadoc/com/c [source,java] ---- -private final EventDispatcher listeners = new EventDispatcher(); - -public void addActionListener(ActionListener a) { - listeners.addListener(a); -} -public void removeActionListener(ActionListener a) { - listeners.removeListener(a); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava011Snippet.java[tag=events-java-011,indent=0] ---- Then when you need to broadcast the event use: -[source,java] ----- -private void fireEvent(ActionEvent ev) { - listeners.fireActionEvent(ev); -} ----- === Low-level events @@ -317,22 +252,6 @@ IMPORTANT: Key events are relevant to physical keys and won't trigger on virtual The pointer events (touch events) can be intercepted by overriding one or more of these methods in `Component` or `Form`. Notice that unless you want to block functionality you should probably invoke `super` when overriding: -[source,java] ----- -public void pointerDragged(int[] x, int[] y) -public void pointerDragged(final int x, final int y) -public void pointerPressed(int[] x, int[] y) -public void pointerPressed(int x, int y) -public void pointerReleased(int[] x, int[] y) -public void pointerReleased(int x, int y) -public void pointerHover(int[] x, int[] y) -public void pointerHoverPressed(int[] x, int[] y) -public void pointerHoverReleased(int[] x, int[] y) -public void longPointerPress(int x, int y) -public void keyPressed(int keyCode) -public void keyReleased(int keyCode) -public void keyRepeated(int keyCode) ----- Notice that most pointer events have a version that accepts an array as an argument, this allows for multi-touch event handling by sending all the touched coordinates. Desktop and pen-enabled devices can also trigger hover events without a press. To respond to those you can override the `pointerHover*` callbacks on `Form` or `Component`, which are invoked before a button receives focus or a drag begins on those platforms. @@ -350,11 +269,7 @@ For example, if your component is a painting app where you're trying to draw usi [source,java] ---- -public class MyComponent extends Component { - protected int getDragRegionStatus(int x, int y) { - return DRAG_REGION_LIKELY_DRAG_XY; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/EventsJava014Snippet.java[tag=events-java-014,indent=0] ---- This indicates that you want all drag events on both AXIS to be sent as soon as possible. Notice that this doesn't disable event sanitation. @@ -366,16 +281,3 @@ The https://www.codenameone.com/javadoc/com/codename1/ui/events/BrowserNavigatio IMPORTANT: The callback method of this interface is invoked off the EDT! You must **NEVER** block this method and must not access UI or Codename One sensitive elements in this method! The browser navigation callback is invoked directly from the native web component as it navigates to a new page. Because of that it's invoked on the native OS thread and gives you a unique opportunity to handle the navigation ourselves as you see fit. That's why it MUST be invoked on the native thread, since the native browser is pending on your response to that method, spanning an invokeAndBlock/callSerially would be to slow and would bog down the browser. - -You can use the browser navigation callback to change the UI or even to invoke Java code from JavaScript code e.g.: - -[source,java] ----- -bc.setBrowserNavigationCallback((url) -> { - if(url.startsWith("http://click")) { - Display.getInstance().callSerially(() -> bc.execute("fnc('

You clicked!

')")); - return false; - } - return true; -}); ----- diff --git a/docs/developer-guide/Game-Assets.asciidoc b/docs/developer-guide/Game-Assets.asciidoc index fefdb5bad31..2699b26a9ee 100644 --- a/docs/developer-guide/Game-Assets.asciidoc +++ b/docs/developer-guide/Game-Assets.asciidoc @@ -108,9 +108,7 @@ asset entry from the `platformer` pack -- the player: [source,json] ---- -{ "id": "player", "name": "Player", "kind": "actor", - "w": 28, "h": 32, "color": "#4D86FF", "unique": true, - "defaults": { "lives": 3, "jumpHeight": 96 } } +include::../demos/common/src/main/snippets/developer-guide/game-assets.json[tag=game-assets-json-001,indent=0] ---- Each JSON key maps onto an `AssetDef` field (see `AssetDef.fromMap`): `kind` is @@ -180,16 +178,7 @@ example, a collectible key: [source,json] ---- -{ - "id": "key", - "name": "Key", - "kind": "actor", - "w": 24, - "h": 24, - "color": "#E0C040", - "unique": false, - "defaults": { "opens": "door", "value": 1 } -} +include::../demos/common/src/main/snippets/developer-guide/game-assets.json[tag=game-assets-json-002,indent=0] ---- Field by field: @@ -235,11 +224,6 @@ Materials are resolved through `MaterialRegistry`, a process-wide registry that built-ins (`MaterialRegistry.GRASS`, `ROAD`, `STONE`, `SAND`, `WATER`, `DIRT`). `WATER` is registered as solid. Applications add their own without changing the level format: -[source,java] ----- -MaterialRegistry.register(new Material("lava", "Lava", 0xc0392b).setSolid(true)); -Material m = MaterialRegistry.get("lava"); // never null; unknown ids return gray ----- Unknown ids resolve to a neutral gray placeholder, so a level that references a not-yet-registered material still loads. Materials cover terrain cells and diff --git a/docs/developer-guide/Game-Builder.asciidoc b/docs/developer-guide/Game-Builder.asciidoc index 0571d955b6b..ffa7748f7aa 100644 --- a/docs/developer-guide/Game-Builder.asciidoc +++ b/docs/developer-guide/Game-Builder.asciidoc @@ -60,15 +60,6 @@ sets up the perspective camera, lights and a `Model` per element. Put game logic Loading and playing a level at runtime is three lines: -[source,java] ----- -GameLevel level = GameLevel.load( - Display.getInstance().getResourceAsStream(MyScene.class, "/games/Level1.game")); -GameSceneView view = new GameSceneView(level, AssetCatalog.load(packsJson)); -form.add(BorderLayout.CENTER, view); -form.show(); -view.start(); ----- === Getting started @@ -76,7 +67,7 @@ Create a scene from a *Java 17* Codename One project: [source,bash] ---- -mvn cn1:create-game-scene -DclassName=com.example.game.Level1 -Dmode=2d +include::../demos/common/src/main/snippets/developer-guide/game-builder.sh[tag=game-builder-bash-001,indent=0] ---- `-Dmode` is `2d` (default), `3d` or `board`. This writes @@ -88,7 +79,7 @@ builder: [source,bash] ---- -mvn cn1:gamebuilder +include::../demos/common/src/main/snippets/developer-guide/game-builder.sh[tag=game-builder-bash-002,indent=0] ---- The editor itself is delivered through Maven: the goal resolves the @@ -188,27 +179,6 @@ companion's `onUpdate(double dt)`. `GameSceneView` realizes every element into a whose `getUserData()` is the source `GameElement`, so you read the properties you set in the editor: -[source,java] ----- -@Override -protected void onUpdate(double dt) { - GameInput in = getInput(); - Scene scene = getScene(); - for (int i = 0; i < scene.size(); i++) { - Sprite s = scene.get(i); - GameElement el = (GameElement) s.getUserData(); - if (el == null) continue; - switch (el.getAssetId()) { - case "player" -> { - if (in.isGameKeyDown(Display.GAME_RIGHT)) s.setX(s.getX() + 200 * dt); - // jump height, lives, gravity... all read from el.getInt(...)/getDouble(...) - } - case "slime" -> s.setX(s.getX() + el.getDouble("speed", 1.5)); // patrol - case "coin" -> { if (s.intersects(player)) score += el.getInt("value", 10); } - } - } -} ----- Win/lose conditions, scoring, enemy AI and level transitions (using a door's `target`) are all decided here. For rigid-body games use @@ -266,14 +236,6 @@ companion in your app, where the scene is rendered by the GPU-accelerated companion `.java`. To show the scene in your app, instantiate the companion and `start()` it: -[source,java] ----- -Form game = new Form("Play", new BorderLayout()); -Level1 scene = new Level1(StarterPacks.loadCatalog()); -game.add(BorderLayout.CENTER, scene); -game.show(); -scene.start(); ----- The level is plain data, so you can ship many `.game` files and load whichever level the player selects. diff --git a/docs/developer-guide/Game-Development.asciidoc b/docs/developer-guide/Game-Development.asciidoc index 89f11cf40d5..10772acf784 100644 --- a/docs/developer-guide/Game-Development.asciidoc +++ b/docs/developer-guide/Game-Development.asciidoc @@ -72,29 +72,6 @@ software rasterizer in the simulator -- so there's no EDT busy loop and no frame rate to manage. You build your world by adding `Sprite`s to its `Scene`, advance the game in `update(double)`, and the view draws the scene every frame. -[source,java] ----- -class MyGame extends GameView { - final Sprite player = new Sprite(playerImage); - - MyGame() { - getScene().add(player); - player.setPosition(160, 240); - } - - protected void update(double dt) { - if (getInput().isGameKeyDown(Display.GAME_RIGHT)) { - player.setX(player.getX() + 200 * dt); // 200 px/second - } - } -} - -Form f = new Form("Game", new BorderLayout()); -MyGame game = new MyGame(); -f.add(BorderLayout.CENTER, game); -f.show(); -game.start(); ----- The `dt` passed to `update` is the wall clock time since the previous frame. Multiplying movement by it keeps the game running at the same speed regardless of @@ -107,13 +84,6 @@ sprites is all you do; the GPU renderer handles the rest (see <>). suspend `update` while the view keeps drawing the last frame. `start()` is safe to call before or after the form is shown. -[source,java] ----- -game.start(); // begin the loop -game.pause(); // freeze game logic, keep rendering -game.resume(); // continue -game.stop(); // end the loop ----- `setClearColor(int argb)` sets the background the view is cleared to each frame. @@ -126,10 +96,6 @@ catch up (capped to avoid a "spiral of death" after a long pause). The leftover fraction is available from `getInterpolationAlpha()` (0 to 1) so you can interpolate rendered positions between physics states: -[source,java] ----- -game.setFixedTimestep(1.0 / 120.0); // step physics at a steady 120Hz ----- ==== Threading @@ -157,12 +123,6 @@ has run. the same across keyboards and devices. Pointer coordinates are reported relative to the `GameView`'s top left. -[source,java] ----- -GameInput in = getInput(); -if (in.isGameKeyDown(Display.GAME_FIRE)) { fire(); } -if (in.wasPointerPressed()) { spawnAt(in.getPointerX(), in.getPointerY()); } ----- === On-screen touch controls @@ -187,22 +147,6 @@ margin. The framework keeps them clear of notches and home indicators and repositions them automatically when the view resizes or the device rotates -- you never recompute coordinates: -[source,java] ----- -TouchControls c = getControls(); - -// analog stick, bottom-left, 80px radius, 24px in from the safe-area edges -c.addJoystick(80, TouchControls.LEFT, TouchControls.BOTTOM, 24); - -// a JUMP button, bottom-right, mapped to GAME_FIRE -int jumpKey = Display.getInstance().getKeyCode(Display.GAME_FIRE); -c.addButton(jumpKey, 55, TouchControls.RIGHT, TouchControls.BOTTOM, 30) - .setLabel("Jump").setColor(0xc0ff7043); - -// then read input exactly as you would for a keyboard: -float ax = getInput().getAxisX(); // analog steering -boolean jump = getInput().wasKeyPressed(jumpKey); // or GAME_UP, etc. ----- The joystick (bottom-left) and JUMP button (bottom-right) are visible in the <> above. There are also absolutely @@ -226,15 +170,6 @@ demand. `getX()`/`getY()` is the location of the anchor point (the image center by default), and rotation and scale pivot around that anchor. -[source,java] ----- -Sprite ship = new Sprite(shipImage); -ship.setPosition(160, 240); -ship.setRotation(45); // degrees, clockwise -ship.setScale(2f); -ship.setAlpha(200); // 0 (transparent) to 255 (opaque) -getScene().add(ship); ----- `getBounds()` returns the axis aligned bounding box and `intersects(Sprite)` does a quick box overlap test, handy for broad phase collision before you involve physics. @@ -250,13 +185,6 @@ scene. `GameView.getScene()` is the scene the view draws. You don't need a `GameView` to render sprites: a `SpriteRenderer` is itself a `com.codename1.gpu.Renderer`, so you can host one in a plain `RenderView`. -[source,java] ----- -SpriteRenderer r = new SpriteRenderer(); -r.getScene().add(mySprite); -RenderView view = new RenderView(r).setContinuous(true); -form.add(BorderLayout.CENTER, view); ----- ==== Sprite sheets and animation @@ -268,14 +196,6 @@ https://www.codenameone.com/javadoc/com/codename1/gaming/AnimatedSprite.html[`An is a `Sprite` that cycles through a sequence of frames over time; the scene advances it every frame, so adding it to the scene is all you need. -[source,java] ----- -SpriteSheet sheet = new SpriteSheet(explosionImage, 64, 64); -AnimatedSprite boom = new AnimatedSprite( - sheet, new int[]{0, 1, 2, 3, 4, 5}, 0.05); // 50ms per frame -boom.setLooping(false); -getScene().add(boom); ----- image::img/game-spritesheet.png[A six-frame run cycle laid out as a sprite sheet,640] @@ -284,21 +204,6 @@ arms and legs swing a little further each frame, and playing them in sequence re as motion. `AnimatedSprite` also accepts a plain `com.codename1.ui.Image[]` when you build the frames yourself, which is what `ScrollerGameSample`'s runner does: -[source,java] ----- -AnimatedSprite hero = new AnimatedSprite(runFrames, 0.09); // Image[6], 90ms each -hero.play(); -getScene().add(hero); - -// in update(double dt): -if (moving) { - hero.play(); - hero.setScale(facing, 1); // facing is -1 (left) or 1 (right) -} else { - hero.pause(); - hero.setCurrentFrame(0); // standing pose -} ----- Setting a *negative scale* flips the sprite horizontally, so a single set of frames faces both ways -- no mirrored art needed (sprites are drawn double-sided for @@ -325,17 +230,6 @@ so the sample needs no assets). Changing what a card shows is just 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. @@ -344,23 +238,6 @@ runner, driven over time to play as an animation. `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` @@ -382,34 +259,11 @@ 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 @@ -417,12 +271,6 @@ 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 @@ -464,18 +312,6 @@ Call `GameCamera#setPerspective(float, float, float)` and position the camera wi space with `Sprite#setPosition(double, double, double)` and is drawn as a billboard that always faces the camera, so your existing 2D art keeps working in 3D: -[source,java] ----- -getCamera() - .setPerspective(60, 0.1f, 500f) // vertical FOV, near, far - .setPosition(0, 6, 12) // eye - .setTarget(0, 0, 0); // look-at - -Sprite tree = new Sprite(treeImage); -tree.setPosition(0, 1, 0); // world coordinates, y up -tree.setSize(2, 4); // size is now in world units, not pixels -getScene().add(tree); ----- World coordinates are right-handed with y up (the `com.codename1.gpu` convention), and a sprite's size is world units rather than pixels -- use `Sprite#setSize(float, @@ -490,21 +326,6 @@ Because GPU meshes need the `com.codename1.gpu.GraphicsDevice`, build them in `GameView#onSetup(com.codename1.gpu.GraphicsDevice)`, the render-thread hook that runs once before the first frame: -[source,java] ----- -protected void onSetup(GraphicsDevice device) { - Mesh cubeMesh = Primitives.cube(device, 1f); - Material gold = new Material(Material.Type.PHONG).setColor(0xffffcc33).setShininess(32); - crate = new Model(cubeMesh, gold).setPosition(0, 0.5f, 0); - addModel(crate); - - getLight().setDirection(-1, -1, -0.5f); // shade the lit material -} - -protected void update(double dt) { - crate.setRotation(0, crate.getRotationY() + (float) (60 * dt), 0); // spin -} ----- `onSetup` is also where you load real assets with `com.codename1.gpu.GltfLoader` (glTF `.glb`/`.gltf` models). Models are drawn opaque and depth-written before the @@ -522,19 +343,6 @@ Lit materials multiply their base color by a texture, so you can detail a surfac a grid on the ground for depth, say -- by drawing an `com.codename1.ui.Image` and uploading it with `com.codename1.gpu.GraphicsDevice#createTexture(com.codename1.ui.Image)`: -[source,java] ----- -Mesh ground = Primitives.quad(device, 64f); -Texture grid = device.createTexture(makeGridImage()); // any Image you draw -Material mat = new Material(Material.Type.LAMBERT) - .setColor(0xff3f7d4f).setTexture(grid); -Model floor = new Model(ground, mat).setRotation(-90, 0, 0); // lay the quad flat -addModel(floor); - -// one cube mesh, scaled into buildings of varying height: -Model tower = new Model(cubeMesh, brick).setScale(1.5f, 4f, 1.5f).setPosition(9, 2, 0); -addModel(tower); ----- For richer geometry, load glTF (`.glb`/`.gltf`) assets with `com.codename1.gpu.GltfLoader` from `onSetup`. @@ -547,15 +355,6 @@ https://www.codenameone.com/javadoc/com/codename1/gaming/SoundPool.html[`SoundPo loads short clips once and triggers them with minimal latency, mixing several at the same time. -[source,java] ----- -SoundPool sfx = SoundPool.create(12); // up to 12 simultaneous voices -SoundEffect coin = sfx.load("/coin.wav"); -// ... in the game loop: -coin.play(); // fire and forget -int voice = coin.play(0.8f, -0.3f, 1.2f, 0); // volume, pan, rate/pitch, loop -sfx.setVolume(voice, 0.5f); // adjust a playing voice ----- `play` returns a voice id (or `-1` if the pool is momentarily exhausted -- it never blocks on the hot path). The parameters are: `volume` 0 to 1, `pan` -1 (left) to 1 @@ -576,16 +375,6 @@ https://www.codenameone.com/javadoc/com/codename1/gaming/VoiceListener.html[`Voi It's called on the EDT with the voice id as each voice completes. Not every native engine can report completion, so guard with `isVoiceCompletionSupported()`: -[source,java] ----- -if (sfx.isVoiceCompletionSupported()) { - sfx.setVoiceListener(new VoiceListener() { - public void onComplete(int voiceId) { - onEffectFinished(voiceId); - } - }); -} ----- NOTE: Load your sounds once, up front -- ideally on a background thread with `SoundPool.loadAsync(...)` -- and keep the `SoundEffect` references for the life of @@ -612,21 +401,6 @@ Create a https://www.codenameone.com/javadoc/com/codename1/gaming/physics/Physic populate it with https://www.codenameone.com/javadoc/com/codename1/gaming/physics/PhysicsBody.html[`PhysicsBody`] objects, and step it once per frame from your `update`: -[source,java] ----- -PhysicsWorld world = new PhysicsWorld(0, 900); // gravity 900 px/s^2 downward - -// a STATIC floor that never moves -world.createBox(160, 460, 320, 40, BodyType.STATIC); - -// a DYNAMIC crate affected by gravity and collisions -PhysicsBody crate = world.createBox(160, 0, 32, 32, BodyType.DYNAMIC); -crate.setRestitution(0.4f); // bounciness -crate.setFriction(0.5f); - -// in update(double dt): -world.step((float) dt); ----- Bodies come in three kinds, the https://www.codenameone.com/javadoc/com/codename1/gaming/physics/BodyType.html[`BodyType`] @@ -645,15 +419,6 @@ usable by any body type, while a concave, larger, or open subpath becomes a one- edge chain (ideal for static terrain). This lets the hit-shape and the drawn shape come from one piece of geometry: -[source,java] ----- -GeneralPath ramp = new GeneralPath(); -ramp.moveTo(0, 0); -ramp.lineTo(240, 0); -ramp.lineTo(240, 80); -ramp.closePath(); -world.createShape(40, 380, ramp, BodyType.STATIC); // collide with exactly what you draw ----- ==== Driving sprites from physics @@ -662,15 +427,6 @@ https://www.codenameone.com/javadoc/com/codename1/gaming/physics/PhysicsLinkable so linking the two means `world.step(...)` updates the sprite's position and rotation automatically (in pixels, screen space) -- and the scene draws it there: -[source,java] ----- -Sprite crateSprite = new Sprite(crateImage); -crate.setLinkedSprite(crateSprite); -getScene().add(crateSprite); - -// in update(double dt): -world.step((float) dt); // moves crate, which moves crateSprite, which the scene draws ----- ==== Collisions @@ -680,17 +436,6 @@ to be told when bodies start and stop touching. Callbacks fire from inside directly, but you must not create or destroy bodies during the callback; defer that until after `step` returns. -[source,java] ----- -world.addContactListener(new ContactListener() { - public void beginContact(PhysicsContact c) { - Object a = c.getSpriteA(); // the linked sprites, if any - Object b = c.getSpriteB(); - // e.g. flag the bodies for removal after this step - } - public void endContact(PhysicsContact c) { } -}); ----- If you need a feature the wrapper doesn't expose, `PhysicsWorld.getNativeWorld()` and `PhysicsBody.getNativeBody()` give you the underlying engine objects (which @@ -703,19 +448,6 @@ welds. `PhysicsWorld` creates them in pixel coordinates and returns a https://www.codenameone.com/javadoc/com/codename1/gaming/physics/PhysicsJoint.html[`PhysicsJoint`] handle: -[source,java] ----- -// a hinge the two bodies pivot around (anchor in pixels) -world.createRevoluteJoint(bodyA, bodyB, pivotXpx, pivotYpx); -// a fixed-length link, like a rod between two crates -world.createDistanceJoint(bodyA, bodyB, ax, ay, bx, by, 0f, 0f); -// drag a body toward the finger -- great for "pick up and throw" -PhysicsJoint drag = world.createMouseJoint(ground, body, px, py, 1000f); -// ... while the finger moves: -drag.setTarget(getInput().getPointerX(), getInput().getPointerY()); -// ... on release: -drag.destroy(); ----- `createWeldJoint` and `createPrismaticJoint` (a sliding axis) round out the set. diff --git a/docs/developer-guide/In-Car-Experiences.asciidoc b/docs/developer-guide/In-Car-Experiences.asciidoc index 91ad1e0bf0f..592ae7164bf 100644 --- a/docs/developer-guide/In-Car-Experiences.asciidoc +++ b/docs/developer-guide/In-Car-Experiences.asciidoc @@ -13,35 +13,6 @@ This API is *zero cost when unused*: simply referencing `com.codename1.car` is w Register a single `CarApplication` from your app's `init()` -- before a head unit connects -- and return a root `CarScreen` that builds a template: -[source,java] ----- -import com.codename1.car.*; - -public class MyApp { - public void init(Object context) { - // ... normal app init ... - Car.setApplication(new MyCarApplication()); - } -} - -class MyCarApplication extends CarApplication { - public CarScreen onCreateRootScreen(CarContext context) { - return new LibraryScreen(); - } -} - -class LibraryScreen extends CarScreen { - protected CarTemplate onCreateTemplate() { - return new CarListTemplate().setTitle("Library") - .addRow(new CarRow("Now Playing").setText("Daft Punk — Discovery") - .setOnAction(ctx -> play())) - .addRow(new CarRow("Albums").setBrowsable(true) - .setOnAction(ctx -> ctx.pushScreen(new AlbumsScreen()))) - .addRow(new CarRow("Playlists").setBrowsable(true) - .setOnAction(ctx -> ctx.pushScreen(new PlaylistsScreen()))); - } -} ----- That single description renders natively on both platforms: @@ -70,32 +41,14 @@ image::img/car-androidauto-grid.png[Android Auto grid template,640] Head units enforce a hard cap on the number of rows/items they display (driver-distraction rules). Query it and trim accordingly: -[source,java] ----- -int max = context.getListRowLimit(); // 0 when unknown ----- === Screen stack & lifecycle `CarContext` manages a back stack, mirroring `androidx.car.app`'s `ScreenManager` and CarPlay's `CPInterfaceController`: -[source,java] ----- -context.pushScreen(new DetailScreen()); // drill in -context.popScreen(); // back -screen.invalidate(); // rebuild this screen's template after a model change -context.showToast("Added to queue"); ----- `CarScreen` exposes optional lifecycle hooks -- `onCreate()`, `onResume()`, `onPause()`, `onDestroy()` -- and `CarApplication` is notified of connection changes via `onCarConnected(CarContext)` / `onCarDisconnected()`. You can also observe connection globally: -[source,java] ----- -Car.addConnectionListener(new CarConnectionListener() { - public void carConnected(CarContext ctx) { startLocationStream(); } - public void carDisconnected() { stopLocationStream(); } -}); ----- === Now-playing (audio apps) diff --git a/docs/developer-guide/Index.asciidoc b/docs/developer-guide/Index.asciidoc index 352c60ee5fc..a49d8439272 100644 --- a/docs/developer-guide/Index.asciidoc +++ b/docs/developer-guide/Index.asciidoc @@ -222,15 +222,7 @@ To create a new Codename One project visit https://www.codenameone.com/initializ [source,bash] ---- -mvn archetype:generate \ - -DarchetypeGroupId=com.codenameone \ - -DarchetypeArtifactId=cn1app-archetype \ - -DarchetypeVersion=LATEST \ - -DgroupId=YOUR_GROUP_ID \ - -DartifactId=YOUR_ARTIFACT_ID \ - -Dversion=1.0-SNAPSHOT \ - -DmainName=YOUR_MAIN_NAME \ - -DinteractiveMode=false +include::../demos/common/src/main/snippets/developer-guide/index.sh[tag=index-bash-001,indent=0] ---- This command generates a project in the current directory. The folder name matches the `artifactId` value. For example, specifying `-DartifactId=myapp` produces a project inside a new `myapp` directory. @@ -302,12 +294,7 @@ After clicking finish in the new project wizard you have a `HelloWorld` project [source,java,title='HelloWorld Class'] ---- -public class HelloWorld { // <1> - private Form current; // <2> - private Resources theme; // <3> - - // ... class methods ... -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IndexJava001Snippet.java[tag=index-java-001,indent=0] ---- <1> This is the main class, it's the entry point to the app, notice it doesn't have a `main` method but rather callback which this guide covers soon @@ -318,72 +305,11 @@ public class HelloWorld { // <1> Next consider the first lifecycle method `init(Object)`. The <> discusses the lifecycle in depth: -[source,java,title='HelloWorld init(Object)'] ----- -public void init(Object context) { // <1> - updateNetworkThreadCount(2); // <2> - theme = UIManager.initFirstTheme("/theme"); // <3> - Toolbar.setGlobalToolbar(true); // <4> - Log.bindCrashProtection(true); // <5> - addNetworkErrorListener(err -> { // <6> - err.consume(); // <7> - if(err.getError() != null) { // <8> - Log.e(err.getError()); - } - Log.sendLogAsync(); // <9> - Dialog.show("Connection Error", // <10> - "There was a networking error in the connection to " + - err.getConnectionRequest().getUrl(), "OK", null); - }); -} ----- - -<1> `init` is the first of the four lifecycle methods. It's responsible for initialization of variables and values - -<2> By default Codename One has one thread that performs all the networking, you set the default to two which provides better performance - -<3> The theme determines the appearance of the application. You will discuss this in the next chapter - -<4> This enables the `Toolbar` API by default, it allows finer control over the title bar area - -<5> Crash protection automatically sends device crash logs through the cloud - -<6> In case of a network error the code in this block would run, you can customize it to handle networking errors effectively - -<7> `consume()` swallows the event so it doesn't trigger other alerts, it means "`you got this`" - -<8> Not all errors include an exception, if you have an exception you can log it with this code - -<9> This will email the log from the device to you if you have a pro subscription - -<10> This shows an error dialog to the user, in production you might want to remove that code `init(Object)` works as a constructor to some degree. Recommend avoiding the constructor for the main class and placing logic in the init method instead. This isn't crucial but recommend it since the constructor might happen too early in the application lifecycle. In a cold start `init(Object)` is invoked followed by the `start()` method. For example, `start()` can be invoked more than once if an app is minimized and restored, see the sidebar <>: -[source,java,title='HelloWorld start()'] ----- -public void start() { - if(current != null){ // <1> - current.show(); // <2> - return; - } - Form hi = new Form("Hi World", BoxLayout.y()); // <3> - hi.add(new Label("Hi World")); // <4> - hi.show(); // <5> -} ----- - -<1> If the app was minimized you don't want to do much, show the last `Form` of the application - -<2> `current` is a `Form` which is the top most visual element. You can have one `Form` showing and you enforce that by using the `show()` method - -<3> You create a new simple `Form` instance. It has the title "`Hello World`" and arranges elements vertically (on the Y axis) - -<4> You add another `Label` below the title, see figure <>. You will discuss component hierarchy later - -<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 @@ -422,27 +348,6 @@ IMPORTANT: `destroy()` is optional there is no guarantee that it will be invoked Now that you have a general sense of the lifecycle lets look at the last two lifecycle methods: -[source,java,title='HelloWorld stop() and destroy()'] ----- -public void stop() { // <1> - current = getCurrentForm(); // <2> - if(current instanceof Dialog) { // <3> - ((Dialog)current).dispose(); - current = getCurrentForm(); - } -} - -public void destroy() { // <4> -} ----- - -<1> `stop()` is invoked when the app is minimized or a different app is opened - -<2> As the app is stopped you save the current `Form` so you can restore it back in `start()` if the app is restored - -<3> `Dialog` is a bit of a special case restoring a `Dialog` might block the proper flow of application execution so you dispose them and then get the parent `Form` - -<4> `destroy()` is a special case. Under normal circumstances you shouldn't write code in `destroy()`. `stop()` should work for most cases That's it. You should now have a general sense of the code. It's time to run on the device. @@ -593,86 +498,10 @@ Due to the way Kotlin works you can create a regular Java project and convert so The hello world Java source file looks like this (removed some comments and whitespace): -[source,java] ----- -public class MyApplication { - private Form current; - private Resources theme; - - public void init(Object context) { - theme = UIManager.initFirstTheme("/theme"); - Toolbar.setGlobalToolbar(true); - Log.bindCrashProtection(true); - } - - public void start() { - if(current != null){ - current.show(); - return; - } - Form hi = new Form("Hi World", BoxLayout.y()); - hi.add(new Label("Hi World")); - hi.show(); - } - - public void stop() { - current = getCurrentForm(); - if(current instanceof Dialog) { - ((Dialog)current).dispose(); - current = getCurrentForm(); - } - } - - public void destroy() { - } -} ----- -When you select that file and select the menu option #Code# -> #Convert Java file to Kotlin File# you should get this: +When you select that file and select the menu option #Code# -> #Convert Java file to Kotlin File# you should get a familiar structure. The problem is that there are two bugs in the automatic conversion... That's the code for Kotlin behaves differently from standard Java. -[source,kotlin] ----- -class MyApplication { - private var current: Form? = null - private var theme: Resources? = null - - fun init(context: Any) { - theme = UIManager.initFirstTheme("/theme") - Toolbar.setGlobalToolbar(true) - Log.bindCrashProtection(true) - } - - fun start() { - if (current != null) { - current!!.show() - return - } - val hi = Form("Hi World", BoxLayout.y()) - hi.add(Label("Hi World")) - hi.show() - } - - fun stop() { - current = getCurrentForm() - if (current is Dialog) { - (current as Dialog).dispose() - current = getCurrentForm() - } - } - - fun destroy() { - } -} ----- - -That's pretty familiar. The problem is that there are two bugs in the automatic conversion... That's the code for Kotlin behaves differently from standard Java. - -The first problem is that Kotlin classes are final unless declared otherwise so you need to add the open keyword before the class declaration as such: - -[source,kotlin] ----- -open class MyApplication ----- +The first problem is that Kotlin classes are final unless declared otherwise so you need to add the open keyword before the class declaration. This is essential as the build server will fail with weird errors related to instanceof. @@ -680,40 +509,4 @@ NOTE: This applies to the main class of the project, other classes in Codename O The second problem is that arguments are non-null by default. The `init` method might have a null argument. This fails with an exception. The solution is to add a question mark to the end of the call: `fun init(context: Any?)`. -The full working sample is: - -[source,kotlin] ----- -open class MyApplication { - private var current: Form? = null - private var theme: Resources? = null - fun init(context: Any?) { - theme = UIManager.initFirstTheme("/theme") - Toolbar.setGlobalToolbar(true) - Log.bindCrashProtection(true) - } - - fun start() { - if (current != null) { - current!!.show() - return - } - val hi = Form("Hi World", BoxLayout.y()) - hi.add(Label("Hi World")) - hi.show() - } - - fun stop() { - current = getCurrentForm() - if (current is Dialog) { - (current as Dialog).dispose() - current = getCurrentForm() - } - } - - fun destroy() { - } -} ----- - Once all that's in place Kotlin should work. This should be possible for more JVM languages in the future. diff --git a/docs/developer-guide/Maps.asciidoc b/docs/developer-guide/Maps.asciidoc index e1c5058578d..ba2b596e9c6 100644 --- a/docs/developer-guide/Maps.asciidoc +++ b/docs/developer-guide/Maps.asciidoc @@ -15,9 +15,7 @@ The pure-vector `MapView` renders real maps with zero configuration and no API k [source,java] ---- -MapView map = new MapView(); -map.moveCamera(new LatLng(37.7749, -122.4194), 12); -form.add(BorderLayout.CENTER, map); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava001Snippet.java[tag=maps-java-001,indent=0] ---- `LatLng` is the immutable WGS84 coordinate value type used throughout the API. The camera is described by a center `LatLng` and a fractional zoom level (the standard slippy-map scale where each whole increment doubles the scale). @@ -28,38 +26,6 @@ image::img/maps-vector.png[The pure-vector MapView rendering OpenStreetMap data, Every map -- vector or native -- exposes the same operations through `MapSurface`: -[source,java] ----- -// Camera -map.setCameraPosition(new CameraPosition(new LatLng(48.8566, 2.3522), 11)); -map.moveCamera(new LatLng(48.8566, 2.3522), 11); -map.setZoom(13); -map.fitBounds(new MapBounds(new LatLng(48.8, 2.2), new LatLng(48.9, 2.4)), 24); - -// Markers -Marker m = map.addMarker(new MarkerOptions(new LatLng(48.8584, 2.2945)) - .icon(pinImage) - .title("Eiffel Tower") - .anchor(0.5f, 1.0f) - .onClick(e -> showDetails())); -map.removeMarker(m); - -// Shapes -map.addPolyline(new Polyline(routePoints).setStrokeColor(0xff5722).setStrokeWidth(6)); -map.addPolygon(new Polygon(areaPoints).setFillColor(0x803f51b5).setStrokeColor(0x3f51b5)); -map.addCircle(new Circle(new LatLng(48.85, 2.35), 500).setFillColor(0x804caf50)); -map.clearMapObjects(); - -// Coordinate conversion and bounds -Point pixel = map.latLngToScreen(new LatLng(48.85, 2.35)); -LatLng coord = map.screenToLatLng(120, 240); -MapBounds visible = map.getVisibleRegion(); - -// Events -map.addTapListener((surface, location, x, y) -> placeMarker(location)); -map.addLongPressListener((surface, location, x, y) -> contextMenu(location)); -map.addCameraChangeListener((surface, camera) -> persist(camera)); ----- Marker icons are supplied as `EncodedImage` and anchored in normalized `(u, v)` image space (`0.5, 1.0` puts the pin tip on the location). When no icon is given, a marker draws the standard Material Design map pin. Polygon and circle fills accept an `0xAARRGGBB` color so you can make them translucent. @@ -78,21 +44,6 @@ image::img/maps-markers.png[Markers drawn with the default Material map pin,scal For a real app you point `MapView` at a hosted tile service. The simplest keyless option is OpenFreeMap (OpenStreetMap-based vector tiles); for branded styles or higher quotas use a keyed provider such as MapTiler or your own self-hosted Protomaps/TileServer GL endpoint: -[source,java] ----- -// Keyless, zero-config vector basemap (OpenStreetMap data via OpenFreeMap): -MapView vector = new MapView(MvtTileSource.openFreeMap(), MapStyle.light()); - -// A keyed hosted provider (e.g. MapTiler). {z}/{x}/{y} are the XYZ tile -// coordinates and {key} is substituted from setApiKey(...): -MapView branded = new MapView( - new MvtTileSource("https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key={key}", 0, 14) - .setApiKey(apiKey), - MapStyle.dark()); - -// A keyless raster (image-tile) basemap, if you don't need vector styling: -MapView raster = new MapView(RasterTileSource.openStreetMap()); ----- If the URL has no `{z}` token it's treated as a TileJSON document and the real tile-URL template (and min/max zoom) are resolved from it automatically, so you can paste a provider's TileJSON URL directly. @@ -108,13 +59,7 @@ image::img/maps-dark.png[The same vector data rendered with the built-in dark st [source,java] ---- -NativeMap map = new NativeMap(new LatLng(37.7749, -122.4194), 12); -map.addMarker(new MarkerOptions(new LatLng(37.7749, -122.4194)).title("San Francisco")); -form.add(BorderLayout.CENTER, map); - -if (!map.isNativeMap()) { - // Running on the simulator (or a build without a provider) -> vector fallback. -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MapsJava004Snippet.java[tag=maps-java-004,indent=0] ---- image::img/maps-native.png[NativeMap backed by Apple MapKit (ios.maps.provider=apple),scaledwidth=40%] @@ -139,10 +84,6 @@ The built-in providers are Apple MapKit (iOS), Google Maps (Android) and Huawei When a provider is selected the build server injects that provider's implementation into your app and wires it in; with no provider selected (or when the provider is unavailable at runtime, for example when Google Play Services is missing) `NativeMap` simply renders the vector `MapView` fallback. You can configure the fallback basemap explicitly: -[source,java] ----- -NativeMap map = new NativeMap(new LatLng(0, 0), 4, fallbackTileSource, MapStyle.light()); ----- ==== Provider API keys @@ -158,19 +99,9 @@ codename1.arg.ios.afterFinishLaunching=[GMSServices provideAPIKey:@"YOUR_IOS_API A native provider only renders on the platforms that ship its SDK. To show a provider's map *everywhere* -- including platforms with no native SDK for it -- register a `WebMapProvider`, which hosts the provider's JavaScript SDK inside a `BrowserComponent`: -[source,java] ----- -// Render Google Maps through its JavaScript SDK on any platform with a browser: -MapProviderRegistry.register(WebMapProvider.google("YOUR_MAPS_JS_API_KEY")); -NativeMap map = new NativeMap(new LatLng(41.0, 13.0), 5); ----- Because it needs only a web view, the web provider (id `web`) is the natural last step before the pure-vector fallback in a provider chain. The order is fully customizable from code, so you can express per-platform preferences -- for example "try the native Google SDK, then the web map, then the vector `MapView`": -[source,java] ----- -MapProviderRegistry.setProviderOrder(new String[]{"google", "web", "vector"}); ----- image::img/maps-web.png[Google Maps rendered cross-platform through WebMapProvider,scaledwidth=40%] diff --git a/docs/developer-guide/Maven-Appendix-Control-Center.adoc b/docs/developer-guide/Maven-Appendix-Control-Center.adoc index c8bf64b4e94..3f291a3df9f 100644 --- a/docs/developer-guide/Maven-Appendix-Control-Center.adoc +++ b/docs/developer-guide/Maven-Appendix-Control-Center.adoc @@ -13,7 +13,7 @@ Use the `run.sh` (or run.bat, if on Windows) to open Codename One settings: [source,bash] ---- -./run.sh settings +include::../demos/common/src/main/snippets/developer-guide/maven-appendix-control-center.sh[tag=maven-appendix-control-center-bash-001,indent=0] ---- ==== Opening Codename One settings from IntelliJ diff --git a/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc b/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc index de0178fc1b3..d08b4c165fd 100644 --- a/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc +++ b/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc @@ -8,10 +8,7 @@ The rich properties file (rpf) format is used to store configuration for the `ge [source,rpf] ---- -[keyname] <1> -=== <2> -Key value <3> -=== <4> +include::../demos/common/src/main/snippets/developer-guide/maven-appendix-rich-properties.txt[tag=maven-appendix-rich-properties-rpf-001,indent=0] ---- <1> The property key appears on a new line wrapped in square brackets. <2> A separator consisting of 3 or more equals signs on the next line. @@ -22,26 +19,7 @@ Key value <3> [source,rpf] ---- -firstName=Bob -lastName=Smith -[bio] -==== -Bob is a hard worker. -He attended Harvard and is looking for opportunities in the fast food industry. -==== -age=23 - -# A comment that it is ignored -[xmldata] -======== - - Bob Smith - 23 - -======== - -favoriteColor=Brown - +include::../demos/common/src/main/snippets/developer-guide/maven-appendix-rich-properties.txt[tag=maven-appendix-rich-properties-rpf-002,indent=0] ---- Many of the properties of this file are regular properties. Two rich properties exist: `bio` and `xmldata`. diff --git a/docs/developer-guide/Maven-Creating-CN1Libs.adoc b/docs/developer-guide/Maven-Creating-CN1Libs.adoc index cbd49adef6a..dd535471090 100644 --- a/docs/developer-guide/Maven-Creating-CN1Libs.adoc +++ b/docs/developer-guide/Maven-Creating-CN1Libs.adoc @@ -34,14 +34,7 @@ Use the `cn1lib-archetype` for generating a new Codename One library project as [source,bash] ---- -mvn archetype:generate \ - -DarchetypeArtifactId=cn1lib-archetype \ - -DarchetypeGroupId=com.codenameone \ - -DarchetypeVersion=LATEST \ - -DgroupId=com.example.mylib \ - -DartifactId=mylib \ - -Dversion=1.0-SNAPSHOT \ - -DinteractiveMode=false +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh[tag=maven-creating-cn1libs-bash-001,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) @@ -54,15 +47,14 @@ You can run the `archetype:generate` goal with as many or few properties as you [source,bash] ---- -mvn archetype:generate +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh[tag=maven-creating-cn1libs-bash-002,indent=0] ---- And then follow the prompts. Or you could enter: [source, bash] ---- -mvn archetype:generate -DarchetypeGroupId=com.codenameone \ - -DarchetypeArtifactId=cn1lib-archetype +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh[tag=maven-creating-cn1libs-bash-003,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) @@ -223,90 +215,7 @@ If you do a file listing on the project directory, it shows the following: [source,listing] ---- -Steves-Mac-Pro:MyFirstLibrary shannah$ find . -. -./tests -./tests/pom.xml -./tests/javase -./tests/javase/pom.xml -./tests/common -./tests/common/codenameone_settings.properties -./tests/common/pom.xml -./tests/common/nbactions.xml -./tests/common/src -./tests/common/src/test -./tests/common/src/test/java -./tests/common/src/test/java/com -./tests/common/src/test/java/com/example -./tests/common/src/test/java/com/example/myfirstlib -./tests/common/src/test/java/com/example/myfirstlib/MyFirstTest.java -./tests/common/src/main -./tests/common/src/main/css -./tests/common/src/main/css/theme.css -./tests/common/src/main/java -./tests/common/src/main/java/com -./tests/common/src/main/java/com/example -./tests/common/src/main/java/com/example/myfirstlib -./tests/common/src/main/java/com/example/myfirstlib/LibraryTests.java -./tests/cn1libs -./tests/.mvn -./tests/.mvn/jvm.config -./pom.xml -./javase -./javase/pom.xml -./javase/src -./javase/src/main -./javase/src/main/java -./javase/src/main/java/com -./javase/src/main/java/com/example -./javase/src/main/java/com/example/myfirstlib -./ios -./ios/pom.xml -./ios/src -./ios/src/main -./ios/src/main/objectivec -./common -./common/codenameone_library_required.properties -./common/pom.xml -./common/codenameone_library_appended.properties -./common/src -./common/src/test -./common/src/test/java -./common/src/test/java/com -./common/src/test/java/com/example -./common/src/test/java/com/example/myfirstlib -./common/src/test/java/com/example/myfirstlib/MyLibraryTest.java -./common/src/main -./common/src/main/css -./common/src/main/css/theme.css -./common/src/main/java -./common/src/main/java/com -./common/src/main/java/com/example -./common/src/main/java/com/example/myfirstlib -./common/src/main/java/com/example/myfirstlib/MyLibrary.java -./android -./android/pom.xml -./android/src -./android/src/main -./android/src/main/java -./android/src/main/java/com -./android/src/main/java/com/example -./android/src/main/java/com/example/myfirstlib -./lib -./lib/pom.xml -./MyFirstLibrary.iml -./javascript -./javascript/pom.xml -./javascript/src -./javascript/src/main -./javascript/src/main/javascript -./.idea -./.idea/encodings.xml -./.idea/jarRepositories.xml -./.idea/.gitignore -./.idea/workspace.xml -./.idea/misc.xml -./.idea/compiler.xml +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.txt[tag=maven-creating-cn1libs-listing-001,indent=0] ---- This may seem daunting at first, but it's important to realize that 99% of the time, you'll be working in the "common" module - most of the other stuff is boilerplate. @@ -351,7 +260,7 @@ To build the library, run the "install" goal on the root module as follows: [source,bash] ---- -mvn install +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.sh[tag=maven-creating-cn1libs-bash-004,indent=0] ---- ===== IntelliJ IDEA @@ -391,13 +300,7 @@ Add a new class inside the "common/src/main/java" directory with package `com.ex [source,java] ---- -package com.example; - -public class HelloWorld { - public static void helloWorld() { - System.out.println("Hello world"); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava001Snippet.java[tag=maven-creating-cn1libs-java-001,indent=0] ---- Now build the library again. (See <>). @@ -417,7 +320,7 @@ The common/pom.xml file will have more than one `` tag, as it incl [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.xml[tag=maven-creating-cn1libs-xml-001,indent=0] ---- You should add your dependencies before this comment. @@ -434,22 +337,13 @@ In this case you would add the following XML snippet to the `` sec [source,xml] ---- - - com.example - mylib-lib - 1.0-SNAPSHOT - pom - +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.xml[tag=maven-creating-cn1libs-xml-002,indent=0] ---- IMPORTANT: Notice that you appended "-lib" to the `artifactId`. This is because you're including the "lib" module of your library project as the dependency, and not the root module. Also the `pom` is important as it indicates that this is a pom dependency - not a regular jar dependency. Now try it out. Try adding the following code to your application project's main class (or anywhere in the application project, for that matter): -[source,java] ----- -com.example.HelloWorld.helloWorld(); ----- And build the project. The project should build OK, and if you run it, you should see that the `helloWorld()` method works as designed. @@ -465,21 +359,7 @@ Each cn1lib ships a properties file at a well-known classpath location. The simu [source,properties] ---- -# META-INF/codenameone/simulator-hooks.properties -name=Bluetooth -namespace=bluetooth # optional; defaults to slugified `name` - -# Each itemN is the action; the matching labelN is the menu text. -# Items are positional — the loader reads item1, item2, item3, ... and -# stops at the first missing index. Don't skip numbers. -item1=com.example.bt.simulator.Hooks#toggleAdapter -label1=Toggle adapter on/off - -item2=com.example.bt.simulator.Hooks#addDemoPeripheral -label2=Add demo peripheral - -# Label omitted → API-only hook. Callable from tests, invisible in menu. -item3=com.example.bt.simulator.Hooks#primeReadFailure +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.properties[tag=maven-creating-cn1libs-properties-001,indent=0] ---- Required keys: @@ -498,23 +378,6 @@ No groups, no submenus, no priority — flat by design. If you need ordering rel The simulator dispatches every action on the Codename One EDT through `Display.callSerially`, so your method can call `Display.getInstance()`, `Form.show()`, `Dialog.show()`, `ToastBar.showInfoMessage()` and any other CN1 API. Reflection uses the same classloader that loaded `Display`, so cn1lib internals (including package-private classes) resolve normally: -[source,java] ----- -package com.example.bt.simulator; - -import com.codename1.components.ToastBar; -import com.codename1.ui.Display; - -public final class Hooks { - public static void toggleAdapter() { - boolean next = !BluetoothSimulator.isEnabled(); - BluetoothSimulator.setEnabled(next); - if (Display.isInitialized()) { - ToastBar.showInfoMessage("Bluetooth adapter " + (next ? "ON" : "OFF")); - } - } -} ----- The `Display.isInitialized()` guard is a useful pattern when the same static methods are also called from JUnit tests that don't run inside a live CN1 simulator — the state mutation runs in both contexts, only the UI feedback is skipped. @@ -526,20 +389,7 @@ Its `simulator-hooks.properties` looks like this: [source,properties] ---- -name=Bluetooth -namespace=bluetooth - -item1=com.codename1.bluetoothle.BluetoothSimulatorHooks#toggleAdapter -label1=Toggle adapter on/off - -item2=com.codename1.bluetoothle.BluetoothSimulatorHooks#addDemoPeripheral -label2=Add demo peripheral - -item3=com.codename1.bluetoothle.BluetoothSimulatorHooks#switchToNativeBle -label3=Switch backend → native BLE (real hardware) - -# API-only: used by the test suite but never displayed in the menu -item4=com.codename1.bluetoothle.BluetoothSimulatorHooks#primeReadFailure +include::../demos/common/src/main/snippets/developer-guide/maven-creating-cn1libs.properties[tag=maven-creating-cn1libs-properties-002,indent=0] ---- When the user runs an app that depends on `cn1-bluetooth`, the simulator's menu bar gets a *Bluetooth* menu with the three labeled items. Clicking *Add demo peripheral* drops a peripheral into the in-memory simulator that the running app can then scan for, connect to, and exchange data with — without any real hardware. `item4` is callable from tests via `CN.execute("bluetooth:item4")` but never shows up in the menu. @@ -550,23 +400,7 @@ CN1 unit tests (`AbstractTest` subclasses run via `mvn cn1:test`) compile under [source,java] ---- -import com.codename1.testing.AbstractTest; -import com.codename1.ui.CN; - -public class BluetoothDemoTest extends AbstractTest { - @Override - public boolean runTest() throws Exception { - // Skip cleanly off-simulator: a real device has no hook registered - // and CN.canExecute will not return TRUE. - if (!Boolean.TRUE.equals(CN.canExecute("bluetooth:item2"))) { - return true; - } - // Seed the simulator — same effect as clicking "Add demo peripheral". - CN.execute("bluetooth:item2"); - // ...now drive the public Bluetooth API as usual. - return true; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MavenCreatingCn1libsJava004Snippet.java[tag=maven-creating-cn1libs-java-004,indent=0] ---- A common pattern: ship label-bearing hooks for actions a developer might want to fire manually (toggle adapter, inject notification), and ship label-less hooks for test-only state setup (`primeReadFailure`, `seedFixture`) that would just clutter the menu. diff --git a/docs/developer-guide/Maven-Getting-Started.adoc b/docs/developer-guide/Maven-Getting-Started.adoc index b4fc4c21a09..57a66502522 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -11,8 +11,7 @@ After installing the JDK, point `JAVA_HOME` at it and confirm with: [source,bash] ---- -java -version -mvn -v +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-001,indent=0] ---- Both should report Java 11 or newer. The Codename One Maven plugin checks the @@ -56,15 +55,7 @@ If you prefer to generate your projects directly on the command-line, you can us [source,bash] ---- -mvn archetype:generate \ - -DarchetypeGroupId=com.codenameone \ - -DarchetypeArtifactId=cn1app-archetype \ - -DarchetypeVersion=LATEST \ - -DgroupId=YOUR_GROUP_ID \ - -DartifactId=YOUR_ARTIFACT_ID \ - -Dversion=1.0-SNAPSHOT \ - -DmainName=YOUR_MAIN_NAME \ - -DinteractiveMode=false +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-002,indent=0] ---- This will generate a project in the current directory. The project's directory will have the same name as the artifact ID you specified here. For example, If your command had `-DartifactId=myapp`, then the project will be located in a newly created directory named "myapp." @@ -76,12 +67,7 @@ This command uses the <<#cn1app-archetype>> which has the following Maven coordi [source,xml] ---- - - com.codenameone - cn1app-archetype - LATEST - maven-archetype - +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.xml[tag=maven-getting-started-xml-001,indent=0] ---- This archetype generates a bare-bones Java project (the same one described in https://shannah.github.io/cn1-maven-archetypes/cn1app-archetype-tutorial/getting-started.html[Getting Started with the Bare-bones Java App Template]). @@ -98,16 +84,7 @@ Here is an example which generates a project based on the bare-bones Kotlin temp [source,bash,subs="+attributes"] ---- -mvn com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project \ - -DarchetypeGroupId=$archetypeGroupId \ - -DarchetypeArtifactId=$archetypeArtifactId \ - -DarchetypeVersion=$archetypeVersion \ - -DartifactId=$artifactId \ - -DgroupId=$groupId \ - -Dversion=$version \ - -DmainName=$mainName \ - -DinteractiveMode=false \ - -DsourceProject=/path/to/kotlin-example-app +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-003,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) @@ -138,15 +115,7 @@ A minimal invocation of this goal would look like: [source,bash,subs="+attributes"] ---- -# Specify your the version of the codenameone-maven-plugin. -# Find the latest version at -# https://search.maven.org/search?q=a:codenameone-maven-plugin -CN1VERSION={cn1-plugin-release-version} -mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ - -DgroupId=YOUR_GROUP_ID \ - -DartifactId=YOUR_ARTIFACT_ID \ - -DsourceProject=/path/to/your/project \ - -Dcn1Version=$CN1VERSION +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-004,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) @@ -159,8 +128,7 @@ After building the project, try running it to make sure that the migration worke [source,bash,subs="+attributes"] ---- -cd myapp -./run.sh +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-005,indent=0] ---- NOTE: On Windows it would be `run.bat` instead of `run.sh`. @@ -232,19 +200,7 @@ The following is a bash script that uses curl to download this project as a zip [source,bash,subs="+attributes"] ---- -CN1_VERSION={cn1-plugin-release-version} -curl -L https://github.com/codenameone/KitchenSink/archive/v1.0-cn7.0.11.zip > master.zip -unzip master.zip -rm master.zip -mvn com.codenameone:codenameone-maven-plugin:${CN1_VERSION}:generate-app-project \ - -DarchetypeGroupId=com.codename1 \ - -DarchetypeArtifactId=cn1app-archetype \ - -DarchetypeVersion=${CN1_VERSION} \ - -DartifactId=kitchensink \ - -DgroupId=com.example \ - -Dversion=1.0-SNAPSHOT \ - -DinteractiveMode=false \ - -DsourceProject=KitchenSink-1.0-cn7.0.11 +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-006,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) @@ -307,12 +263,7 @@ As described in the https://github.com/codenameone/codenameone-google-maps#maven [source,xml] ---- - - com.codenameone - googlemaps-lib - 1.0.1 - pom - +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.xml[tag=maven-getting-started-xml-002,indent=0] ---- You should, but, look on https://search.maven.org/artifact/com.codenameone/googlemaps-lib[Maven central] to see what the latest version number is, and substitute that version into the `` tag of the snippet. @@ -327,7 +278,7 @@ The *correct* `` section, is located near the top of the file. You [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.xml[tag=maven-getting-started-xml-003,indent=0] ---- This is a special marker that's used by some Codename One tooling to help it locate the optimal place to inject dependencies. @@ -392,6 +343,6 @@ In cases like this you can use the `install-cn1lib` Maven goal to install it as [source,bash] ---- -mvn cn1:install-cn1lib -Dfile=/path/to/yourlibrary.cn1lib +include::../demos/common/src/main/snippets/developer-guide/maven-getting-started.sh[tag=maven-getting-started-bash-007,indent=0] ---- diff --git a/docs/developer-guide/Maven-Project-Templates.adoc b/docs/developer-guide/Maven-Project-Templates.adoc index 4fa29600479..6b9f80ade3d 100644 --- a/docs/developer-guide/Maven-Project-Templates.adoc +++ b/docs/developer-guide/Maven-Project-Templates.adoc @@ -13,18 +13,7 @@ The contents of this file should look like: [source,rpf] ---- -template.mainName=$YOUR_PROJECT_MAIN_NAME -template.packageName=$YOUR_PROJECT_PACKAGE_NAME - -[dependencies] -==== -... YOUR PROJECT MAVEN DEPENDENCIES ... -==== - -[parentDependencies] -==== -... YOUR PARENT PROJECT MAVEN DEPENDENCIES ... -==== +include::../demos/common/src/main/snippets/developer-guide/maven-project-templates.txt[tag=maven-project-templates-rpf-001,indent=0] ---- Where you make the following substitutions: diff --git a/docs/developer-guide/Maven-Updating-Codename-One.adoc b/docs/developer-guide/Maven-Updating-Codename-One.adoc index b8ea48f0f99..a4157a177d3 100644 --- a/docs/developer-guide/Maven-Updating-Codename-One.adoc +++ b/docs/developer-guide/Maven-Updating-Codename-One.adoc @@ -8,7 +8,7 @@ For example: [source,bash] ---- -mvn cn:update +include::../demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.sh[tag=maven-updating-codename-one-bash-001,indent=0] ---- [discrete] @@ -18,7 +18,7 @@ Or you can use the `run.sh/run.bat` script to run this goal as follows: [source,bash] ---- -./run.sh update +include::../demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.sh[tag=maven-updating-codename-one-bash-002,indent=0] ---- NOTE: Use `run.bat update` instead on Windows @@ -50,8 +50,7 @@ For example, Open the `pom.xml` file, and look for the following: [source,xml,subs="+attributes"] ---- -{cn1-plugin-release-version} -{cn1-release-version} +include::../demos/common/src/main/snippets/developer-guide/maven-updating-codename-one.xml[tag=maven-updating-codename-one-xml-001,indent=0] ---- Change these values to reflect the latest version of the `codenameone-maven-plugin` found https://search.maven.org/artifact/com.codenameone/codenameone-maven-plugin[here]. diff --git a/docs/developer-guide/Media-And-Audio.asciidoc b/docs/developer-guide/Media-And-Audio.asciidoc index 09e1c45e50c..21bbec9c221 100644 --- a/docs/developer-guide/Media-And-Audio.asciidoc +++ b/docs/developer-guide/Media-And-Audio.asciidoc @@ -1,7 +1,7 @@ [[media-and-audio-section]] = Media and Audio -[[mediamanager-section]] +[[media-audio-mediamanager-section]] == MediaManager and MediaPlayer // HTML_ONLY_START @@ -22,26 +22,7 @@ In the demo code below you use the gallery functionality to pick a video from th [source,java] ---- -final Form hi = new Form("MediaPlayer", new BorderLayout()); -hi.setToolbar(new Toolbar()); -Style s = UIManager.getInstance().getComponentStyle("Title"); -FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s); -hi.getToolbar().addCommandToRightBar("", icon, (evt) -> { - Display.getInstance().openGallery((e) -> { - if(e != null && e.getSource() != null) { - String file = (String)e.getSource(); - try { - Media video = MediaManager.createMedia(file, true); - hi.removeAll(); - hi.add(BorderLayout.CENTER, new MediaPlayer(video)); - hi.revalidate(); - } catch(IOException err) { - Log.e(err); - } - } - }, Display.GALLERY_VIDEO); -}); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava001Snippet.java[tag=media-and-audio-java-001,indent=0] ---- .Video playback running on the simulator @@ -61,85 +42,21 @@ The portable live PCM source in Codename One is microphone capture: create an `A [source,java] ---- -String bufferKey = "voice-buffer"; -AudioBuffer livePcm = MediaManager.getAudioBuffer(bufferKey, true, 8192); -livePcm.addCallback(buffer -> { - float[] frame = new float[buffer.getSize()]; - buffer.copyTo(frame); - // Process or copy this frame before the next callback arrives. -}); - -Media recorder = MediaManager.createMediaRecorder( - new MediaRecorderBuilder() - .path(bufferKey) - .samplingRate(44100) - .audioChannels(1) - .redirectToAudioBuffer(true)); -recorder.play(); - -// Later: -recorder.cleanup(); -MediaManager.releaseAudioBuffer(bufferKey); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MediaAndAudioJava002Snippet.java[tag=media-and-audio-java-002,indent=0] ---- When the `VideoIO` API is available, it can also produce PCM from a prerecorded video file. `VideoIO.getVideoIO().openReader(path).readAudio()` returns the decoded audio track as an `AudioBuffer`, so video audio can be mixed or processed with the same APIs shown below. `VideoIO` support is platform-gated with `VideoIO.isSupported()`, and JavaScript currently reports no decoded audio track even though frame decoding and encoding are supported. -[source,java] ----- -if (VideoIO.isSupported()) { - VideoReader reader = VideoIO.getVideoIO().openReader(videoPath); - AudioBuffer videoPcm = reader.readAudio(); - if (videoPcm != null) { - // Mix or process videoPcm. - } -} ----- Codename One still doesn't expose a standalone portable decoder that converts an arbitrary prerecorded audio-only file, such as MP3 or AAC, into an `AudioBuffer` without going through a video container API. Use the normal `Media` APIs to play those files. If you need offline PCM editing for audio-only formats, decode them before passing samples to Codename One, or add a platform decoder API for that format. When you need to combine PCM clips before writing or playing them, use `AudioMixer`. It creates a sample-accurate timeline on one sample clock. Each track must use the same sample rate and channel count as the mixer. Track offsets are supplied in milliseconds and are rounded to the nearest sample frame on the mixer clock; if you already know the exact frame position, use `addTrackAtFrame(...)`. -[source,java] ----- -AudioBuffer music = new AudioBuffer(musicPcm.length); -music.copyFrom(44100, 2, musicPcm); - -AudioBuffer effect = new AudioBuffer(effectPcm.length); -effect.copyFrom(44100, 2, effectPcm); - -AudioMixer mixer = new AudioMixer(44100, 2); -mixer.addTrack(music, 0, 0.75f); // Start immediately at 75% gain -mixer.addTrack(effect, 250, 1.0f); // Start 250 ms into the timeline - -AudioBuffer mixed = mixer.mix(); -float[] pcm = new float[mixed.getSize()]; -mixed.copyTo(pcm); - -WAVWriter writer = new WAVWriter(new File("mix.wav"), mixed.getSampleRate(), mixed.getNumChannels(), 16); -try { - writer.write(pcm, 0, mixed.getSize()); -} finally { - writer.close(); -} ----- The mixed buffer is clipped to the normalized PCM range `[-1, 1]`, so it can be passed directly to `WAVWriter`. The mixer doesn't resample or convert channel layouts; downsample or otherwise convert source buffers before adding them if they're not already on the target clock. `AudioEffects` provides small platform-neutral PCM transforms that compose with the mixer: -[source,java] ----- -AudioBuffer voice = AudioEffects.normalize(livePcm, 0.9f); -AudioBuffer brighter = AudioEffects.equalize(voice, - 0.8f, // low band gain - 1.0f, // mid band gain - 1.25f // high band gain -); - -AudioBuffer karaoke = AudioEffects.removeCenter(stereoMusic); -AudioBuffer centeredSpeech = AudioEffects.isolateCenter(stereoInterview); -AudioBuffer voiceEnhanced = AudioEffects.midSide(stereoInterview, 1.4f, 0.6f); ----- `equalize(...)` is a simple three-band tone shaper for lightweight cleanup and demos. `removeCenter(...)`, `isolateCenter(...)`, and `midSide(...)` use classic stereo mid/side processing. They can reduce, isolate, or enhance centered dialog only when the source is stereo and the voice is actually centered. These methods aren't source-separation, speech-isolation, or noise-reduction APIs. diff --git a/docs/developer-guide/Miscellaneous-Features.asciidoc b/docs/developer-guide/Miscellaneous-Features.asciidoc index 2ee269f5883..70b5547b568 100644 --- a/docs/developer-guide/Miscellaneous-Features.asciidoc +++ b/docs/developer-guide/Miscellaneous-Features.asciidoc @@ -10,13 +10,7 @@ Codename One supports sending SMS messages but not receiving them as this functi [source,java] ---- -try { - Display.getInstance().sendSMS("+999999999", "My SMS Message"); - // Or: CN.sendSMS("+999999999", "My SMS Message"); -} catch(IOException err) { - Log.e(err); - Dialog.show("SMS Failed", "Unable to send the SMS", "OK", null); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava001Snippet.java[tag=miscellaneous-features-java-001,indent=0] ---- Android supports sending SMS messages in the background without any UI. iOS doesn't provide that ability, so the best it can offer is to launch the native SMS app with your message composed for the user to send. Android supports that interactive flow as well (launching the OS native SMS app). @@ -39,25 +33,6 @@ sending which is the current behavior on Android. A typical use of this API would be something like this: -[source,java] ----- -try { - switch(Display.getInstance().getSMSSupport()) { - case Display.SMS_NOT_SUPPORTED: - return; - case Display.SMS_SEAMLESS: - showUIDialogToEditMessageData(); - Display.getInstance().sendSMS(phone, data); - return; - default: - Display.getInstance().sendSMS(phone, data); - return; - } -} catch(IOException err) { - Log.e(err); - Dialog.show("SMS Failed", "Unable to send the SMS", "OK", null); -} ----- ==== Dialing @@ -67,7 +42,7 @@ You can dial the phone by using: [source,java] ---- -Display.getInstance().dial("+999999999"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava003Snippet.java[tag=miscellaneous-features-java-003,indent=0] ---- ==== Call detection @@ -84,10 +59,7 @@ On Android, call detection is unsupported because robust detection would require [source,java] ---- -Display display = Display.getInstance(); -if (display.isCallDetectionSupported() && display.isInCall()) { - Log.p("Call interruption is currently active (heuristic)"); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava004Snippet.java[tag=miscellaneous-features-java-004,indent=0] ---- ==== E-Mail @@ -96,8 +68,7 @@ You can send an email through the platforms native email client with code such a [source,java] ---- -Message m = new Message("Body of message"); -Display.getInstance().sendMessage(new String[] {"someone@gmail.com"}, "Subject of message", m); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava005Snippet.java[tag=miscellaneous-features-java-005,indent=0] ---- You can add one attachment by using `setAttachment` and `setAttachmentMimeType`. @@ -106,13 +77,6 @@ NOTE: You need to use files from `FileSystemStorage` and *NOT* `Storage` files! You can add more than one attachment by putting them directly into the attachment map for example: -[source,java] ----- -Message m = new Message("Body of message"); -m.getAttachments().put(textAttachmentUri, "text/plain"); -m.getAttachments().put(imageAttachmentUri, "image/png"); -Display.getInstance().sendMessage(new String[] {"someone@gmail.com"}, "Subject of message", m); ----- NOTE: Some features such as attachments etc. Don't work in the simulator but should work on iOS/Android @@ -124,23 +88,13 @@ If you want the theme system to automatically scale its fonts when larger text i [source,java] ---- -UIManager.getInstance().setUseLargerTextScale(true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava007Snippet.java[tag=miscellaneous-features-java-007,indent=0] ---- You can also enable this behavior in the theme by setting the `useLargerTextScaleBool` theme constant to `true`. If you need to apply the scale manually for custom fonts or layout calculations, read the values directly: -[source,java] ----- -Display display = Display.getInstance(); -if (display.isLargerTextEnabled()) { - float scale = display.getLargerTextScale(); - Font base = UIManager.getInstance().getLookAndFeel().getDefaultStyle().getFont(); - Font scaled = base.derive(base.getHeight() * scale, base.getStyle()); - someComponent.getUnselectedStyle().setFont(scaled); -} ----- ==== How the scale is computed per platform @@ -164,12 +118,6 @@ Notice that on some platforms this will prompt the user for permissions and the Once you have a https://www.codenameone.com/javadoc/com/codename1/contacts/Contact.html[Contact] you can use the `getContactById` method, but the default method is a bit slow if you want to pull a large batch of contacts. The solution for this is to extract the data that you need through -[source,java] ----- -getContactById(String id, boolean includesFullName, - boolean includesPicture, boolean includesNumbers, boolean includesEmail, - boolean includeAddress) ----- Here you can specify true for the attributes that actually matter to you. @@ -181,25 +129,6 @@ IMPORTANT: When retrieving all the contacts, notice that you should probably not You can then extract all the contacts using code that looks a bit like this, notice that you use a thread so the UI won't be blocked! -[source,java] ----- -Form hi = new Form("Contacts", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add(new InfiniteProgress()); -Display.getInstance().scheduleBackgroundTask(() -> { - Contact[] contacts = ContactsManager.getContacts(true, true, false, true, false, false); - Display.getInstance().callSerially(() -> { - hi.removeAll(); - for(Contact c : contacts) { - MultiButton mb = new MultiButton(c.getDisplayName()); - mb.setTextLine2(c.getPrimaryPhoneNumber()); - hi.add(mb); - mb.putClientProperty("id", c.getId()); - } - hi.getContentPane().animateLayout(150); - }); -}); -hi.show(); ----- .List of contacts image::img/contacts-list.png[List of contacts,scaledwidth=20%] @@ -208,38 +137,6 @@ Notice that you didn't fetch the image of the contact as the performance of load TIP: The `scheduleBackgroundTask` method is like `new Thread()` in some regards. It places elements in a queue instead of opening too many threads so it can be good for non-urgent tasks -[source,java] ----- -Form hi = new Form("Contacts", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add(new InfiniteProgress()); -int size = Display.getInstance().convertToPixels(5, true); -FontImage fi = FontImage.createFixed("" + FontImage.MATERIAL_PERSON, FontImage.getMaterialDesignFont(), 0xff, size, size); - -Display.getInstance().scheduleBackgroundTask(() -> { - Contact[] contacts = ContactsManager.getContacts(true, true, false, true, false, false); - Display.getInstance().callSerially(() -> { - hi.removeAll(); - for(Contact c : contacts) { - MultiButton mb = new MultiButton(c.getDisplayName()); - mb.setIcon(fi); - mb.setTextLine2(c.getPrimaryPhoneNumber()); - hi.add(mb); - mb.putClientProperty("id", c.getId()); - Display.getInstance().scheduleBackgroundTask(() -> { - Contact cc = ContactsManager.getContactById(c.getId(), false, true, false, false, false); - Display.getInstance().callSerially(() -> { - Image photo = cc.getPhoto(); - if(photo != null) { - mb.setIcon(photo.fill(size, size)); - mb.revalidate(); - } - }); - }); - } - hi.getContentPane().animateLayout(150); - }); -}); ----- .Contacts with the default photos on the simulator, on device these will use actual user photos when available image::img/contacts-with-photos.png[Contacts with the default photos on the simulator, on device these will use actual user photos when available,scaledwidth=20%] @@ -263,32 +160,23 @@ When you're iterating on translations you can also use the simulator to capture Open the Simulator menu and enable *Auto Update Default Bundle* so that running your app in the Codename One simulator will create any missing resource bundles on the fly as you interact with the UI, which makes it simple to populate keys without manually editing files during development. You can install the bundle using code like this: -[source,java] ----- -UIManager.getInstance().setBundle(res.getL10N("l10n", local)); ----- The device language (as an ISO 639 two-letter code) could be retrieved with this: [source,java] ---- -String local = L10NManager.getInstance().getLanguage(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava013Snippet.java[tag=miscellaneous-features-java-013,indent=0] ---- Once installed a resource bundle takes over the UI and every string set to a label (and label like components) will be automatically localized based on the bundle. You can also use the localize method of https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html[UIManager] to perform localization on your own: [source,java] ---- -UIManager.getInstance().localize( "KeyInBundle", "DefaultValue"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava014Snippet.java[tag=miscellaneous-features-java-014,indent=0] ---- The list of available languages in the resource bundle could be retrieved like this. Notice that this a list that was set by you and doesn't need to confirm to the ISO language code standards: -[source,java] ----- -Resources res = fetchResourceFile(); -Enumeration locales = res.listL10NLocales( "l10n" ); ----- An exception for localization is the `TextField`/`TextArea` components both of which contain user data, in those cases the text won't be localized to avoid accidental localization of user input. @@ -298,12 +186,7 @@ The resource bundle is a map between keys and values for example: the code below [source,java] ---- -Form hi = new Form("L10N", new BoxLayout(BoxLayout.Y_AXIS)); -HashMap resourceBudle = new HashMap(); -resourceBudle.put("Localize", "This Label is localized"); -UIManager.getInstance().setBundle(resourceBudle); -hi.add(new Label("Localize")); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java[tag=miscellaneous-features-java-016,indent=0] ---- .Localized label @@ -315,28 +198,6 @@ The https://www.codenameone.com/javadoc/com/codename1/l10n/L10NManager.html[L10N It allows formatting numbers/dates & time based on platform locale. It also provides a great deal of the information you need such as the language/locale information you need to pick the proper resource bundle: -[source,java] ----- -Form hi = new Form("L10N", new TableLayout(16, 2)); -L10NManager l10n = L10NManager.getInstance(); -hi.add("format(double)").add(l10n.format(11.11)). - add("format(int)").add(l10n.format(33)). - add("formatCurrency").add(l10n.formatCurrency(53.267)). - add("formatDateLongStyle").add(l10n.formatDateLongStyle(new Date())). - add("formatDateShortStyle").add(l10n.formatDateShortStyle(new Date())). - add("formatDateTime").add(l10n.formatDateTime(new Date())). - add("formatDateTimeMedium").add(l10n.formatDateTimeMedium(new Date())). - add("formatDateTimeShort").add(l10n.formatDateTimeShort(new Date())). - add("getCurrencySymbol").add(l10n.getCurrencySymbol()). - add("getLanguage").add(l10n.getLanguage()). - add("getLocale").add(l10n.getLocale()). - add("isRTLLocale").add("" + l10n.isRTLLocale()). - add("parseCurrency").add(l10n.formatCurrency(l10n.parseCurrency("33.77$"))). - add("parseDouble").add(l10n.format(l10n.parseDouble("34.35"))). - add("parseInt").add(l10n.format(l10n.parseInt("56"))). - add("parseLong").add("" + l10n.parseLong("4444444")); -hi.show(); ----- .Localization formatting/parsing and information image::img/l10n-manager.png[Localization formatting/parsing and information,scaledwidth=20%] @@ -395,13 +256,13 @@ image::img/ios_strings_directory_screenshot.png[] .ios/src/main/strings/en.lproj/InfoPlist.strings [source,strings] ---- -"CFBundleDisplayName"="Hello App"; +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings[tag=miscellaneous-features-strings-001,indent=0] ---- .ios/src/main/strings/en.lproj/InfoPlist.strings [source,strings] ---- -"CFBundleDisplayName"="Bonjour App"; +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings[tag=miscellaneous-features-strings-002,indent=0] ---- [NOTE] @@ -422,7 +283,7 @@ iOS requires you to supply usage descriptions for many features that will be dis [source,properties] ---- -ios.NSCameraUsageDescription=This app needs to use your camera to scan bar codes +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.properties[tag=miscellaneous-features-properties-001,indent=0] ---- These descriptions are embedded in your app's Info.plist file, so they can be localized the same way you localize other Info.plist values - in the localized `InfoPlist.strings` file. @@ -432,15 +293,13 @@ See <<_example_localizing_the_app_name, the above example>> for instructions on .ios/src/main/strings/en.lproj/InfoPlist.strings [source,strings] ---- -"CFBundleDisplayName"="Hello App"; -"NSCameraUsageDescription"="This app needs to use your camera to scan bar codes"; +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings[tag=miscellaneous-features-strings-003,indent=0] ---- .ios/src/main/strings/en.lproj/InfoPlist.strings [source,strings] ---- -"CFBundleDisplayName"="Bonjour App"; -"NSCameraUsageDescription"="Cette application doit utiliser votre appareil photo pour scanner les codes à barres"; +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.strings[tag=miscellaneous-features-strings-004,indent=0] ---- ==== Localizing the app icon @@ -449,7 +308,7 @@ Codename One can ship a different launcher icon per locale on iOS and Android by [source,text] ---- -cn1_icon_[_].png +include::../demos/common/src/main/snippets/developer-guide/miscellaneous-features.txt[tag=miscellaneous-features-text-001,indent=0] ---- `` is a two-letter ISO 639-1 language code and the optional `` is a two-letter ISO 3166-1 country code. Language is treated as case-insensitive; the country component is normalized to uppercase. A few valid examples: @@ -503,7 +362,7 @@ The most basic usage for the API allows you to fetch a device Location, notice t [source,java] ---- -Location position = LocationManager.getLocationManager().getCurrentLocationSync(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava018Snippet.java[tag=miscellaneous-features-java-018,indent=0] ---- IMPORTANT: In order for location to work on iOS you *MUST* define the build hint `ios.locationUsageDescription` and describe why your application needs access to location. Otherwise you won't get location updates! @@ -515,16 +374,7 @@ TIP: Notice that there is a method called `getCurrentLocation()` which will retu [source,java] ---- -public MyListener implements LocationListener { - public void locationUpdated(Location location) { - // update UI etc. - } - - public void providerStateChanged(int newState) { - // handle status changes/errors appropriately - } -} -LocationManager.getLocationManager().setLocationListener(new MyListener()); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava019Snippet.java[tag=miscellaneous-features-java-019,indent=0] ---- IMPORTANT: On Android location maps to low-level APIs if you disable the usage of Google Play Services. By default location should perform well if you leave the Google Play Services on @@ -551,13 +401,6 @@ Class names are problematic since device builds are obfuscated, you should use l The following code demonstrates usage of the GeoFence API: -[source,java] ----- -Geofence gf = new Geofence("test", loc, 100, 100000); - -LocationManager.getLocationManager() - .addGeoFencing(GeofenceListenerImpl.class, gf); ----- ===== Android background location permissions (API 30+) @@ -565,28 +408,6 @@ On Android 11 (API level 30) and higher, requesting background location permissi For Android 11+ (API 30+), Codename One detects if background location is needed and presents a dialog explaining the need before redirecting the user to the app settings. You can customize the permission prompt message using the localization key `android.permission.ACCESS_BACKGROUND_LOCATION`: -[source,java] ----- -public class GeofenceListenerImpl implements GeofenceListener { - @Override - public void onExit(String id) { - } - - @Override - public void onEntered(String id) { - if(Display.getInstance().isMinimized()) { - Display.getInstance().callSerially(() -> { - Dialog.show("Welcome", "Thanks for arriving", "OK", null); - }); - } else { - LocalNotification ln = new LocalNotification(); - ln.setAlertTitle("Welcome"); - ln.setAlertBody("Thanks for arriving!"); - Display.getInstance().scheduleLocalNotification(ln, 10, false); - } - } -} ----- === Background music playback @@ -607,7 +428,7 @@ The API itself couldn’t be simpler: [source,java] ---- -String filePath = Capture.capturePhoto(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava022Snippet.java[tag=miscellaneous-features-java-022,indent=0] ---- Just captures and returns a path to a photo you can either open it using the https://www.codenameone.com/javadoc/com/codename1/ui/Image.html[Image] class or save it somewhere. @@ -616,13 +437,6 @@ IMPORTANT: The returned file is a temporary file, you shouldn't store a referenc For example: you can copy the `Image` to `Storage` using: -[source,java] ----- -String filePath = Capture.capturePhoto(); -if(filePath != null) { - Util.copy(FileSystemStorage.getInstance().openInputStream(filePath), Storage.getInstance().createOutputStream(myImageFileName)); -} ----- TIP: When running on the simulator the `Capture` API opens a file chooser API instead of physically capturing the data. This makes debugging device or situation specific issues simpler @@ -630,35 +444,7 @@ You can capture an image from the camera using an API like this: [source,java] ---- -Form hi = new Form("Capture", new BorderLayout()); -hi.setToolbar(new Toolbar()); -Style s = UIManager.getInstance().getComponentStyle("Title"); -FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); - -ImageViewer iv = new ImageViewer(icon); - -hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { - String filePath = Capture.capturePhoto(); - if(filePath != null) { - try { - DefaultListModel m = (DefaultListModel)iv.getImageList(); - Image img = Image.createImage(filePath); - if(m == null) { - m = new DefaultListModel<>(img); - iv.setImageList(m); - iv.setImage(img); - } else { - m.addItem(img); - } - m.setSelectedIndex(m.getSize() - 1); - } catch(IOException err) { - Log.e(err); - } - } -}); - -hi.add(BorderLayout.CENTER, iv); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava024Snippet.java[tag=miscellaneous-features-java-024,indent=0] ---- .Captured photos previewed in the ImageViewer @@ -676,56 +462,7 @@ The sample below captures audio recordings (using the 'Capture' API) and copies [source,java] ---- -Form hi = new Form("Capture", BoxLayout.y()); -hi.setToolbar(new Toolbar()); -Style s = UIManager.getInstance().getComponentStyle("Title"); -FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_MIC, s); - -FileSystemStorage fs = FileSystemStorage.getInstance(); -String recordingsDir = fs.getAppHomePath() + "recordings/"; -fs.mkdir(recordingsDir); -try { - for(String file : fs.listFiles(recordingsDir)) { - MultiButton mb = new MultiButton(file.substring(file.lastIndexOf("/") + 1)); - mb.addActionListener((e) -> { - try { - Media m = MediaManager.createMedia(recordingsDir + file, false); - m.play(); - } catch(IOException err) { - Log.e(err); - } - }); - hi.add(mb); - } - - hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { - try { - String file = Capture.captureAudio(); - if(file != null) { - SimpleDateFormat sd = new SimpleDateFormat("yyyy-MMM-dd-kk-mm"); - String fileName =sd.format(new Date()); - String filePath = recordingsDir + fileName; - Util.copy(fs.openInputStream(file), fs.openOutputStream(filePath)); - MultiButton mb = new MultiButton(fileName); - mb.addActionListener((e) -> { - try { - Media m = MediaManager.createMedia(filePath, false); - m.play(); - } catch(IOException err) { - Log.e(err); - } - }); - hi.add(mb); - hi.revalidate(); - } - } catch(IOException err) { - Log.e(err); - } - }); -} catch(IOException err) { - Log.e(err); -} -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava025Snippet.java[tag=miscellaneous-features-java-025,indent=0] ---- .Captured recordings in the demo @@ -733,207 +470,6 @@ image::img/capture-audio.png[Captured recordings in the demo,scaledwidth=20%] Or, you can use the `Media`, `MediaManager` and `MediaRecorderBuilder` APIs to capture audio, as a more customizable approach than using the Capture API: -[source,java] ----- - private static final EasyThread countTime = EasyThread.start("countTime"); - - public void start() { - if (current != null) { - current.show(); - return; - } - Form hi = new Form("Recording audio", BoxLayout.y()); - hi.add(new SpanLabel("Example of recording and playback audio using the Media, MediaManager and MediaRecorderBuilder APIs")); - hi.add(recordAudio((String filePath) -> { - ToastBar.showInfoMessage("Do something with the recorded audio file: " + filePath); - })); - hi.show(); - } - - public static Component recordAudio(OnComplete callback) { - try { - // mime types supported by Android: audio/amr, audio/aac, audio/mp4 - // mime types supported by iOS: audio/mp4, audio/aac, audio/m4a - // mime type supported by Simulator: audio/wav - // more info: https://www.iana.org/assignments/media-types/media-types.xhtml - - List availableMimetypes = Arrays.asList(MediaManager.getAvailableRecordingMimeTypes()); - String mimetype; - if (availableMimetypes.contains("audio/aac")) { - // Android and iOS - mimetype = "audio/aac"; - } else if (availableMimetypes.contains("audio/wav")) { - // Simulator - mimetype = "audio/wav"; - } else { - // others - mimetype = availableMimetypes.get(0); - } - String fileName = "audioExample." + mimetype.substring(mimetype.indexOf("/") + 1); - String output = FileSystemStorage.getInstance().getAppHomePath() + "/" + fileName; - // https://tritondigitalcommunity.force.com/s/article/Choosing-Audio-Bitrate-Settings - MediaRecorderBuilder options = new MediaRecorderBuilder() - .mimeType(mimetype) - .path(output) - .bitRate(64000) - .samplingRate(44100); - Media[] microphone = {MediaManager.createMediaRecorder(options)}; - Media[] speaker = {null}; - - Container recordingUI = new Container(BoxLayout.y()); - Label time = new Label("0:00"); - Button recordBtn = new Button("", FontImage.MATERIAL_FIBER_MANUAL_RECORD, "Button"); - Button playBtn = new Button("", FontImage.MATERIAL_PLAY_ARROW, "Button"); - Button stopBtn = new Button("", FontImage.MATERIAL_STOP, "Button"); - Button sendBtn = new Button("Send"); - sendBtn.setEnabled(false); - Container buttons = GridLayout.encloseIn(3, recordBtn, stopBtn, sendBtn); - recordingUI.addAll(FlowLayout.encloseCenter(time), FlowLayout.encloseCenter(buttons)); - - recordBtn.addActionListener(l -> { - try { - // every time we have to create a new instance of Media to make it working correctly (as reported in the Javadoc) - microphone[0] = MediaManager.createMediaRecorder(options); - if (speaker[0] != null && speaker[0].isPlaying()) { - return; // do nothing if the audio is currently recorded or played - } - recordBtn.setEnabled(false); - sendBtn.setEnabled(true); - Log.p("Audio recording started", Log.DEBUG); - if (buttons.contains(playBtn)) { - buttons.replace(playBtn, stopBtn, CommonTransitions.createEmpty()); - buttons.revalidateWithAnimationSafety(); - } - if (speaker[0] != null) { - speaker[0].pause(); - } - - microphone[0].play(); - startWatch(time); - } catch (IOException ex) { - Log.p("ERROR recording audio", Log.ERROR); - Log.e(ex); - } - }); - - stopBtn.addActionListener(l -> { - if (!microphone[0].isPlaying() && (speaker[0] == null || !speaker[0].isPlaying())) { - return; // do nothing if the audio is NOT currently recorded or played - } - recordBtn.setEnabled(true); - sendBtn.setEnabled(true); - Log.p("Audio recording stopped"); - if (microphone[0].isPlaying()) { - microphone[0].pause(); - } else if (speaker[0] != null) { - speaker[0].pause(); - } else { - return; - } - stopWatch(time); - if (buttons.contains(stopBtn)) { - buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty()); - buttons.revalidateWithAnimationSafety(); - } - if (FileSystemStorage.getInstance().exists(output)) { - Log.p("Audio saved to: " + output); - } else { - ToastBar.showErrorMessage("Error recording audio", 5000); - Log.p("ERROR SAVING AUDIO"); - } - }); - - playBtn.addActionListener(l -> { - // every time we have to create a new instance of Media to make it working correctly (as reported in the Javadoc) - if (microphone[0].isPlaying() || (speaker[0] != null && speaker[0].isPlaying())) { - return; // do nothing if the audio is currently recorded or played - } - recordBtn.setEnabled(false); - sendBtn.setEnabled(true); - if (buttons.contains(playBtn)) { - buttons.replace(playBtn, stopBtn, CommonTransitions.createEmpty()); - buttons.revalidateWithAnimationSafety(); - } - if (FileSystemStorage.getInstance().exists(output)) { - try { - speaker[0] = MediaManager.createMedia(output, false, () -> { - // callback on completation - recordBtn.setEnabled(true); - if (speaker[0].isPlaying()) { - speaker[0].pause(); - } - stopWatch(time); - if (buttons.contains(stopBtn)) { - buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty()); - buttons.revalidateWithAnimationSafety(); - } - }); - speaker[0].play(); - startWatch(time); - } catch (IOException ex) { - Log.p("ERROR playing audio", Log.ERROR); - Log.e(ex); - } - } - }); - - sendBtn.addActionListener(l -> { - if (microphone[0].isPlaying()) { - microphone[0].pause(); - } - if (speaker[0] != null && speaker[0].isPlaying()) { - speaker[0].pause(); - } - if (buttons.contains(stopBtn)) { - buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty()); - buttons.revalidateWithAnimationSafety(); - } - stopWatch(time); - recordBtn.setEnabled(true); - - callback.completed(output); - }); - - return FlowLayout.encloseCenter(recordingUI); - - } catch (IOException ex) { - Log.p("ERROR recording audio", Log.ERROR); - Log.e(ex); - return new Label("Error recording audio"); - } - - } - - private static void startWatch(Label label) { - label.putClientProperty("stopTime", Boolean.FALSE); - countTime.run(() -> { - long startTime = System.currentTimeMillis(); - while (label.getClientProperty("stopTime") == Boolean.FALSE) { - // the sleep is every 200ms instead of 1000ms to make the app more reactive when stop is tapped - Util.sleep(200); - int seconds = (int) ((System.currentTimeMillis() - startTime) / 1000); - String min = (seconds / 60) + ""; - String sec = (seconds % 60) + ""; - if (sec.length() == 1) { - sec = "0" + sec; - } - String newTime = min + ":" + sec; - if (!label.getText().equals(newTime)) { - CN.callSerially(() -> { - label.setText(newTime); - if (label.getParent() != null) { - label.getParent().revalidateWithAnimationSafety(); - } - }); - } - } - }); - } - - private static void stopWatch(Label label) { - label.putClientProperty("stopTime", Boolean.TRUE); - } ----- image::img/media-audio-recording-example.png[Example of recording and playback audio using Media API] @@ -941,29 +477,6 @@ image::img/media-audio-recording-example.png[Example of recording and playback a The `Capture` API also includes a callback based API that uses the `ActionListener` interface to implement capture. For example: you can adapt the previous sample to use this API as such: -[source,java] ----- -hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { - Capture.capturePhoto((e) -> { - if(e != null && e.getSource() != null) { - try { - DefaultListModel m = (DefaultListModel)iv.getImageList(); - Image img = Image.createImage((String)e.getSource()); - if(m == null) { - m = new DefaultListModel<>(img); - iv.setImageList(m); - iv.setImage(img); - } else { - m.addItem(img); - } - m.setSelectedIndex(m.getSize() - 1); - } catch(IOException err) { - Log.e(err); - } - } - }); -}); ----- === Gallery @@ -975,35 +488,7 @@ You can adapt the `Capture` sample above to use the gallery as such: [source,java] ---- -Form hi = new Form("Capture", new BorderLayout()); -hi.setToolbar(new Toolbar()); -Style s = UIManager.getInstance().getComponentStyle("Title"); -FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); - -ImageViewer iv = new ImageViewer(icon); - -hi.getToolbar().addCommandToRightBar("", icon, (ev) -> { - Display.getInstance().openGallery((e) -> { - if(e != null && e.getSource() != null) { - try { - DefaultListModel m = (DefaultListModel)iv.getImageList(); - Image img = Image.createImage((String)e.getSource()); - if(m == null) { - m = new DefaultListModel<>(img); - iv.setImageList(m); - iv.setImage(img); - } else { - m.addItem(img); - } - m.setSelectedIndex(m.getSize() - 1); - } catch(IOException err) { - Log.e(err); - } - } - }, Display.GALLERY_IMAGE); -}); - -hi.add(BorderLayout.CENTER, iv); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava028Snippet.java[tag=miscellaneous-features-java-028,indent=0] ---- TIP: No need for a screenshot as it will look identical to the capture image screenshot above @@ -1049,14 +534,7 @@ This creates some potential issues for instance in `MultiButton`: [source,java] ---- -myMultiButton.addActionListener((e) -> { - if(e.getComponent() == myMultiButton) { - // this won't occur since the source component is really a button! - } - if(e.getActualComponent() == myMultiButton) { - // this will happen... - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava029Snippet.java[tag=miscellaneous-features-java-029,indent=0] ---- The leader also determines the style state, so all the elements being lead are in the same state. For example: if the button is pressed all elements will display their pressed states, notice that they will do so with their own styles but @@ -1069,49 +547,7 @@ For example: the `SpanButton` class is like this code: [source,java] ---- -public class SpanButton extends Container { - private Button actualButton; - private TextArea text; - - public SpanButton(String txt) { - setUIID("Button"); - setLayout(new BorderLayout()); - text = new TextArea(getUIManager().localize(txt, txt)); - text.setUIID("Button"); - text.setEditable(false); - text.setFocusable(false); - actualButton = new Button(); - addComponent(BorderLayout.WEST, actualButton); - addComponent(BorderLayout.CENTER, text); - setLeadComponent(actualButton); - } - - - public void setText(String t) { - text.setText(getUIManager().localize(t, t)); - } - - public void setIcon(Image i) { - actualButton.setIcon(i); - } - - public String getText() { - return text.getText(); - } - - public Image getIcon() { - return actualButton.getIcon(); - } - - public void addActionListener(ActionListener l) { - actualButton.addActionListener(l); - } - - public void removeActionListener(ActionListener l) { - actualButton.removeActionListener(l); - } - -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava030Snippet.java[tag=miscellaneous-features-java-030,indent=0] ---- ==== Blocking lead behavior @@ -1126,30 +562,7 @@ The sample below is based on the `Accordion` component which uses a lead compone [source,java] ---- -Form f = new Form("Accordion", new BorderLayout()); -Accordion accr = new Accordion(); -f.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_ADD, e -> addEntry(accr)); -addEntry(accr); -f.add(BorderLayout.CENTER, accr); -f.show(); - -void addEntry(Accordion accr) { - TextArea t = new TextArea("New Entry"); - Button delete = new Button(); - FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE); - Label title = new Label(t.getText()); - t.addActionListener(ee -> title.setText(t.getText())); - delete.addActionListener(ee -> { - accr.removeContent(t); - accr.animateLayout(200); - }); - delete.setBlockLead(true); - delete.setUIID("Label"); - Container header = BorderLayout.center(title). - add(BorderLayout.EAST, delete); - accr.addContent(header, t); - accr.animateLayout(200); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava031Snippet.java[tag=miscellaneous-features-java-031,indent=0] ---- This allows you to add/edit entries but it also allows the delete button above to actually work. Without a call to `setBlockLead(true)` the delete button would cat as the rest of the accordion title. @@ -1167,11 +580,7 @@ TIP: Pull to refresh is implicitly implements in the `InifiniteContainer` [source,java] ---- -Form hi = new Form("Pull To Refresh", BoxLayout.y()); -hi.getContentPane().addPullToRefresh(() -> { - hi.add("Pulled at " + L10NManager.getInstance().formatDateTimeShort(new Date())); -}); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava032Snippet.java[tag=miscellaneous-features-java-032,indent=0] ---- `setPullToRefresh(Runnable)` is provided as a more discoverable alias for the @@ -1195,20 +604,7 @@ To enable in a custom theme: [source,css] ---- -#Constants { - pullToRefreshModernBool: true; - pullToRefreshIndicatorDiameterMm: 8; /* 8mm circle */ - pullToRefreshIndicatorStrokeMm: "0.6"; /* 0.6mm stroke thickness */ -} - -TabIndicator { - /* The pull-to-refresh arc shares its color with the animated tab - indicator -- both follow the brand's accent. */ - color: #007aff; - background-color: transparent; - padding: 0; - margin: 0; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=miscellaneous-features-css-001,indent=0] ---- Behavior: @@ -1245,12 +641,7 @@ The sample below launches a "godfather" search on IMDB when this is sure to work [source,java] ---- -Boolean can = Display.getInstance().canExecute("imdb:///find?q=godfather"); -if(can != null && can) { - Display.getInstance().execute("imdb:///find?q=godfather"); -} else { - Display.getInstance().execute("http://www.imdb.com"); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava033Snippet.java[tag=miscellaneous-features-java-033,indent=0] ---- === Automatic build hint configuration @@ -1261,24 +652,6 @@ A good example for a common problem developers face is location code that doesn' To solve this sort of used case you have two APIs in `Display`: -[source,java] ----- -/** - * Returns the build hints for the simulator, this will only work in the debug environment and it's - * designed to allow extensions/APIs to verify user settings/build hints exist - * @return map of the build hints that isn't modified without the codename1.arg. prefix - */ -public Map getProjectBuildHints() {} - -/** - * Sets a build hint into the settings while overwriting any previous value. This will only work in the - * debug environment and it's designed to allow extensions/APIs to verify user settings/build hints exist. - * Important: this will throw an exception outside of the simulator! - * @param key the build hint without the codename1.arg. prefix - * @param value the value for the hint - */ -public void setProjectBuildHint(String key, String value) {} ----- Both of these allow you to detect if a build hint is set and if not (or if it's set incorrectly) set its value... @@ -1299,37 +672,17 @@ That's why `EasyThread` exists: it takes some concepts of Codename One's threadi Easy thread can be created like this: -[source,java] ----- -EasyThread e = EasyThread.start("ThreadName"); ----- You can send a task to the thread using: -[source,java] ----- -e.run(() -> doThisOnTheThread()); ----- However, it gets better, say you want to return a value: -[source,java] ----- -e.run((success) -> success.onSuccess(doThisOnTheThread()), (myResult) -> onEDTGotResult(myRsult)); ----- Lets break that down... You ran the thread with the success callback on the new thread then the callback got invoked on the EDT as a result. This code `(success) -> success.onSuccess(doThisOnTheThread())` ran off the EDT in the thread and when you invoked the `onSuccess` callback it sent it asynchronously to the EDT here: `(myResult) -> onEDTGotResult(myRsult)`. These asynchronous calls make things a bit painful to wade through, so the API wraps them in a simplified synchronous version: -[source,java] ----- -EasyThread e = EasyThread.start("Hi"); -int result = e.run(() -> { - System.out.println("This is a thread"); - return 3; -}); ----- There are a few other variants like `runAndWait` and there is a `kill()` method which stops a thread and releases its resources. @@ -1339,11 +692,7 @@ Codename one can change the mouse cursor when hovering over specific areas to sh [source,java] ---- -@Override -protected void initComponent() { - super.initComponent(); - getComponentForm().setEnableCursors(true); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava039Snippet.java[tag=miscellaneous-features-java-039,indent=0] ---- Once this is enabled you can set the cursor over a specific region using `cmp.setCursor()` which accepts one of the cursor constants defined in `Component`. diff --git a/docs/developer-guide/Monetization.asciidoc b/docs/developer-guide/Monetization.asciidoc index 9e3364f2a0e..e69ba91df9e 100644 --- a/docs/developer-guide/Monetization.asciidoc +++ b/docs/developer-guide/Monetization.asciidoc @@ -30,82 +30,21 @@ Start with a simple example of an app that sells `Worlds`. First, pick the SKU f [source,java] ---- -public static final String SKU_WORLD = "com.codename1.world"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava001Snippet.java[tag=monetization-java-001,indent=0] ---- TIP: Although this example uses the package-name convention for a SKU, you can use any name you want, for example `UA8879`. Next, the app's main class needs to implement the `PurchaseCallback` interface -[source,java] ----- -public class HelloWorldIAP implements PurchaseCallback { - .... - - @Override - public void itemPurchased(String sku) { -... - } - - @Override - public void itemPurchaseError(String sku, String errorMessage) { -... - } - - @Override - public void paymentFailed(String paymentCode, String failureReason) { -... - } - - @Override - public void paymentSucceeded(String paymentCode, double amount, String currency) { -... - } - -} ----- Using these callbacks, the app receives notifications whenever a purchase changes. For this simple app, only `itemPurchased()` and `itemPurchaseError()` matter. The legacy `itemRefunded()`, `subscriptionStarted()`, and `subscriptionCanceled()` hooks are deprecated in the core API and are no longer dispatched by the stores. Instead of relying on callbacks for long-term entitlement state, query the current receipts at runtime with helpers such as `Purchase.wasPurchased(...)`, `Purchase.isSubscribed(...)`, or `Purchase.getReceipts()`. That keeps the UI aligned with the latest data from the underlying store. Now in the `start()` method, add a button that lets the user buy the world: -[source,java] ----- - public void start() { - if(current!= null){ - current.show(); - return; - } - Form hi = new Form("Hi World"); - Button buyWorld = new Button("Buy World"); - buyWorld.addActionListener(e->{ - if (Purchase.getInAppPurchase().wasPurchased(SKU_WORLD)) { - Dialog.show("can't Buy It", "You already Own It", "OK", null); - } else { - Purchase.getInAppPurchase().purchase(SKU_WORLD); - } - }); - - hi.addComponent(buyWorld); - hi.show(); - } ----- At this point, the app can track the sale of the world. To make it more useful, add `ToastBar` feedback for purchase completion: -[source,java] ----- - @Override - public void itemPurchased(String sku) { - ToastBar.showMessage("Thanks. You now own the world", FontImage.MATERIAL_THUMB_UP); - } - - @Override - public void itemPurchaseError(String sku, String errorMessage) { - ToastBar.showErrorMessage("Failure occurred: "+errorMessage); - } ----- - NOTE: You can test out this code in the simulator without doing any more setup and it will work. If you want the code to work on Android and iOS, you'll need to set up the app and in-app purchase settings in the Google Play and iTunes stores respectively as explained below @@ -132,53 +71,14 @@ You'll use storage to keep track of the number of worlds that the user purchased [source,java] ---- -private static final String NUM_WORLDS_KEY = "NUM_WORLDS.dat"; -public int getNumWorlds() { - synchronized (NUM_WORLDS_KEY) { - Storage s = Storage.getInstance(); - if (s.exists(NUM_WORLDS_KEY)) { - return (Integer)s.readObject(NUM_WORLDS_KEY); - } else { - return 0; - } - } -} - -public void addWorld() { - synchronized (NUM_WORLDS_KEY) { - Storage s = Storage.getInstance(); - int count = 0; - if (s.exists(NUM_WORLDS_KEY)) { - count = (Integer)s.readObject(NUM_WORLDS_KEY); - } - count++; - s.writeObject(NUM_WORLDS_KEY, new Integer(count)); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava005Snippet.java[tag=monetization-java-005,indent=0] ---- Now you'll change your buy code as follows: -[source,java] ----- -buyWorld.addActionListener(e->{ - if (Dialog.show("Confirm", "You own "+getNumWorlds()+ - " worlds. Do you want to buy another one?", "Yes", "No")) { - Purchase.getInAppPurchase().purchase(SKU_WORLD); - } -}); ----- And your `itemPurchased()` callback will need to add a world: -[source,java] ----- -@Override -public void itemPurchased(String sku) { - addWorld(); - ToastBar.showMessage("Thanks. You now own "+getNumWorlds()+" worlds", FontImage.MATERIAL_THUMB_UP); -} ----- NOTE: When you set up the products in the iTunes store you will need to mark the product as a consumable product or iTunes will prevent you from purchasing it more than once @@ -204,18 +104,6 @@ Apple allows you to present discounted introductory pricing to existing subscrib To build the signed discount payload required by Apple you can use the `ApplePromotionalOffer` helper: -[source,java] ----- -ApplePromotionalOffer offer = new ApplePromotionalOffer(); -offer.setOfferIdentifier("my-intro-offer"); -offer.setKeyIdentifier("A1B2C3D4"); -offer.setNonce(UUID.randomUUID().toString()); -offer.setSignature(signatureFromYourServer); -offer.setTimestamp(timestampFromYourServer); - -Purchase purchase = Purchase.getInAppPurchase(); -purchase.subscribe(SKU_WORLD_MONTHLY, offer); ----- Apple generates the signature and timestamp from your App Store Connect server notifications endpoint; Codename One passes them to the native StoreKit APIs. For one-time products you can call `purchase(sku, offer)` instead of `subscribe(...)`. @@ -263,16 +151,6 @@ You'll expand on the theme of "Buying" the world for this app, except, this time . A 1-month subscription . A 1-year subscription -[source,java] ----- -public static final String SKU_WORLD_1_MONTH = "com.codename1.world.subscribe.1month"; -public static final String SKU_WORLD_1_YEAR = "com.codename1.world.subscribe.1year"; - -public static final String[] PRODUCTS = { - SKU_WORLD_1_MONTH, - SKU_WORLD_1_YEAR -}; ----- Notice that you create two separate SKUs for the 1 month and 1-year subscription. **Each subscription period must have its own SKU**. The example uses an array (`PRODUCTS`) that contains both of the SKUs. This is handy, as you'll see in the examples ahead, because the APIs for checking status and expiry date of a subscription take the SKUs in a "subscription group" as input. @@ -296,53 +174,11 @@ A basic receipt store needs to implement just two methods: You'll register it in your app's init() method so that it's always available: -[source,java] ----- -public void init(Object context) { -... - - Purchase.getInAppPurchase().setReceiptStore(new ReceiptStore() { - - @Override - public void fetchReceipts(SuccessCallback callback) { - // Fetch receipts from storage and pass them to the callback - } - - @Override - public void submitReceipt(Receipt receipt, SuccessCallback callback) { - // Save a receipt to storage. Make sure to call callback when done. - } - }); -} ----- These methods are designed to be asynchronous since real-world apps will always be connecting to some sort of network service. Instead of returning a value, both of these methods are passed instances of the `SuccessCallback` class. It's important to make sure to call `callback.onSuccess()` *ALWAYS* when the methods have completed, even if there is an error, or the Buy class will just assume that you're taking a long time to complete the task, and will continue to wait for you to finish. Once implemented, your `fetchReceipts()` method will look like: -[source,java] ----- -// static declarations used by receipt store - -// Storage key where list of receipts are stored -private static final String RECEIPTS_KEY = "RECEIPTS.dat"; - -@Override -public void fetchReceipts(SuccessCallback callback) { - Storage s = Storage.getInstance(); - Receipt[] found; - synchronized(RECEIPTS_KEY) { - if (s.exists(RECEIPTS_KEY)) { - List receipts = (List)s.readObject(RECEIPTS_KEY); - found = receipts.toArray(new Receipt[receipts.size()]); - } else { - found = new Receipt[0]; - } - } - // Make sure this is outside the synchronized block - callback.onSucess(found); -} ----- This is straight forward. You're checking to see if you already have a list of receipts stored. If so you return that list to the callback. If not you return an empty array of receipts. @@ -350,71 +186,8 @@ NOTE: `Receipt` implements `Externalizable` so you are able to write instances d The `submitReceipt()` method is a little more complex, as it needs to calculate the new expiry date for your subscription: -[source,java] ----- -@Override -public void submitReceipt(Receipt receipt, SuccessCallback callback) { - Storage s = Storage.getInstance(); - synchronized(RECEIPTS_KEY) { - List receipts; - if (s.exists(RECEIPTS_KEY)) { - receipts = (List)s.readObject(RECEIPTS_KEY); - } else { - receipts = new ArrayList(); - } - // Check to see if this receipt already exists - // This probably won't ever happen (that you will be asked to submit an - // existing receipt, but better safe than sorry - for (Receipt r : receipts) { - if (r.getStoreCode().equals(receipt.getStoreCode()) && - r.getTransactionId().equals(receipt.getTransactionId())) { - // If you've already got this receipt, you will this submission. - return; - } - } - - // Now try to find the current expiry date - Date currExpiry = new Date(); - List lProducts = Arrays.asList(PRODUCTS); - for (Receipt r : receipts) { - if (!lProducts.contains(receipt.getSku())) { - continue; - } - if (r.getCancellationDate()!= null) { - continue; - } - if (r.getExpiryDate() == null) { - continue; - } - if (r.getExpiryDate().getTime() > currExpiry.getTime()) { - currExpiry = r.getExpiryDate(); - } - } - - // Now set the appropriate expiry date by adding time onto - // the end of the current expiry date - Calendar cal = Calendar.getInstance(); - cal.setTime(currExpiry); - switch (receipt.getSku()) { - case SKU_WORLD_1_MONTH: - cal.add(Calendar.MONTH, 1); - break; - case SKU_WORLD_1_YEAR: - cal.add(Calendar.YEAR, 1); - } - Date newExpiry = cal.getTime(); - - receipt.setExpiryDate(newExpiry); - receipts.add(receipt); - s.writeObject(RECEIPTS_KEY, receipts); - - } - // Make sure this is outside the synchronized block - callback.onSucess(Boolean.TRUE); -} ----- -The main logic of this method involves iterating through all the existing receipts to find the *latest* current expiry date, so that when the user purchases a subscription, it's added onto the end of the current subscription (if one exists) rather than going from today's date. This enables users to safely renew their subscription before the subscription has expired. +The main logic of this method involves iterating through all the existing receipts to find the *latest* current expiry date, so that when the user purchases a subscription, it's added onto the end of the current subscription (if one exists) rather than going from today's date. This enables users to renew their subscription before the subscription has expired. In the real-world, you would implement this logic on the server-side. @@ -434,33 +207,9 @@ In your hello world app you synchronize the subscriptions in a few places. At the end of the `start()` method: -[source,java] ----- -public void start() { - -... - - // Now synchronize the receipts - iap.synchronizeReceipts(0, res->{ - // Update the UI as necessary to reflect - - }); -} ----- And you also provide a button to allow the user to manually synchronize the receipts: -[source,java] ----- -Button syncReceipts = new Button("Synchronize Receipts"); - -syncReceipts.addActionListener(e->{ - - iap.synchronizeReceipts(0, res->{ - // Update the UI - }); -}); ----- ===== Expiry dates and subscription status @@ -474,25 +223,6 @@ If you need to know more information about subscriptions, you can always just ca In the hello world app you'll use this information in a few different places. On your main form you'll include a label to show the current expiry date, and you allow the user to press a button to synchronize receipts manually if they think the value is out of date: -[source,java] ----- -//... - -SpanLabel rentalStatus = new SpanLabel("Loading rental details..."); -Button syncReceipts = new Button("Synchronize Receipts"); - -syncReceipts.addActionListener(e->{ - - iap.synchronizeReceipts(0, res->{ - if (iap.isSubscribed(PRODUCTS)) { - rentalStatus.setText("World rental expires "+iap.getExpiryDate(PRODUCTS)); - } else { - rentalStatus.setText("You don't currently have a subscription to the world"); - } - hi.revalidate(); - }); -}); ----- ===== Allowing the user to purchase the subscription @@ -500,51 +230,6 @@ You should now have all the background required to implement the Hello World Sub In the main form, you want two buttons to subscribe to the `World`, for one month and one year respectively. They look like: -[source,java] ----- -Purchase iap = Purchase.getInAppPurchase(); -//... -Button rentWorld1M = new Button("Rent World 1 Month"); -rentWorld1M.addActionListener(e->{ - String msg = null; - if (iap.isSubscribed(PRODUCTS)) { // <1> - msg = "you're already renting the world until " - +iap.getExpiryDate(PRODUCTS) // <2> - +". Extend it for one more month?"; - } else { - msg = "Rent the world for 1 month?"; - } - if (Dialog.show("Confirm", msg, "Yes", "No")) { - Purchase.getInAppPurchase().purchase(SKU_WORLD_1_MONTH); // <3> - // Note: since this is a non-renewable subscription it's a regular - // product in the play store - therefore you use the purchase() method. - // If it were a "subscription" product in the play store, then you - // would use subscribe() instead. - } -}); - -Button rentWorld1Y = new Button("Rent World 1 Year"); -rentWorld1Y.addActionListener(e->{ - String msg = null; - if (iap.isSubscribed(PRODUCTS)) { - msg = "you're already renting the world until "+ - iap.getExpiryDate(PRODUCTS)+ - ". Extend it for one more year?"; - } else { - msg = "Rent the world for 1 year?"; - } - if (Dialog.show("Confirm", msg, "Yes", "No")) { - Purchase.getInAppPurchase().purchase(SKU_WORLD_1_YEAR); - // Note: since this is a non-renewable subscription it's a regular - // product in the play store - therefore you use the purchase() method. - // If it were a "subscription" product in the play store, then you - // would use subscribe() instead. - } -}); ----- -<1> In the event handler you check if the user is subscribed by calling `isSubscribed(PRODUCTS)`. Notice that you check it against the array of both the one month and one year subscription SKUs. -<2> You are able to tell the user when the current expiry date is so that they can gauge whether to proceed. -<3> Since this is a non-renewable subscription, you use the `Purchase.purchase()` method. See following note about `subscribe()` vs `purchase()` ==== Subscribe() vs purchase() @@ -560,22 +245,6 @@ Which one you use depends on the type of product that's being purchased. If your The buy callbacks are like the ones implemented in the regular in-app purchase examples: -[source,java] ----- -@Override -public void itemPurchased(String sku) { - Purchase iap = Purchase.getInAppPurchase(); - - // Force you to reload the receipts from the store. - iap.synchronizeReceiptsSync(0); - ToastBar.showMessage("Your subscription has been extended to "+iap.getExpiryDate(PRODUCTS), FontImage.MATERIAL_THUMB_UP); -} - -@Override -public void itemPurchaseError(String sku, String errorMessage) { - ToastBar.showErrorMessage("Failure occurred: "+errorMessage); -} ----- Notice that, in `itemPurchased()` you don't need to explicitly create any receipts or submit anything to the receipt store. This is handled for you automatically. You do make a call to `synchronizeReceiptsSync(0)` but this is just to ensure that your toast message has the new expiry date loaded already. @@ -640,9 +309,9 @@ To aid in this process, Codename One provides a fully functional in-app purchase + [source,java] ---- -private static final String localHost = "http://10.0.1.32"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava018Snippet.java[tag=monetization-java-018,indent=0] ---- -5. Add the `ios.plistInject` build hint to your project with the value "NSAppTransportSecurity NSAllowsArbitraryLoads ". This is so that you can use http urls in iOS. Since you don't intend to fully publish this app, you can cut corners like this. If you were creating a real app, you would use proper secure URLs. +5. Add the `ios.plistInject` build hint to your project with the value `NSAppTransportSecurity NSAllowsArbitraryLoads `. This is so that you can use http urls in iOS. Since you don't intend to fully publish this app, you can cut corners like this. If you were creating a real app, you would use proper secure URLs. ===== Setting up the server project @@ -666,19 +335,7 @@ Open the project in Netbeans + [source,sql] ---- -create TABLE RECEIPTS -( - TRANSACTION_ID VARCHAR(128) not null, - USERNAME VARCHAR(64) not null, - SKU VARCHAR(128) not null, - ORDER_DATA VARCHAR(32000), - PURCHASE_DATE BIGINT, - EXPIRY_DATE BIGINT, - CANCELLATION_DATE BIGINT, - LAST_VALIDATED BIGINT, - STORE_CODE VARCHAR(20) default '' not null, - primary key (TRANSACTION_ID, STORE_CODE) -) +include::../demos/common/src/main/snippets/developer-guide/monetization.sql[tag=monetization-sql-001,indent=0] ---- 3. Open the "persistence.xml" file in the server NetBeans project. + @@ -753,167 +410,22 @@ The example uses the https://github.com/shannah/cn1-generic-webservice-client[Ge The source for your ReceiptStore is as follows: -[source,java] ----- -private ReceiptStore createReceiptStore() { - return new ReceiptStore() { - - RESTfulWebServiceClient client = createRESTClient(receiptsEndpoint); - - @Override - public void fetchReceipts(SuccessCallback callback) { - RESTfulWebServiceClient.Query query = new RESTfulWebServiceClient.Query() { - - @Override - protected void setupConnectionRequest(RESTfulWebServiceClient client, ConnectionRequest req) { - super.setupConnectionRequest(client, req); - req.setUrl(receiptsEndpoint); - } - - }; - client.find(query, rowset->{ - List out = new ArrayList(); - for (Map m : rowset) { - Result res = Result.fromContent(m); - Receipt r = new Receipt(); - r.setTransactionId(res.getAsString("transactionId")); - r.setPurchaseDate(new Date(res.getAsLong("purchaseDate"))); - r.setQuantity(1); - r.setStoreCode(m.getAsString("storeCode")); - r.setSku(res.getAsString("sku")); - - if (m.containsKey("cancellationDate") && m.get("cancellationDate")!= null) { - r.setCancellationDate(new Date(res.getAsLong("cancellationDate"))); - } - if (m.containsKey("expiryDate") && m.get("expiryDate")!= null) { - r.setExpiryDate(new Date(res.getAsLong("expiryDate"))); - } - out.add(r); - - } - callback.onSucess(out.toArray(new Receipt[out.size()])); - }); - } - - @Override - public void submitReceipt(Receipt r, SuccessCallback callback) { - Map m = new HashMap(); - m.put("transactionId", r.getTransactionId()); - m.put("sku", r.getSku()); - m.put("purchaseDate", r.getPurchaseDate().getTime()); - m.put("orderData", r.getOrderData()); - m.put("storeCode", r.getStoreCode()); - client.create(m, callback); - } - - }; -} ----- Notice that you aren't doing any calculation of expiry dates in your client app, as you did in the previous post (on non-renewable receipts). Since you are using a server now, it makes sense to move all that logic over to the server. The `createRESTClient()` method shown there creates a `RESTfulWebServiceClient` and configuring it to use basic authentication with a username and password. The idea is that your user would have logged into your app at some point, and you would have a username and password on hand to pass back to the web service with the receipt data so that you can connect the subscription to a user account. The source of that method is listed here: -[source,java] ----- -/** - * Creates a REST client to connect to a particular endpoint. The REST client - * generated here will automatically add the Authorization header - * which tells the service what platform you're on. - * @param url The url of the endpoint. - * @return - */ -private RESTfulWebServiceClient createRESTClient(String url) { - return new RESTfulWebServiceClient(url) { - - @Override - protected void setupConnectionRequest(ConnectionRequest req) { - try { - req.addRequestHeader("Authorization", "Basic " + Base64.encode((getUsername()+":"+getPassword()).getBytes("UTF-8"))); - } catch (Exception ex) {} - } - - }; -} ----- ===== Server-Side On the server-side, your REST controller is a standard JAX-RS REST interface. The Netbeans web service wizard generated it and then it was modified to suit the purposes here. The methods of the `ReceiptsFacadeREST` class for the REST API are shown here: -[source,java] ----- -@Stateless -@Path("com.codename1.demos.iapserver.receipts") -public class ReceiptsFacadeREST extends AbstractFacade { - - //... - - @POST - @Consumes({"application/xml", "application/json"}) - public void create(Receipts entity) { - - String username = credentialsWithBasicAuthentication(request).getName(); - entity.setUsername(username); - - // Save the receipt first in case something goes wrong in the validation stage - super.create(entity); - - // Let's validate the receipt - validateAndSaveReceipt(entity); - // validates the receipt against appropriate web service - // and updates database if expiry date has changed. - } - - //... - @GET - @Override - @Produces({"application/xml", "application/json"}) - public List findAll() { - String username = credentialsWithBasicAuthentication(request).getName(); - return getEntityManager() -.createNamedQuery("Receipts.findByUsername") -.setParameter("username", username) -.getResultList(); - } -} ----- The magic happens inside that `validateAndSaveReceipt()` method, which You'll cover in detail soon. ====== Notifications -It's important to note that you won't be notified by apple or google when changes are made to subscriptions. It's up to you to periodically "poll" their web service to find if any changes have been made. Changes you would be interested in are primarily renewals and cancellations. To deal with this, set up a method to run periodically (once-per day might be enough). For testing, the example below sets it up to run once per minute: - -[source,java] ----- -private static final long ONE_DAY = 24 * 60 * 60 * 1000; -private static final long ONE_DAY_SANDBOX = 10 * 1000; -@Schedule(hour="*", minute="*") -public void validateSubscriptionsCron() { - System.out.println("----------- DOING TIMED TASK ---------"); - List res = null; - final Set completedTransactionIds = new HashSet(); - for (String storeCode : new String[]{Receipt.STORE_CODE_ITUNES, Receipt.STORE_CODE_PLAY}) { - while (!(res = getEntityManager().createNamedQuery("Receipts.findNextToValidate") -.setParameter("threshold", System.currentTimeMillis() - ONE_DAY_SANDBOX) -.setParameter("storeCode", storeCode) -.setMaxResults(1) -.getResultList()).isEmpty() && -!completedTransactionIds.contains(res.get(0).getTransactionId())) { - - final Receipts curr = res.get(0); - completedTransactionIds.add(curr.getTransactionId()); - Receipts[] validatedReceipts = validateAndSaveReceipt(curr); - em.flush(); - for (Receipts r : validatedReceipts) { - completedTransactionIds.add(r.getTransactionId()); - } - - } - } -} ----- +It's important to note that you won't be notified by apple or google when changes are made to subscriptions. It's up to you to periodically "poll" their web service to find if any changes have been made. Changes you would be interested in are primarily renewals and cancellations. Set up a method to run periodically (once per day might be enough), using the scheduling mechanism on your server stack. That method finds all the receipts in the database that haven't been validated in some period of time, and validates it. Again, the magic happens inside the `validateAndSaveReceipt()` method which you cover later. @@ -926,20 +438,6 @@ For this tutorial, the example uses a purpose-built library to handle receipt va The general usage is as follows: -[source,java] ----- -IAPValidator validator = IAPValidator.getValidatorForPlatform(receipt.getStoreCode()); -if (validator == null) { - // no validators were found for this store - // Do custom validation -} else { - validator.setAppleSecret(APPLE_SECRET); - validator.setGoogleClientId(GOOGLE_DEVELOPER_API_CLIENT_ID); - validator.setGooglePrivateKey(GOOGLE_DEVELOPER_PRIVATE_KEY); - Receipt[] result = validator.validate(receipt); -... -} ----- As you can see from this snippet, the complexity of receipt validation has been reduced to entering three configuration strings: @@ -953,141 +451,6 @@ The next section walks through the steps to get these values. You are now ready to see the full magic of the `validateAndSaveReceipt()` method in all its glory: -[source,java] ----- -/** - * Validates a given receipt, updating the expiry date, - * @param receipt The receipt to be validated - * @param forInsert If true, then an expiry date will be calculated even if there is no validator. - */ -private Receipts[] validateAndSaveReceipt(Receipts receipt) { - EntityManager em = getEntityManager(); - Receipts managedReceipt = getManagedReceipt(receipt); - // managedReceipt == receipt if receipt is in database or null otherwise - - if (Receipt.STORE_CODE_SIMULATOR.equals(receipt.getStoreCode())) { // <1> - if (receipt.getExpiryDate() == null && managedReceipt == null) { - //Not inserted yet and no expiry date set yet - Date dt = calculateExpiryDate(receipt.getSku(), true); - if (dt!= null) { - receipt.setExpiryDate(dt.getTime()); - } - } - if (managedReceipt == null) { - // Receipt isn't in the database yet. Add it - em.persist(receipt); - return new Receipts[]{receipt}; - } else { - // The receipt is already in the database. Update it. - em.merge(managedReceipt); - return new Receipts[]{managedReceipt}; - } - } else { - // It isn't a simulator receipt - IAPValidator validator = IAPValidator.getValidatorForPlatform(receipt.getStoreCode()); - if (validator == null) { - // Receipt must have come from a platform other than iTunes or Play - // Because there is no validator - - if (receipt.getExpiryDate() == null && managedReceipt == null) { - // No expiry date. - // Generate one. - Date dt = calculateExpiryDate(receipt.getSku(), false); - if (dt!= null) { - receipt.setExpiryDate(dt.getTime()); - } - - } - if (managedReceipt == null) { - em.persist(receipt); - return new Receipts[]{receipt}; - } else { - em.merge(managedReceipt); - return new Receipts[]{managedReceipt}; - } - - } - - // Set credentials for the validator - validator.setAppleSecret(APPLE_SECRET); - validator.setGoogleClientId(GOOGLE_DEVELOPER_API_CLIENT_ID); - validator.setGooglePrivateKey(GOOGLE_DEVELOPER_PRIVATE_KEY); - - // Create a dummy receipt with transaction ID and order data to pass - // to the validator. Really all it needs is order data to be able to validate - Receipt r2 = Receipt(); - r2.setTransactionId(receipt.getTransactionId()); - r2.setOrderData(receipt.getOrderData()); - try { - Receipt[] result = validator.validate(r2); - // Depending on the platform, result may contain many receipts or a single receipt - // matching your receipt. In the case of iTunes, none of the receipt transaction IDs - // might match the original receipt's transactionId because the validator - // will set the transaction ID to the *original* receipt's transaction ID. - // If none match, then you should remove your receipt, and update each of the returned - // receipts in the database. - Receipt matchingValidatedReceipt = null; - for (Receipt r3 : result) { - if (r3.getTransactionId().equals(receipt.getTransactionId())) { - matchingValidatedReceipt = r3; - break; - } - } - - if (matchingValidatedReceipt == null) { - // Since the validator didn't find your receipt, - // you should remove the receipt. The equivalent - // is stored under the original receipt's transaction ID - if (managedReceipt!= null) { - em.remove(managedReceipt); - managedReceipt = null; - } - } - List out = new ArrayList(); - // Now go through and - for (Receipt r3 : result) { - if (r3.getOrderData() == null) { - // No order data found in receipt. Setting it to the original order data - r3.setOrderData(receipt.getOrderData()); - } - Receipts eReceipt = new Receipts(); - eReceipt.setTransactionId(r3.getTransactionId()); - eReceipt.setStoreCode(receipt.getStoreCode()); - Receipts eManagedReceipt = getManagedReceipt(eReceipt); - if (eManagedReceipt == null) { - copy(eReceipt, r3); - eReceipt.setUsername(receipt.getUsername()); - eReceipt.setLastValidated(System.currentTimeMillis()); - em.persist(eReceipt); - out.add(eReceipt); - } else { - - copy(eManagedReceipt, r3); - eManagedReceipt.setUsername(receipt.getUsername()); - eManagedReceipt.setLastValidated(System.currentTimeMillis()); - em.merge(eManagedReceipt); - out.add(eManagedReceipt); - } - } - - return out.toArray(new Receipts[out.size()]); - - } catch (Exception ex) { - // You should probably store some info about the failure in the - // database to make it easier to find receipts that aren't validating, - // but for now you will log it. - Log.p("Failed to validate receipt "+r2); - Log.p("Reason: "+ex.getMessage()); - Log.e(ex); - return new Receipts[]{receipt}; - - } - } -} ----- -<1> You need to handle the case where the app is being used in the CN1 simulator. You'll treat this -as a non-renewable receipt, and you'll calculate the expiry date using an "accelerated" clock to assist in testing. - NOTE: In many of the code snippets for the Server-side code, you'll see references to both a `Receipts` class and a `Receipt` class. This is slightly confusing. The `Receipts` class is a JPA entity the encapsulates a row from the "receipts" table of your SQL database. The `Receipt` class is `com.codename1.payment.Receipt`. It's used to interface with the IAP validation library. @@ -1199,18 +562,7 @@ This should prompt the download of a JSON file that will have contents like the [source,json] ---- -{ - "type": "service_account", - "project_id": "iapdemo-152500", - "private_key_id": "1b1d39************7d839826b8a", - "private_key": "-----BEGIN PRIVATE KEY-----... Some private key string -----END PRIVATE KEY-----\n", - "client_email": "iapdemo@iapdemo-152500.iam.gserviceaccount.com", - "client_id": "117601572633333082772", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://accounts.google.com/o/oauth2/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/iapdemo%40iapdemo-152500.iam.gserviceaccount.com" -} +include::../demos/common/src/main/snippets/developer-guide/monetization.json[tag=monetization-json-001,indent=0] ---- This is where you get the information you're looking for. The "client_email" is what you'll use for your `googleClientId`, and the "private_key" is what you'll use for the `googlePrivateKey`. @@ -1219,16 +571,6 @@ WARNING: Use the "client_email" value as your client ID, not the "client_id" val You'll set these in your constants: -[source,java] ----- -public static final String GOOGLE_DEVELOPER_API_CLIENT_ID="iapdemo@iapdemo-152500.iam.gserviceaccount.com"; -public static final String GOOGLE_DEVELOPER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----... -----END PRIVATE KEY-----\n"; - -... - -validator.setGoogleClientId(GOOGLE_DEVELOPER_API_CLIENT_ID); -validator.setGooglePrivateKey(GOOGLE_DEVELOPER_PRIVATE_KEY); ----- **NOT DONE YET** @@ -1255,11 +597,11 @@ At this point, the service account *should* be active so you can try to check re ====== Testing receipt validation -The `ReceiptsFacadeREST` class includes a flag to enable/disable play store validation. By default it's disabled. Let's enable it: +The `ReceiptsFacadeREST` class includes a flag to enable/disable play store validation. By default it's disabled. Enable it: [source,java] ---- -public static final boolean DISABLE_PLAY_STORE_VALIDATION=true; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava026Snippet.java[tag=monetization-java-026,indent=0] ---- Change this to `false`. @@ -1307,14 +649,14 @@ Once you have a shared secret, update the ReceiptsFacadeREST class with the valu [source,java] ---- -public static final String APPLE_SECRET = "your-shared-secret-here"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava027Snippet.java[tag=monetization-java-027,indent=0] ---- And enable iTunes store validation: [source,java] ---- -public static final boolean DISABLE_ITUNES_STORE_VALIDATION=true; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MonetizationJava028Snippet.java[tag=monetization-java-028,indent=0] ---- Change this to `false`. diff --git a/docs/developer-guide/Motion-Sensors.asciidoc b/docs/developer-guide/Motion-Sensors.asciidoc index 0f0fac95054..2988490979b 100644 --- a/docs/developer-guide/Motion-Sensors.asciidoc +++ b/docs/developer-guide/Motion-Sensors.asciidoc @@ -8,27 +8,9 @@ Gesture detection lives in the core rather than in each platform port. Any platf `MotionSensorManager.getInstance()` always returns a manager. On a device without motion hardware every sensor reports as unsupported, so application code can call the API without a null check on the manager itself. `getSensor(int)` returns `null` when the requested sensor isn't available on the current device: -[source,java] ----- -MotionSensorManager m = MotionSensorManager.getInstance(); -MotionSensor accelerometer = m.getSensor(MotionSensorManager.TYPE_ACCELEROMETER); -if (accelerometer != null) { - accelerometer.addListener(new MotionSensorListener() { - public void motionReceived(MotionEvent evt) { - // x, y and z are in meters per second squared - label.setText(evt.getX() + ", " + evt.getY() + ", " + evt.getZ()); - label.getParent().revalidate(); - } - }); -} ----- Callbacks arrive on the EDT, so the listener can update the UI directly. Sampling is reference counted: the hardware sensor draws power only while at least one listener is registered. Remove the listener once the screen is no longer visible, typically from the form's `removeNotify`, so the sensor powers down: -[source,java] ----- -accelerometer.removeListener(listener); ----- === Sensor types @@ -63,14 +45,6 @@ Readings use a single convention across every port: the x-axis points to the rig Register a `GestureListener` for one of the `GestureEvent.TYPE_*` gestures. The accelerometer powers on for the duration that a gesture listener stays registered: -[source,java] ----- -MotionSensorManager.getInstance().addGestureListener(GestureEvent.TYPE_SHAKE, new GestureListener() { - public void gestureDetected(GestureEvent evt) { - Dialog.show("Shaken", "You shook the device", "OK", null); - } -}); ----- The recognized gestures are: @@ -82,13 +56,6 @@ The recognized gestures are: The detection thresholds suit most apps as shipped, and you can tune them when a gesture needs to be more or less sensitive: -[source,java] ----- -MotionSensorManager m = MotionSensorManager.getInstance(); -m.setShakeThreshold(15.0); // require a more vigorous shake -m.setTiltThreshold(0.5); // tilt angle in radians -m.setSamplingInterval(33); // sample at about 30Hz for snappier gestures ----- A smaller sampling interval makes gestures more responsive at a higher battery cost. The value is clamped to the range 10ms through 1000ms. diff --git a/docs/developer-guide/Native-Themes.asciidoc b/docs/developer-guide/Native-Themes.asciidoc index 3e0f36618b6..a30ab4cbab2 100644 --- a/docs/developer-guide/Native-Themes.asciidoc +++ b/docs/developer-guide/Native-Themes.asciidoc @@ -160,20 +160,7 @@ accent-bearing UIID at once: [source,css] ---- -#Constants { - includeNativeBool: true; - darkModeBool: true; -} - -/* Touch the four UIIDs that paint with primary / primary-container. - The role-as-rule pattern keeps brand changes localised. */ -Button { background-color: #00796b; } -Button.pressed { background-color: #4db6ac; color: #00251a; } -RaisedButton { background-color: #b2dfdb; color: #00251a; } -RaisedButton.pressed { background-color: #80cbc4; } -SelectedTab { color: #00796b; } -BackCommand { color: #00796b; } -TitleCommand { color: #00796b; } +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=native-themes-css-001,indent=0] ---- === iOS modern color palette @@ -338,29 +325,7 @@ runtime overrides: [source,css] ---- -#Constants { - includeNativeBool: true; - darkModeBool: true; - - /* Override the native theme's accent palette. The compiler picks - up these declarations from #Constants and exports them as - @accent-color / @accent-color-dark theme constants so every - UIID bound to var(--accent-color) in the parent native theme - picks them up at app launch - no per-UIID rule edit needed. */ - --accent-color: #ff2d95; - --accent-color-dark: #ff2d95; - --accent-pressed-color: #c71a75; - --accent-pressed-color-dark: #c71a75; - --accent-on-color: #ffffff; - - /* Material 3 RaisedButton uses a separate "container" tonal - pair; iOS ignores these (no bindings reference them) so it's - safe to set them unconditionally. */ - --accent-container-color: #ff2d95; - --accent-container-color-dark: #ff2d95; - --accent-on-container-color: #ffffff; - --accent-on-container-color-dark: #ffffff; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=native-themes-css-002,indent=0] ---- Bindings that reference a constant you didn't override stay at @@ -373,21 +338,6 @@ For dynamic theming - in-app accent toggles, branded flavours, A/B tests - push the same constants through `UIManager.addThemeProps` after the theme has been installed: -[source,java] ----- -Hashtable override = new Hashtable(); -override.put("@accent-color", "ff2d95"); -override.put("@accent-color-dark", "ff2d95"); -override.put("@accent-pressed-color", "c71a75"); -override.put("@accent-pressed-color-dark", "c71a75"); -override.put("@accent-on-color", "ffffff"); -override.put("@accent-container-color", "ff2d95"); -override.put("@accent-container-color-dark", "ff2d95"); -override.put("@accent-on-container-color", "ffffff"); -override.put("@accent-on-container-color-dark", "ffffff"); -UIManager.getInstance().addThemeProps(override); -Form.getCurrentForm().refreshTheme(); ----- Values can be passed with or without the leading `#` and in any case; the runtime accepts `"ff2d95"`, `"#FF2D95"`, and the 3-digit @@ -544,21 +494,7 @@ Your app's `theme.css` inherits from the installed native theme: [source,css] ---- -#Constants { - includeNativeBool: true; - darkModeBool: true; -} - -/* Tweak only what's different. Everything you do not redeclare - keeps coming from the native theme. */ -RaisedButton { background-color: #d81b60; } -RaisedButton.pressed { background-color: #b71c5c; } -RaisedButton.disabled { background-color: #ffd6e2; color: #ffffff; } - -@media (prefers-color-scheme: dark) { - RaisedButton { background-color: #ff80ab; color: #4a0026; } - RaisedButton.pressed { background-color: #f06292; } -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=native-themes-css-003,indent=0] ---- The user's CSS is layered on top of the native theme at app launch, @@ -580,14 +516,7 @@ theme's UIIDs and refine it: [source,css] ---- -DangerButton { - cn1-derive: RaisedButton; - background-color: #d32f2f; -} -DangerButton.pressed { - cn1-derive: RaisedButton; - background-color: #b71c1c; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=native-themes-css-004,indent=0] ---- `cn1-derive` works best when the relationship is *child refines diff --git a/docs/developer-guide/Near-Field-Communication.asciidoc b/docs/developer-guide/Near-Field-Communication.asciidoc index 8af2c8ca140..57efb79f83a 100644 --- a/docs/developer-guide/Near-Field-Communication.asciidoc +++ b/docs/developer-guide/Near-Field-Communication.asciidoc @@ -8,49 +8,13 @@ A single entry point exposes the platform NFC controller, a typed enum reports t [source,java] ---- -import com.codename1.nfc.Nfc; -import com.codename1.nfc.NfcReadOptions; -import com.codename1.nfc.NdefMessage; - -Nfc nfc = Nfc.getInstance(); -if (!nfc.canRead()) { - // no NFC hardware, or it is disabled in system settings - return; -} -nfc.readNdef(new NfcReadOptions() - .setNdefOnly(true) - .setAlertMessage("Hold near the poster")) - .onResult((NdefMessage msg, Throwable err) -> { - if (err != null) { - return; - } - String url = msg.getFirstRecord().getUriPayload(); - Display.getInstance().execute(url); - }); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NearFieldCommunicationJava001Snippet.java[tag=near-field-communication-java-001,indent=0] ---- `setNdefOnly(true)` picks the fastest iOS Core NFC session (`NFCNDEFReaderSession`) and matches the most common tag layout. For mixed payloads use `readTag(...)` and inspect `tag.getTypes()`. === Quick start: Write an NDEF URI -[source,java] ----- -import com.codename1.nfc.NdefMessage; -import com.codename1.nfc.NdefRecord; - -NdefMessage msg = new NdefMessage( - NdefRecord.createUri("https://codenameone.com"), - NdefRecord.createText("en", "Codename One")); -Nfc.getInstance().writeNdef( - new NfcReadOptions().setAlertMessage("Tap a writable tag"), - msg) - .onResult((Boolean ok, Throwable err) -> { - if (err != null) { - NfcError code = ((NfcException) err).getError(); - // READ_ONLY, CAPACITY_EXCEEDED, INVALID_NDEF, TAG_LOST, ... - } - }); ----- NDEF records carry one of the well-known types (`T`, `U`, `Sp`), a MIME media type, an absolute URI, or an external `domain:type`. Use the factory methods on `NdefRecord` rather than constructing raw byte arrays: @@ -68,23 +32,6 @@ NDEF records carry one of the well-known types (`T`, `U`, `Sp`), a MIME media ty Beyond NDEF, the discovered `Tag` exposes accessors for the underlying technologies: -[source,java] ----- -nfc.readTag(new NfcReadOptions() - .setTechFilter(TagType.ISO_DEP) - .setIsoSelectAids(myAid)) - .onResult((Tag tag, Throwable err) -> { - if (err != null) return; - IsoDep iso = tag.getIsoDep(); - if (iso == null) return; - iso.transceive(myCommandApdu).onResult((byte[] resp, Throwable e) -> { - if (ApduResponse.isSuccess(resp)) { - byte[] body = ApduResponse.body(resp); - // application-specific parsing - } - }); - }); ----- Each accessor returns `null` when the tag doesn't advertise the corresponding technology, so always null-check before calling. @@ -102,12 +49,6 @@ Each accessor returns `null` when the tag doesn't advertise the corresponding te For FeliCa on iOS, set the system codes in `NfcReadOptions`: -[source,java] ----- -new NfcReadOptions() - .setTechFilter(TagType.NFC_F) - .setFelicaSystemCodes("0003", "8008"); ----- The Codename One Maven plugin and build daemon automatically register `com.apple.developer.nfc.readersession.felica.systemcodes` from these values. @@ -115,27 +56,6 @@ The Codename One Maven plugin and build daemon automatically register `com.apple Host Card Emulation (HCE) lets the device pretend to be a contactless smart card. A nearby reader (Android phone, payment terminal, access control gate) sends ISO 7816 APDUs and your app responds. Subclass `HostCardEmulationService` and register the instance: -[source,java] ----- -class MyService extends HostCardEmulationService { - @Override - public String[] getAids() { - return new String[] { "F0010203040506" }; - } - @Override - public byte[] processCommand(byte[] apdu) { - if (apdu.length > 1 && apdu[1] == (byte) 0xA4) { - // SELECT -- terminal has just routed an APDU to our AID - return ApduResponse.withStatus( - new byte[] { 'O', 'K' }, - ApduResponse.swSuccess()); - } - return ApduResponse.swInsNotSupported(); - } -} - -Nfc.getInstance().registerHostCardEmulationService(new MyService()); ----- Set the AIDs as a build hint so they appear in the platform routing tables: @@ -151,7 +71,7 @@ iOS HCE (`CardSession`) requires iOS 17.4+ and, as of 2026, is EU-only. The Code === Permissions and build hints -The Codename One build pipeline auto-detects NFC usage. Apps that never touch `com.codename1.nfc` see no manifest / plist change. +The Codename One build pipeline automatically detects NFC usage. Apps that never touch `com.codename1.nfc` see no manifest / plist change. [options="header"] |=== diff --git a/docs/developer-guide/Network-Connectivity.asciidoc b/docs/developer-guide/Network-Connectivity.asciidoc index 003a35d585f..13b6861ae55 100644 --- a/docs/developer-guide/Network-Connectivity.asciidoc +++ b/docs/developer-guide/Network-Connectivity.asciidoc @@ -18,14 +18,7 @@ The fastest way to know whether the user is on WiFi, what SSID they joined, and [source,java] ---- -import com.codename1.io.wifi.WiFi; - -if (WiFi.isInfoSupported()) { - String ssid = WiFi.getCurrentSSID(); // e.g. "Codename One" - String bssid = WiFi.getBSSID(); // "aa:bb:cc:11:22:33" - String gw = WiFi.getGateway(); // "192.168.1.1" - String ip = WiFi.getIp(); // "192.168.1.42" -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava001Snippet.java[tag=network-connectivity-java-001,indent=0] ---- Any of these calls may return `null` if the device isn't on WiFi, if the user denied a runtime permission, or if the platform never exposed the value to apps. The framework normalizes the wrapping that Android adds to the SSID and treats `` and the obfuscated BSSID `02:00:00:00:00:00` as `null`. @@ -56,21 +49,6 @@ android.xpermissions= - - org.jmdns - jmdns - 3.5.9 - runtime - +include::../demos/common/src/main/snippets/developer-guide/network-connectivity.xml[tag=network-connectivity-xml-001,indent=0] ---- [WARNING] @@ -196,28 +127,14 @@ Subscribe to network type transitions (`WiFi <-> Cellular <-> Ethernet <-> None` [source,java] ---- -import com.codename1.io.NetworkManager; -import com.codename1.io.NetworkTypeListener; - -NetworkManager.getInstance().addNetworkTypeListener(new NetworkTypeListener() { - @Override - public void onNetworkTypeChanged(int oldType, int newType, boolean vpnActive) { - if (newType == NetworkManager.NETWORK_TYPE_NONE) { - // offline -- queue uploads for later - } - if (vpnActive) { - // refuse to roam to corporate-only resources - } - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava006Snippet.java[tag=network-connectivity-java-006,indent=0] ---- The current snapshot is available without subscribing: [source,java] ---- -int type = NetworkManager.getInstance().getCurrentNetworkType(); -boolean vpn = NetworkManager.getInstance().isVPNActive(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava007Snippet.java[tag=network-connectivity-java-007,indent=0] ---- The platform implementations are: @@ -232,37 +149,6 @@ VPN detection is best-effort and intentionally heuristic. On Android it uses `Ne WiFi Direct lets two devices form a peer-to-peer link without an access point. The API is Android-only -- iOS uses MultipeerConnectivity for similar scenarios and is intentionally out of scope. -[source,java] ----- -import com.codename1.io.wifi.WiFiDirect; -import com.codename1.io.wifi.WiFiDirectListener; -import com.codename1.io.wifi.WiFiDirectPeer; - -if (!WiFiDirect.isSupported()) { - return; -} - -WiFiDirect.startDiscovery(new WiFiDirectListener() { - @Override - public void onPeersAvailable(WiFiDirectPeer[] peers) { - for (WiFiDirectPeer p : peers) { - // p.getDeviceName(), p.getDeviceAddress(), p.getState() - } - } - @Override - public void onDiscoveryError(Throwable error) { Log.e(error); } -}); - -// later, with the user's chosen peer: -WiFiDirect.connect(peer, new WiFiConnectCallback() { - @Override - public void onConnectResult(boolean connected, Throwable error) { /* ... */ } -}); - -// when finished: -WiFiDirect.disconnect(); -WiFiDirect.stopDiscovery(); ----- The build pipeline injects `CHANGE_WIFI_STATE`, `ACCESS_WIFI_STATE`, `ACCESS_NETWORK_STATE`, `ACCESS_FINE_LOCATION` and `NEARBY_WIFI_DEVICES` automatically when `WiFiDirect` is on the classpath, plus a `` declaration so devices without the radio can still install the app via Play Store filtering. @@ -272,34 +158,7 @@ The USB API in `com.codename1.io.usb` lets the device act as a USB host and talk [source,java] ---- -import com.codename1.io.usb.Usb; -import com.codename1.io.usb.UsbDevice; -import com.codename1.io.usb.UsbDeviceListener; - -if (!Usb.isSupported()) { return; } - -Usb.addDeviceListener(new UsbDeviceListener() { - @Override - public void onDeviceAttached(UsbDevice d) { - if (d.getVendorId() == 0x0403 && d.getProductId() == 0x6001) { - Usb.requestPermission(d); - } - } - @Override - public void onDeviceDetached(UsbDevice d) { } - @Override - public void onPermissionResult(UsbDevice d, boolean granted) { - if (granted) { - try (InputStream in = Usb.openInputStream(d, 0x81); - OutputStream out = Usb.openOutputStream(d, 0x02)) { - out.write("AT\r\n".getBytes()); - byte[] buf = new byte[64]; - int n = in.read(buf); - // ... - } catch (IOException e) { Log.e(e); } - } - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NetworkConnectivityJava009Snippet.java[tag=network-connectivity-java-009,indent=0] ---- The build pipeline injects: @@ -310,10 +169,7 @@ If you want the OS to launch your app when a matching device is plugged in, ship [source,xml] ---- - - - - +include::../demos/common/src/main/snippets/developer-guide/network-connectivity.xml[tag=network-connectivity-xml-002,indent=0] ---- [source] diff --git a/docs/developer-guide/Notifications-And-Background-Execution.asciidoc b/docs/developer-guide/Notifications-And-Background-Execution.asciidoc index aab94509c03..4fa65405072 100644 --- a/docs/developer-guide/Notifications-And-Background-Execution.asciidoc +++ b/docs/developer-guide/Notifications-And-Background-Execution.asciidoc @@ -19,19 +19,7 @@ its notifications are shown. Request it through `Display`: [source,java] ---- -import com.codename1.notifications.NotificationPermissionRequest; -import com.codename1.notifications.NotificationPermissionResult.AuthorizationLevel; - -NotificationPermissionRequest req = new NotificationPermissionRequest() - .provisional(true) // iOS: deliver quietly without an explicit prompt - .timeSensitive(true); // iOS: allow the time-sensitive interruption level - -Display.getInstance().requestNotificationPermission(req, result -> { - if (result.isGranted()) { - // schedule notifications - } - AuthorizationLevel level = result.getAuthorizationLevel(); -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/NotificationsAndBackgroundExecutionJava001Snippet.java[tag=notifications-and-background-execution-java-001,indent=0] ---- `NotificationPermissionResult.getAuthorizationLevel()` returns an `AuthorizationLevel` @@ -59,21 +47,6 @@ provisioning profile doesn't carry breaks code signing). `LocalNotification` is scheduled through `Display.scheduleLocalNotification(...)` as before; the bean has been extended with fluent, backward-compatible setters: -[source,java] ----- -LocalNotification n = new LocalNotification() - .setChannelId("messages") // Android channel (see below) - .setGroup("chat-42") // bundle related notifications - .setProgress(100, 40) // determinate progress bar (Android) - .setTimeSensitive(true); // break through Focus / elevated importance -n.setId("msg-42"); -n.setAlertTitle("New message"); -n.setAlertBody("Tap to reply"); -n.addAction("open", "Open"); -n.addInputAction("reply", "Reply", "Type a message", "Send"); // inline quick reply -Display.getInstance().scheduleLocalNotification(n, System.currentTimeMillis() + 1000, - LocalNotification.REPEAT_NONE); ----- When the user taps an action, the selected action id (and any quick-reply text) are delivered through `com.codename1.push.PushContent` -- `getActionId()`, `getActionTitle()` @@ -117,16 +90,6 @@ Channels let the user control importance, sound, vibration, lights, lock screen visibility and badge display for a group of notifications. Build and register one at startup, then reference it from a notification with `setChannelId(...)`: -[source,java] ----- -new NotificationChannelBuilder("messages", "Messages") - .description("Incoming chat messages") - .importance(NotificationChannelBuilder.IMPORTANCE_HIGH) - .sound("/notification_sound_ping.mp3") - .enableVibration(true) - .lightColor(0xff0000) - .register(); ----- Channels are an Android concept. On iOS and the simulator `register()` is a no-op (the simulator stores the channel so its name can be shown in the notification panel); the @@ -138,16 +101,6 @@ channel id you assign is still carried so the notification behaves consistently. Implement `BackgroundWorker` in a class with a public no-argument constructor (it's reconstructed after a cold launch -- pass state through the input data, not fields): -[source,java] ----- -WorkRequest req = WorkRequest.builder("sync", SyncWorker.class) - .setRequiresNetwork(true) - .setRequiresCharging(true) - .setPeriodic(6 * 60 * 60 * 1000L) - .putInputData("account", "primary") - .build(); -BackgroundWork.schedule(req); ----- [options="header"] |=== @@ -169,17 +122,6 @@ and links `BackgroundTasks.framework` automatically. `ForegroundService` runs a long task while a persistent notification is shown: -[source,java] ----- -ForegroundService svc = ForegroundService.start("downloads", "Downloading", "Please wait", - null, service -> { - for (int i = 0; i <= 100; i++) { - service.updateNotification("Downloading", i + "%"); - // ... do a chunk of work ... - } - }); -// auto-stops when the task returns, or call svc.stop() ----- [options="header"] |=== @@ -196,18 +138,6 @@ Override the type with the `android.foregroundServiceType` build hint (default ` Override `onReceivedSharedContent(SharedContent)` on your `Lifecycle` subclass to receive text, URLs, files or images shared into your app from other apps: -[source,java] ----- -public class MyApp extends Lifecycle { - public void onReceivedSharedContent(SharedContent content) { - for (SharedContent.Item item : content.getItems()) { - if (item.getType() == SharedContent.TYPE_IMAGE) { - importImage(item.getFilePath()); // a FileSystemStorage path - } - } - } -} ----- [options="header"] |=== @@ -236,7 +166,7 @@ Server-side topic delivery is handled by the Codename One push server. === Simulator -The JavaSE simulator implements all of the above so you can develop without a device. Use +The JavaSE simulator implements all the above so you can develop without a device. Use the *Simulate -> Notifications and Background* menu to toggle the background constraints (network / charging / idle / battery), run scheduled work immediately, inspect registered channels, and inject shared content. Posted notifications appear in a panel at the top diff --git a/docs/developer-guide/On-Device-Debugging-Android.asciidoc b/docs/developer-guide/On-Device-Debugging-Android.asciidoc index a217fc2ce41..fda35ffe4d0 100644 --- a/docs/developer-guide/On-Device-Debugging-Android.asciidoc +++ b/docs/developer-guide/On-Device-Debugging-Android.asciidoc @@ -42,7 +42,7 @@ archetype generated: [source,properties] ---- -codename1.arg.android.onDeviceDebug=true +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.properties[tag=on-device-debugging-android-properties-001,indent=0] ---- This flips the generated `AndroidManifest.xml` to @@ -116,14 +116,13 @@ the real Android runtime. + [source,bash] ---- -adb pair 192.168.1.42:37051 -# When prompted, enter the 6-digit code shown on the device. +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-001,indent=0] ---- . Connect: + [source,bash] ---- -adb connect 192.168.1.42:5555 +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-002,indent=0] ---- + The connect port is *not* the pairing port — it's the one shown on @@ -133,8 +132,7 @@ the *Wireless debugging* screen above the *Pair device* button. + [source,bash] ---- -mvn cn1:android-on-device-debugging \ - -Dcn1.android.onDeviceDebug.wireless=192.168.1.42:5555 +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-003,indent=0] ---- ==== Android 10 and older @@ -145,8 +143,7 @@ mvn cn1:android-on-device-debugging \ + [source,bash] ---- -adb tcpip 5555 -adb connect 192.168.1.42:5555 +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-004,indent=0] ---- . Unplug the cable, then run the debug session as normal. @@ -159,19 +156,14 @@ Without the IntelliJ run configs, the same flow is two terminals: [source,bash] ---- -# Terminal 1 — build the APK once -mvn cn1:buildAndroidOnDeviceDebug - -# Terminal 2 — install, launch, forward JDWP, tail logcat -mvn cn1:android-on-device-debugging +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-005,indent=0] ---- Attach jdb (or another JDWP client) to the forwarded port: [source,bash] ---- -jdb -attach localhost:5005 \ - -sourcepath src/main/java:$HOME/.m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT-sources.jar +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging-android.sh[tag=on-device-debugging-android-bash-006,indent=0] ---- For VS Code (Debugger for Java extension), add a launch diff --git a/docs/developer-guide/On-Device-Debugging.asciidoc b/docs/developer-guide/On-Device-Debugging.asciidoc index ac8e9d309ba..4373fbf7953 100644 --- a/docs/developer-guide/On-Device-Debugging.asciidoc +++ b/docs/developer-guide/On-Device-Debugging.asciidoc @@ -46,11 +46,7 @@ the archetype generated: [source,properties] ---- -codename1.arg.ios.onDeviceDebug=true -codename1.arg.ios.onDeviceDebug.proxyHost=127.0.0.1 -codename1.arg.ios.onDeviceDebug.proxyPort=55333 -# Optional: block the app at startup until the IDE attaches. -codename1.arg.ios.onDeviceDebug.waitForAttach=true +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging.properties[tag=on-device-debugging-properties-001,indent=0] ---- For a physical device, set `proxyHost` to the laptop's LAN IP (run @@ -106,11 +102,7 @@ two terminals: [source,bash] ---- -# Terminal 1 — start the proxy -mvn cn1:ios-on-device-debugging - -# Terminal 2 — attach jdb -jdb -attach localhost:8000 +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging.sh[tag=on-device-debugging-bash-001,indent=0] ---- For VS Code (Debugger for Java extension), add a launch @@ -127,8 +119,7 @@ is manual: [source,bash] ---- -jdb -attach localhost:8000 \ - -sourcepath src/main/java:$HOME/.m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT-sources.jar +include::../demos/common/src/main/snippets/developer-guide/on-device-debugging.sh[tag=on-device-debugging-bash-002,indent=0] ---- The `-sourcepath` is required for `list` / `where` to render framework diff --git a/docs/developer-guide/Printing.asciidoc b/docs/developer-guide/Printing.asciidoc index 8ef466125e7..fae58a9386d 100644 --- a/docs/developer-guide/Printing.asciidoc +++ b/docs/developer-guide/Printing.asciidoc @@ -6,19 +6,6 @@ The document is a file in `FileSystemStorage` identified by its path and mime ty === Quick start: Print a PDF -[source,java] ----- -import com.codename1.printing.Printer; -import com.codename1.printing.PrintResult; - -if (Printer.isPrintingSupported()) { - Printer.printPDF(reportPath, result -> { - if (result.isFailed()) { - ToastBar.showErrorMessage("Print failed: " + result.getError()); - } - }); -} ----- `Printer.printPDF(path, listener)` is shorthand for `Printer.print(path, "application/pdf", listener)`. Use the general `print` variant for other document types, for example `Printer.print(photoPath, "image/jpeg", listener)`. @@ -28,15 +15,6 @@ Gate the call on `isPrintingSupported()`. When it returns `false` the listener r `Printer.printImage(image, listener)` prints a `com.codename1.ui.Image` directly. The image is encoded to a temporary PNG file behind the scenes and the file is deleted once the print flow finishes: -[source,java] ----- -Image chart = renderChart(); -Printer.printImage(chart, result -> { - if (result.isFailed()) { - ToastBar.showErrorMessage("Print failed: " + result.getError()); - } -}); ----- This pairs well with drawing on a mutable image, so you can compose a printable page (a receipt, a ticket, a chart) with the standard `Graphics` API and send it to paper without generating a PDF. diff --git a/docs/developer-guide/Push-Notifications.asciidoc b/docs/developer-guide/Push-Notifications.asciidoc index 85f5cb46d14..214fb7ab40a 100644 --- a/docs/developer-guide/Push-Notifications.asciidoc +++ b/docs/developer-guide/Push-Notifications.asciidoc @@ -22,38 +22,7 @@ Enabling push support in your application is a matter of implementing the `PushC [source,java] ---- -public class MyApplication implements PushCallback { - - // .... - - /** - * Invoked when the push notification occurs - * - * @param value the value of the push notification - */ - public void push(String value) { - System.out.println("Received push message: "+value); - } - - /** - * Invoked when push registration is complete to pass the device ID to the application. - * - * @param deviceId OS native push ID you should not use this value and instead use Push.getPushKey() - * @see Push#getPushKey() - */ - public void registeredForPush(String deviceId) { - System.out.println("The Push ID for this device is "+Push.getPushKey()); - } - - /** - * Invoked to indicate an error occurred during registration for push notification - * @param error descriptive error string - * @param errorCode an error code - */ - public void pushRegistrationError(String error, int errorCode) { - System.out.println("An error occurred during push registration."); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava001Snippet.java[tag=push-notifications-java-001,indent=0] ---- @@ -102,23 +71,6 @@ To handle this, you can opt-in to manually signaling the completion of the push Example: -[source,java] ----- -public void push(String message) { - if (isAudioMessage(message)) { - // Play audio asynchronously - playAudio(message, new Runnable() { - public void run() { - // Audio finished playing - Display.getInstance().notifyPushCompletion(); - } - }); - } else { - // For standard messages, we can notify immediately or let it timeout (safest to notify) - Display.getInstance().notifyPushCompletion(); - } -} ----- **How it works:** @@ -150,9 +102,7 @@ Assuming your `push()` method looks like: [source,java] ---- -public void push(String value) { - System.out.println("Received push message: "+value); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava003Snippet.java[tag=push-notifications-java-003,indent=0] ---- You should see the following output in your console: @@ -328,19 +278,14 @@ The message body should be an XML string. A minimal example of a push type 99 th [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/push-notifications.xml[tag=push-notifications-xml-001,indent=0] ---- To avoid hand-coding this XML you can use the `com.codename1.push.PushBuilder` helper which assembles the payload for you and returns the correct push type to send: [source,java] ---- -PushBuilder builder = new PushBuilder() - .type(1) - .body("Hello World") - .imageUrl("https://example.com/myimage.jpg"); -String payload = builder.build(); -int pushType = builder.getType(); // Will be 99 when image/category metadata is present +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava004Snippet.java[tag=push-notifications-java-004,indent=0] ---- You can then send `payload` as the message body with `pushType` as the type through the REST API, the `Push` helper, or any of the other examples below. @@ -364,17 +309,7 @@ You can access more information about the push content using the `com.codename1. [source,java] ---- -public void push(String message) { - PushContent content = PushContent.get(); - if (content != null) { - String title = content.getTitle(); - String body = content.getBody(); - String hiddenMeta = content.getMetaData(); - String imageUrl = content.getImageUrl(); - String replyText = content.getTextResponse(); - // Use these values (title/body/hiddenMeta/imageUrl/replyText) as needed in your application logic. - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava005Snippet.java[tag=push-notifications-java-005,indent=0] ---- WARNING: Make sure to call `PushContent.get()` *once* inside your `push()` callback, and store the return value. `PushContent.get()` works like a queue of size=1, and it pops off the item from the front of the queue when it's called. If you call it twice, the second time will return `null`. @@ -397,31 +332,6 @@ You can specify the available categories and actions for your app by implementin For example: -[source,java] ----- -import com.codename1.push.PushAction; -import com.codename1.push.PushActionCategory; -import com.codename1.push.PushActionsProvider; -... - -public class MyApplication implements PushCallback, PushActionsProvider { - - ... - - @Override - public PushActionCategory[] getPushActionCategories() { - return new PushActionCategory[]{ - new PushActionCategory("invite", new PushAction[]{ - new PushAction("yes," "Yes"), - new PushAction("no," "No"), - new PushAction("maybe," "Maybe"), - new PushAction("reply", "Reply", null, "Type your response...", "Send") - }) - - }; - } -} ----- In the above example, you create a single category, "invite" that has actions "yes," "no," "maybe," and a "reply" action that prompts the user for text input. A text-input action is enabled by providing either a placeholder, button text, or both when constructing the `PushAction`. @@ -431,7 +341,7 @@ Now you can test your new category. In the push simulator, you can select Push T [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/push-notifications.xml[tag=push-notifications-xml-002,indent=0] ---- .Push notification with "invite" category on the simulator will show dialog with buttons to select between the actions defined in the "invite" category. @@ -454,21 +364,7 @@ For example: [source,java] ---- -public void push(String message) { - PushContent content = PushContent.get(); - if (content != null) { - if ("invite".equals(content.getCategory())) { - if (content.getActionId() != null) { - System.out.println("The user selected the "+content.getActionId()+" action"); - if (content.getTextResponse() != null) { - System.out.println("They replied: "+content.getTextResponse()); - } - } else { - System.out.println("The user clicked on the invite notification, but didn't select an action."); - } - } - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava007Snippet.java[tag=push-notifications-java-007,indent=0] ---- === Deploying Push-Enabled apps to device @@ -553,7 +449,7 @@ Push on iOS is much harder to handle than the Android version, but you simplifie iOS push needs two more P12 certificates. -IMPORTANT: Please *notice* that these are *NOT* the signing certificates! + +IMPORTANT: Please *notice* that these aren't the signing certificates! + They're *completely different certificates* that have nothing to do with the build process! The certificate wizard can generate these more push certificates and do a few other things if you check this flag in the end of the wizard: @@ -625,7 +521,7 @@ You can send a push message in many ways for example: from another device or any [source,java] ----- -private static final String PUSH_TOKEN = "********-****-****-****-*************"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PushNotificationsJava008Snippet.java[tag=push-notifications-java-008,indent=0] ----- The push token is a unique "key" that you can use to send push through your Codename One account. It allows you to send push messages without placing your Codename One email or password into your source files. @@ -641,7 +537,7 @@ The instructions for extracting the API key for Google are <>. @@ -731,36 +596,6 @@ You can send a push message as an HTTP `POST` request to https://push.codenameon You can thus send a push from Java EE using code like this: -[source,java] ------ -HttpURLConnection connection = (HttpURLConnection)new URL("https://push.codenameone.com/push/push").openConnection(); -connection.setDoOutput(true); -connection.setRequestMethod("POST"); -connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); -String cert = ITUNES_DEVELOPMENT_PUSH_CERT; -String pass = ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD; -if(ITUNES_PRODUCTION_PUSH) { - cert = ITUNES_PRODUCTION_PUSH_CERT; - pass = ITUNES_PRODUCTION_PUSH_CERT_PASSWORD; -} -String query = "token=" + PUSH_TOKEN + - "&device=" + URLEncoder.encode(deviceId1, "UTF-8") + - "&device=" + URLEncoder.encode(deviceId2, "UTF-8") + - "&device=" + URLEncoder.encode(deviceId3, "UTF-8") + - "&type=1" + - "&auth=" + URLEncoder.encode(FCM_SERVER_API_KEY, "UTF-8") + - "&certPassword=" + URLEncoder.encode(pass, "UTF-8") + - "&cert=" + URLEncoder.encode(cert, "UTF-8") + - "&body=" + URLEncoder.encode(MESSAGE_BODY, "UTF-8") + - "&production=" + ITUNES_PRODUCTION_PUSH + - "&sid=" + URLEncoder.encode(WNS_SID, "UTF-8") + - "&client_secret=" + URLEncoder.encode(WNS_CLIENT_SECRET, "UTF-8"); -try (OutputStream output = connection.getOutputStream()) { - output.write(query.getBytes("UTF-8")); -} -int c = connection.getResponseCode(); -// read response JSON ------ Notice that you can send a push to 500 devices. To send in larger batches you need to split the push requests into 500 device batches. @@ -772,20 +607,14 @@ If there is an error that isn't fatal such as quota exceeded etc. You will get a [source,JavaScript] ----- -{"error":"Error message"} +include::../demos/javascript/src/main/snippets/developer-guide/push-notifications.js[tag=push-notifications-javascript-001,indent=0] ----- A normal response, will be an array with results: [source,JavaScript] ----- -[ - {"id"="deviceId","status"="error","message"="Invalid Device ID"}, - {"id"="cn1-gcm-nativegcmkey","status"="updateId", "newId"="cn1-gcm-newgcmkey"}, - {"id"="cn1-gcm-okgcmkey","status"="OK"}, - {"id"="cn1-gcm-errorkey","status"="error","message"="Server error message"}, - {"id"="cn1-ios-iphonekey","status"="inactive"}, -] +include::../demos/javascript/src/main/snippets/developer-guide/push-notifications.js[tag=push-notifications-javascript-002,indent=0] ----- diff --git a/docs/developer-guide/SVG-Transcoder.asciidoc b/docs/developer-guide/SVG-Transcoder.asciidoc index 5d028d62470..7965c0ae0c0 100644 --- a/docs/developer-guide/SVG-Transcoder.asciidoc +++ b/docs/developer-guide/SVG-Transcoder.asciidoc @@ -51,19 +51,14 @@ src/main/css/ + [source,css] ---- -HomeIcon { - background: url(home.svg); - cn1-svg-width: 6mm; - cn1-svg-height: 6mm; - bg-type: image_scaled_fit; -} +include::../demos/common/src/main/css/developer-guide/svg-transcoder.css[tag=svg-transcoder-css-001,indent=0] ---- . Build the project: + [source,bash] ---- -mvn package +include::../demos/common/src/main/snippets/developer-guide/svg-transcoder.sh[tag=svg-transcoder-bash-001,indent=0] ---- That's it. App code doesn't call into the registry. The transcoder @@ -96,9 +91,7 @@ reach for first. [source,css] ---- -StarIcon { background: url(star.svg); cn1-svg-width: 4mm; cn1-svg-height: 4mm; } -PrimaryIcon { background: url(home.svg); cn1-svg-width: 6mm; cn1-svg-height: 6mm; } -LogoBanner { background: url(logo.svg); cn1-svg-width: 32mm; cn1-svg-height: 12mm; } +include::../demos/common/src/main/css/developer-guide/svg-transcoder.css[tag=svg-transcoder-css-002,indent=0] ---- Use `cn1-source-dpi: ` (accepted keywords: `low`, `medium`, @@ -118,13 +111,7 @@ The `cn1app` archetype binds the `transcode-svg` goal to the [source,xml] ---- - - transcode-svg - generate-sources - - transcode-svg - - +include::../demos/common/src/main/snippets/developer-guide/svg-transcoder.xml[tag=svg-transcoder-xml-001,indent=0] ---- When Maven runs the mojo: @@ -157,22 +144,11 @@ no SVGs gets no weaving. If you don't use `theme.css` but still want a transcoded SVG, construct the generated class directly: -[source,java] ----- -Image home = new com.codename1.generated.svg.Home(6f, 6f); // 6mm × 6mm -button.setIcon(home); ----- Constructors come in three flavours, matching the three CSS sizing mechanisms above. The two-`float` constructor takes millimeters; that's the one to prefer for the reasons in the previous section. -[source,java] ----- -public Home(float widthMm, float heightMm) { ... } // for cn1-svg-width / cn1-svg-height -public Home(int sourceDensity) { ... } // for cn1-source-dpi hints -public Home() { ... } // default DENSITY_MEDIUM source ----- `scaled(int, int)` returns a lightweight view that reports the requested dimensions from `getWidth()` / `getHeight()` and shares the @@ -228,19 +204,12 @@ src/main/css/ [source,css] ---- -SpinnerStyle { - background: url(spinner.json); - cn1-svg-width: 12mm; - cn1-svg-height: 12mm; - bg-type: image_scaled_fit; -} +include::../demos/common/src/main/css/developer-guide/svg-transcoder.css[tag=svg-transcoder-css-003,indent=0] ---- [source,java] ---- -Image spin = Resources.getGlobalResources().getImage("spinner.json"); -// or by stem, like a multi-image: -Image spin2 = Resources.getGlobalResources().getImage("spinner"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SvgTranscoderJava003Snippet.java[tag=svg-transcoder-java-003,indent=0] ---- `.lottie` (dotLottie ZIP) files are accepted alongside `.json` for diff --git a/docs/developer-guide/Skin-Designer.asciidoc b/docs/developer-guide/Skin-Designer.asciidoc index b9613bf9a5e..169de00f956 100644 --- a/docs/developer-guide/Skin-Designer.asciidoc +++ b/docs/developer-guide/Skin-Designer.asciidoc @@ -180,13 +180,7 @@ layout: [source,text] ---- -Apple-iPhone-16-Pro.skin/ - skin.png # portrait body (device frame + transparent screen + cutouts) - skin_l.png # 90° rotated portrait - skin_map.png # black rect = screen, white = frame, used for hit-testing - skin_map_l.png # rotated map - iOS7Theme.res # bundled native theme (or android_holo_light.res / winTheme.res) - skin.properties # platform metadata, safe-area, PPI, display rect +include::../demos/common/src/main/snippets/developer-guide/skin-designer.txt[tag=skin-designer-text-001,indent=0] ---- The `skin.properties` file is a normal Java `.properties`. The most @@ -194,40 +188,7 @@ important keys the wizard writes: [source,properties] ---- -touch=true -platformName=ios -tablet=false -ppi=460 -pixelRatio=18.110236220472443 - -# roundScreen=true makes the simulator paint skin.png *over* the rendered -# UI rather than clipping the UI to non-frame pixels. That overlay step is -# what makes the Dynamic Island / punch-hole shapes appear "floating" on -# top of the iOS status bar instead of being carved out of the display. -roundScreen=true -displayX=89 -displayY=89 -displayWidth=1206 -displayHeight=2622 - -# Safe area in display-relative coordinates (origin = screen top-left). -safePortraitX=0 -safePortraitY=237 -safePortraitWidth=1206 -safePortraitHeight=2272 -safeLandscapeX=237 -safeLandscapeY=0 -safeLandscapeWidth=2272 -safeLandscapeHeight=1206 - -# These get composed by overrideNames(device) — they let users layer -# device-specific styling on top of the platform theme via -# UIManager.addThemeProps()-style theme inheritance. -overrideNames=phone,ios,iphone - -systemFontFamily=SF Pro -proportionalFontFamily=SF Pro -monospaceFontFamily=SF Mono +include::../demos/common/src/main/snippets/developer-guide/skin-designer.properties[tag=skin-designer-properties-001,indent=0] ---- NOTE: The wizard intentionally does *not* write `smallFontSize`, diff --git a/docs/developer-guide/TVPlatforms.asciidoc b/docs/developer-guide/TVPlatforms.asciidoc index b257bd3d2b2..7ba5bbcbe08 100644 --- a/docs/developer-guide/TVPlatforms.asciidoc +++ b/docs/developer-guide/TVPlatforms.asciidoc @@ -30,15 +30,7 @@ TV at runtime. This is the living-room analog of the existing `isTablet()`, [source,java] ---- -Form f = new Form(BoxLayout.y()); -if (CN.isTV()) { - // 10-foot UI: larger fonts, generous spacing, focus-driven navigation - f.add(new Label("Hello TV")); -} else { - // Full phone/tablet layout - f.add(new Label("Hello")); -} -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TvplatformsJava001Snippet.java[tag=tvplatforms-java-001,indent=0] ---- Codename One's existing D-pad/arrow focus traversal (the same mechanism that @@ -55,17 +47,7 @@ selected at runtime when `Display.isTV()` (respectively `isWatch()`) returns [source,css] ---- -Label { - color: black; -} - -@media device-tv { - Label { - /* Larger type for the 10-foot UI */ - font-size: 3mm; - color: white; - } -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=tvplatforms-css-001,indent=0] ---- === Building for Android TV / Google TV @@ -75,7 +57,7 @@ Enable the Android TV manifest metadata with a single build hint in [source,properties] ---- -codename1.arg.android.tv=true +include::../demos/common/src/main/snippets/developer-guide/tvplatforms.properties[tag=tvplatforms-properties-001,indent=0] ---- This makes the build: @@ -110,7 +92,7 @@ to the `watchNative.*` hints used for Apple Watch): [source,properties] ---- -codename1.arg.tvNative.enabled=true +include::../demos/common/src/main/snippets/developer-guide/tvplatforms.properties[tag=tvplatforms-properties-002,indent=0] ---- The tvOS build produces a standalone Apple TV app. Because tvOS uses a separate diff --git a/docs/developer-guide/Testing-with-JUnit.adoc b/docs/developer-guide/Testing-with-JUnit.adoc index 633922ede3b..4b8ea6e114f 100644 --- a/docs/developer-guide/Testing-with-JUnit.adoc +++ b/docs/developer-guide/Testing-with-JUnit.adoc @@ -27,70 +27,24 @@ The cn1app archetype generates a `common/pom.xml` and `javase/pom.xml` that alre [source,xml,title='common/pom.xml'] ---- - - - com.codenameone - codenameone-javase - test - - - org.junit.jupiter - junit-jupiter - 5.9.3 - test - - +include::../demos/common/src/main/snippets/developer-guide/testing-with-junit.xml[tag=testing-with-junit-xml-001,indent=0] ---- [source,xml,title='javase/pom.xml'] ---- - - org.junit.jupiter - junit-jupiter - 5.9.3 - test - +include::../demos/common/src/main/snippets/developer-guide/testing-with-junit.xml[tag=testing-with-junit-xml-002,indent=0] ---- Tests live in `common/src/test/java`. Surefire only runs from the `javase` module — the `common` module keeps Surefire skipped (`true`) to avoid running each test twice, since `javase/pom.xml` mounts the same sources via `${project.basedir}/../common/src/test/java`. === A minimal JUnit test -[source,java] ----- -package com.example.myapp; - -import com.codename1.testing.junit.CodenameOneTest; -import com.codename1.testing.junit.RunOnEdt; -import com.codename1.ui.CN; -import com.codename1.ui.Display; -import com.codename1.ui.Form; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@CodenameOneTest -class GreetingFormTest { - - @Test - @RunOnEdt - void formShowsExpectedTitle() { - new Form("Hello").show(); - assertEquals("Hello", Display.getInstance().getCurrent().getTitle()); - assertTrue(CN.isEdt(), "@RunOnEdt method runs on the Codename One EDT"); - } -} ----- Run with: [source,bash] ---- -mvn -pl javase test # all JUnit + cn1:test -mvn -pl javase test -Dtest=GreetingFormTest # one class -mvn -pl javase test -Dtest=GreetingFormTest#formShowsExpectedTitle # one method +include::../demos/common/src/main/snippets/developer-guide/testing-with-junit.sh[tag=testing-with-junit-bash-001,indent=0] ---- === `@CodenameOneTest` @@ -106,44 +60,15 @@ A class-level meta-annotation equivalent to `@ExtendWith(CodenameOneExtension.cl Place on a method (or on the class for all tests) when the body mutates UI state. -[source,java] ----- -@Test -@RunOnEdt // body runs on the EDT -void canMutateUiSafely() { - Form f = new Form("X"); - f.add(new com.codename1.ui.Button("Tap")); - f.show(); // safe -- we are on the EDT -} - -@Test -@RunOnEdt(timeoutMillis = 60000) // default 30s; override for slow form builds -void slowFormBuild() { /* ... */ } ----- Class-level `@RunOnEdt` also routes `@BeforeEach`/`@AfterEach` methods through the EDT. Method-level `@RunOnEdt` is scoped to that one `@Test`. -Tests that only exercise pure model or utility code can omit `@RunOnEdt` and run on the JUnit worker thread — they are faster because nothing pumps the EDT. +Tests that only exercise pure model or utility code can omit `@RunOnEdt` and run on the JUnit worker thread — they're faster because nothing pumps the EDT. === `@SimulatorProperty` / `@SimulatorProperties` Set a property visible to the simulator before the test runs. -[source,java] ----- -@Test -@SimulatorProperty(name = "feature.flag", value = "on") -void appReadsFlagFromDisplayProperty() { - assertEquals("on", Display.getInstance().getProperty("feature.flag", null)); -} - -@Test -@SimulatorProperties({ - @SimulatorProperty(name = "user.tier", value = "pro"), - @SimulatorProperty(name = "user.region", value = "eu") -}) -void multipleAppProperties() { /* ... */ } ----- The `scope` field selects where the value lands: @@ -168,24 +93,11 @@ Loads a base theme resource and installs it through `UIManager.setThemeProps`, t *By native theme.* Use the `NativeTheme` enum for the themes bundled into the simulator jar -- the same set the simulator's *Simulate > Native Theme* menu offers: -[source,java] ----- -@Test @Theme(nativeTheme = NativeTheme.IOS_MODERN) void rendersUnderIosModern() { /* ... */ } -@Test @Theme(nativeTheme = NativeTheme.IOS_FLAT) void rendersUnderIosFlat() { /* ... */ } -@Test @Theme(nativeTheme = NativeTheme.IPHONE_PRE_FLAT) void rendersUnderClassicIos() { /* ... */ } -@Test @Theme(nativeTheme = NativeTheme.ANDROID_MATERIAL) void rendersUnderAndroid() { /* ... */ } -@Test @Theme(nativeTheme = NativeTheme.ANDROID_HOLO_LIGHT) void rendersUnderHoloLight() { /* ... */ } -@Test @Theme(nativeTheme = NativeTheme.ANDROID_LEGACY) void rendersUnderAndroidLegacy(){ /* ... */ } ----- The enum carries the resource path (`NativeTheme.IOS_MODERN.resourcePath()` returns `/iOSModernTheme.res`) and the simulator-menu label (`displayName()`), so test reports can render the same name a user sees in the simulator UI. *By resource path.* For app themes shipped under `src/main/resources` or `src/test/resources`, point `value` at the `.res` file with a leading slash: -[source,java] ----- -@Test @Theme("/MyAppTheme.res") void rendersAppTheme() { /* ... */ } ----- If both `nativeTheme` and `value` are set, `nativeTheme` wins. If neither is set, the annotation is a no-op. @@ -193,29 +105,11 @@ If both `nativeTheme` and `value` are set, `nativeTheme` wins. If neither is set Toggles dark/light mode via `Display.setDarkMode(Boolean)` and refreshes the active form. -[source,java] ----- -@Test @DarkMode void mainFormIsLegibleInDark() { /* ... */ } -@Test @DarkMode(enabled = false) void mainFormIsLegibleInLight() { /* ... */ } ----- === `@LargerText` Sets the accessibility text-scale multiplier — the same knob exposed by the simulator's *Simulate → Larger Text* submenu. Useful for catching layout regressions at accessibility font sizes. -[source,java] ----- -@CodenameOneTest -@LargerText // class-level: every test at 1.3x (AX2) -class AccessibilityTest { - - @Test void buttonsStillFit() { /* ... */ } - - @Test - @LargerText(scale = 2.0f) // method-level override: AX5 - void buttonsAtExtremeScale() { /* ... */ } -} ----- `scale = 1.0f` restores the default size; common values mirror the menu's `1.3f` / `1.6f` / `2.0f` presets. @@ -223,15 +117,6 @@ class AccessibilityTest { Forces the simulator into portrait or landscape for the test. -[source,java] ----- -@Test -@Orientation(Orientation.Value.LANDSCAPE) -void formStillFitsInLandscape() { - assertFalse(Display.getInstance().isPortrait()); - // ... layout assertions ... -} ----- This calls a non-persisting setter on `JavaSEPort` and sets an explicit-portrait flag honored by `Display.isPortrait()` — so unit-test JVMs (where the canvas inherits the host window's full size and would otherwise read landscape on every wide screen) get the expected orientation back. @@ -239,31 +124,11 @@ This calls a non-persisting setter on `JavaSEPort` and sets an explicit-portrait Flips the look-and-feel into right-to-left mode (Arabic, Hebrew) via `UIManager.getInstance().getLookAndFeel().setRTL(...)`. The active form is revalidated so existing layouts reflow before the test body asserts. -[source,java] ----- -@Test @RTL void mirrorsCorrectly() { /* ... */ } -@Test @RTL(enabled = false) void restoresLtrLayout() { /* ... */ } ----- === Annotation resolution When the same annotation appears at both the class and the method level, *method wins*. Annotations the method doesn't override are inherited from the class. Annotations that appear on neither leave Display state alone — the extension never resets a knob the caller didn't ask for. Use `@AfterEach` for cross-test cleanup if a class must leave the simulator pristine for the next one. -[source,java] ----- -@CodenameOneTest -@LargerText(scale = 1.3f) // class default -@DarkMode // class default -class LayoutTest { - - @Test - @LargerText(scale = 2.0f) // overrides class -> 2.0x for this test - void extremeScale() { /* runs at 2.0x scale + dark mode */ } - - @Test - void defaultScale() { /* runs at 1.3x scale + dark mode */ } -} ----- === EDT semantics @@ -294,51 +159,16 @@ They don't trip over each other. `mvn install` runs Surefire during the `test` p [source,bash] ---- -mvn -pl javase test # both runners -mvn -pl javase test -DskipTests # skip Surefire, cn1:test still runs -mvn -pl javase test -Dtest=NoMatch # filter Surefire to nothing, - # cn1:test still runs +include::../demos/common/src/main/snippets/developer-guide/testing-with-junit.sh[tag=testing-with-junit-bash-002,indent=0] ---- === Side-by-side example -The same "sign in, assert we land on Home" check, written both ways: +The same "sign in, assert Home is shown" check, written both ways: .AbstractTest / `cn1:test` -[source,java] ----- -public class LoginNavTest extends com.codename1.testing.AbstractTest { - @Override public boolean shouldExecuteOnEDT() { return true; } - - @Override public boolean runTest() throws Exception { - new MyApp().runApp(); - TestUtils.waitForFormTitle("Login"); - TestUtils.setText("usernameField", "alice"); - TestUtils.clickButtonByLabel("Sign In"); - TestUtils.waitForFormTitle("Home"); - return TestUtils.screenshotTest("home-screen"); - } -} ----- .JUnit 5 / Surefire -[source,java] ----- -@CodenameOneTest -class LoginNavTest { - - @Test - @RunOnEdt - void signingInLandsOnHome() throws Exception { - new MyApp().runApp(); - TestUtils.waitForFormTitle("Login"); - TestUtils.setText("usernameField", "alice"); - TestUtils.clickButtonByLabel("Sign In"); - TestUtils.waitForFormTitle("Home"); - assertTrue(TestUtils.screenshotTest("home-screen"), "home screenshot regressed"); - } -} ----- The UI driving (`TestUtils.*`) is identical -- `TestUtils` is independent of the runner. What changes is the lifecycle plumbing: `shouldExecuteOnEDT()` + `runTest() returns boolean` becomes `@RunOnEdt` + `@Test void` + a standard assertion library. diff --git a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc index 7137fec7ce2..7d1a229ad44 100644 --- a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc +++ b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc @@ -59,15 +59,11 @@ https://www.codenameone.com/javadoc/com/codename1/ui/Form.html[Form] is the top- [source,java] ---- -Form currentForm = Display.getInstance().getCurrent(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava001Snippet.java[tag=the-components-of-codename-one-java-001,indent=0] ---- A form is a container with a title, a content area, and an optional menu or menu-bar area. When you call methods such as `add` or `remove` on a form, you're invoking something that maps to this: -[source,java] ----- -myForm.getContentPane().add(...); ----- `Form` is effectively a https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container] with a border layout. The north section holds the title area, the south section holds the optional menu bar, and the center stretches as the content pane. Place all components in the content pane. @@ -110,14 +106,7 @@ If you create a container that should always stay clear of the unsafe portions o [source,java] ---- -Form form = new Form(new BorderLayout()); - -Container bottomBar = new Container(BoxLayout.x()); -bottomBar.setSafeArea(true); -bottomBar.addAll(new Button("Home"), new Button("Search"), new Button("Profile")); - -form.add(BorderLayout.SOUTH, bottomBar); -form.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava003Snippet.java[tag=the-components-of-codename-one-java-003,indent=0] ---- Safe-area padding is applied when the container **doesn't** have a scrollable parent. For scrollable content, you can assume the user can scroll the component into view instead. @@ -126,12 +115,7 @@ Most layouts never need to know where the safe area begins, but if you draw manu [source,java] ---- -Form form = Display.getInstance().getCurrent(); -Rectangle safe = form.getSafeArea(); - -Graphics g = ...; // e.g. inside paint() -g.setClip(safe.getX(), safe.getY(), safe.getWidth(), safe.getHeight()); -// Custom drawing code that should avoid the notch/gesture areas +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava004Snippet.java[tag=the-components-of-codename-one-java-004,indent=0] ---- The rectangle returned by `Form#getSafeArea()` is updated automatically whenever the OS reports a change (rotation, multitasking gestures, showing or hiding the system navigation area, etc.). In unusual situations where you adjust the safe-area root yourself (for example, when animating a container in from off-screen) you can force a recalculation by calling `Form#setSafeAreaChanged()`. @@ -142,9 +126,7 @@ Safe-area padding is calculated relative to a "safe-area root." Forms are roots [source,java] ---- -Container drawer = new Container(BoxLayout.y()); -drawer.setSafeAreaRoot(true); // Ensure safe margins apply before the drawer is visible -drawer.setSafeArea(true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava005Snippet.java[tag=the-components-of-codename-one-java-005,indent=0] ---- Marking the drawer as both a root and a safe area prevents a jump the moment it becomes visible, because the safe padding is already applied while it's off-screen. @@ -178,11 +160,7 @@ For example: a modal dialog can be expressed as such: [source,java] ---- -if(Dialog.show("Click Yes Or No", "Select one", "Yes", "No")) { - // user clicked yes -} else { - // user clicked no -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava006Snippet.java[tag=the-components-of-codename-one-java-006,indent=0] ---- Notice that during the `show` call above the execution of the next line was "paused" until you got a response from the user and once the response was returned you could proceed directly. @@ -208,10 +186,7 @@ For example: you could do something like this to show a simple modal `Dialog`: [source,java] ---- -Dialog d = new Dialog("Title"); -d.setLayout(new BorderLayout()); -d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); -d.showPacked(BorderLayout.SOUTH, true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava007Snippet.java[tag=the-components-of-codename-one-java-007,indent=0] ---- .Custom modal Dialog in the south position @@ -223,10 +198,7 @@ You can position a `Dialog` by determining the space from the edges for example: [source,java] ---- -Dialog d = new Dialog("Title"); -d.setLayout(new BorderLayout()); -d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); -d.show(hi.getHeight() / 2, 0, 0, 0); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava008Snippet.java[tag=the-components-of-codename-one-java-008,indent=0] ---- .Custom Dialog positioned absolutely @@ -247,11 +219,7 @@ By default a `Dialog` uses a platform specific tint color when it's showing for [source,java] ---- -Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS)); -Button showDialog = new Button("Tint"); -showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On....", "OK", null)); -hi.add(showDialog); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava009Snippet.java[tag=the-components-of-codename-one-java-009,indent=0] ---- .Dialog with tinted background @@ -265,12 +233,7 @@ You can also manipulate this default value globally using the theme constant `ti [source,java] ---- -Form hi = new Form("Tint Dialog", new BoxLayout(BoxLayout.Y_AXIS)); -hi.setTintColor(0x7700ff00); -Button showDialog = new Button("Tint"); -showDialog.addActionListener((e) -> Dialog.show("Tint", "Is On....", "OK", null)); -hi.add(showDialog); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava010Snippet.java[tag=the-components-of-codename-one-java-010,indent=0] ---- .Dialog with green tinted background @@ -282,12 +245,7 @@ NOTE: Not all device types support blur you can test if your device supports it [source,java] ---- -Form hi = new Form("Blur Dialog", new BoxLayout(BoxLayout.Y_AXIS)); -Dialog.setDefaultBlurBackgroundRadius(8); -Button showDialog = new Button("Blur"); -showDialog.addActionListener((e) -> Dialog.show("Blur", "Is On....", "OK", null)); -hi.add(showDialog); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava011Snippet.java[tag=the-components-of-codename-one-java-011,indent=0] ---- .The blur effect coupled with the OS default tint @@ -297,7 +255,7 @@ It might be a bit hard to notice the blur effect with the tinting so here is the [source,java] ---- -hi.setTintColor(0); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava012Snippet.java[tag=the-components-of-codename-one-java-012,indent=0] ---- .The blur effect is more pronounced when the tint is disabled @@ -308,13 +266,6 @@ image::img/components-dialog-blur-no-tint.png[The blur effect is more pronounced A popup dialog is a common mobile paradigm showing a `Dialog` that points at a specific component. It's a standard `Dialog` that's shown in a unique way: -[source,java] ----- -Dialog d = new Dialog("Title"); -d.setLayout(new BorderLayout()); -d.add(BorderLayout.CENTER, new SpanLabel("Dialog Body", "DialogBody")); -d.showPopupDialog(showDialog); ----- .Popup Dialog image::img/components-dialog-popup.png[Popup Dialog,scaledwidth=20%] @@ -344,14 +295,6 @@ The block image of the dialog should have empty pixels in the sides to reserve s You will need to define the following theme constants for the arrow to work: -[source,java] ----- -PopupDialogArrowBool=true -PopupDialogArrowTopImage=arrow up image -PopupDialogArrowBottomImage=arrow down image -PopupDialogArrowLeftImage=arrow left image -PopupDialogArrowRightImage=arrow right image ----- Then style the `PopupDialog` UIID with the image for the `Dialog` itself. @@ -375,14 +318,7 @@ Using the interaction dialog is pretty trivial and like dialog: [source,java] ---- -InteractionDialog dlg = new InteractionDialog("Hello"); -dlg.setLayout(new BorderLayout()); -dlg.add(BorderLayout.CENTER, new Label("Hello Dialog")); -Button close = new Button("Close"); -close.addActionListener((ee) -> dlg.dispose()); -dlg.addComponent(BorderLayout.SOUTH, close); -Dimension pre = dlg.getContentPane().getPreferredSize(); -dlg.show(0, 0, Display.getInstance().getDisplayWidth() - (pre.getWidth() + pre.getWidth() / 6), 0); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava015Snippet.java[tag=the-components-of-codename-one-java-015,indent=0] ---- .Interaction Dialog @@ -405,18 +341,6 @@ https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] represent `Label` text can be positioned in one of 4 locations as such: -[source,java] ----- -Label left = new Label("Left", icon); -left.setTextPosition(Component.LEFT); -Label right = new Label("Right", icon); -right.setTextPosition(Component.RIGHT); -Label bottom = new Label("Bottom", icon); -bottom.setTextPosition(Component.BOTTOM); -Label top = new Label("Top", icon); -top.setTextPosition(Component.TOP); -hi.add(left).add(right).add(bottom).add(top); ----- .Label positions image::img/components-label-text-position.png[Label positions,scaledwidth=20%] @@ -447,24 +371,7 @@ To activate this feature use `setAutoSizeMode(true)` for example: [source,java] ---- -Form hi = new Form("AutoSize", BoxLayout.y()); - -Label a = new Label("Short Text"); -a.setAutoSizeMode(true); -Label b = new Label("Much Longer Text than the previous line..."); -b.setAutoSizeMode(true); -Label c = new Label("MUCH MUCH MUCH Much Longer Text than the previous line by a pretty big margin..."); -c.setAutoSizeMode(true); - -Label a1 = new Button("Short Text"); -a1.setAutoSizeMode(true); -Label b1 = new Button("Much Longer Text than the previous line..."); -b1.setAutoSizeMode(true); -Label c1 = new Button("MUCH MUCH MUCH Much Longer Text than the previous line by a pretty big margin..."); -c1.setAutoSizeMode(true); -hi.addAll(a, b, c, a1, b1, c1); - -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava017Snippet.java[tag=the-components-of-codename-one-java-017,indent=0] ---- .Automatically sizes the fonts of the buttons/labels based on text and available space @@ -500,43 +407,6 @@ TIP: If you need to prevent specific types of input check out the < { - if(current.getText().length() == 5) { - current.stopEditing(); - current.setText(val.substring(0, 4)); - next.setText(val.substring(4)); - next.startEditingAsync(); - } - }); -} ----- Notice you can invoke `stopEditing(Runnable)` where you receive a callback as editing is stopped. @@ -590,12 +441,6 @@ TIP: When working with a virtual keyboard it's important that the parent `Contai By default, the virtual keyboard on Android has a "Done" button, you can customize it to be a search icon, a send icon, or a go icon using a hint such as this: -[source,java] ----- -searchTextField.putClientProperty("searchField", Boolean.TRUE); -sendTextField.putClientProperty("sendButton", Boolean.TRUE); -goTextField.putClientProperty("goButton", Boolean.TRUE); ----- This will adapt the icon for the action on the keys. @@ -615,10 +460,6 @@ NOTE: This works with 3rd party keyboards too... For example, this behavior might not be desired so to block that you can do: -[source,java] ----- -tf.putClientProperty("iosHideToolbar", Boolean.TRUE); ----- This will hide the toolbar for that given field. @@ -630,17 +471,9 @@ iOS has a convention where an X can be placed after the text field to clear it. You can wrap a `TextField` with a clearable wrapper to get this effect on all platforms. For example: replace this: -[source,java] ----- -cnt.add(myTextField); ----- With this: -[source,java] ----- -cnt.add(ClearableTextField.wrap(myTextField)); ----- You can also specify the size of the clear icon if you wish. This is technically a `Container` with the text field style and a button to clear the text at the edge. @@ -660,19 +493,7 @@ Doing this with text fields is possible but would require code that looks a bit [source,java] ---- -TextModeLayout tl = new TextModeLayout(3, 2); -Form f = new Form("Pixel Perfect", tl); -TextComponent title = new TextComponent().label("Title"); -TextComponent price = new TextComponent().label("Price"); -TextComponent location = new TextComponent().label("Location"); -TextComponent description = new TextComponent().label("Description").multiline(true); - -f.add(tl.createConstraint().horizontalSpan(2), title); -f.add(tl.createConstraint().widthPercentage(30), price); -f.add(tl.createConstraint().widthPercentage(70), location); -f.add(tl.createConstraint().horizontalSpan(2), description); -f.setEditOnShow(title.getField()); -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava025Snippet.java[tag=the-components-of-codename-one-java-025,indent=0] ---- TIP: This code uses the `TextModeLayout` which is discussed in the layouts section @@ -681,11 +502,7 @@ The text component uses a builder approach to set various values for example: [source,java] ---- -TextComponent t = new TextComponent(). - text("This appears in the text field"). - hint("This is the hint"). - label("This is the label"). - multiline(true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava026Snippet.java[tag=the-components-of-codename-one-java-026,indent=0] ---- The code is pretty self-explanatory and more convenient than typical setters/getters. It automatically handles the floating hint style of animation when running on Android. @@ -696,12 +513,6 @@ The validator class supports text component and it should "work." But the cool t If you add to the sample above a `Validator`: -[source,java] ----- -Validator val = new Validator(); -val.addConstraint(title, new LengthConstraint(2)); -val.addConstraint(price, new NumericConstraint(true)); ----- You would see something that looks like this on Android: @@ -718,9 +529,7 @@ The underlying system is the `errorMessage` method which you can chain like the [source,java] ---- -TextComponent tc = new TextComponent(). - label("Input Required"). - errorMessage("Input is essential in this field"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava028Snippet.java[tag=the-components-of-codename-one-java-028,indent=0] ---- @@ -730,26 +539,6 @@ To keep the code common and generic you use the `InputComponent` abstract base c A picker can work with your existing sample using code like this: -[source,java] ----- -TextModeLayout tl = new TextModeLayout(3, 2); -Form f = new Form("Pixel Perfect", tl); -TextComponent title = new TextComponent().label("Title"); -TextComponent price = new TextComponent().label("Price"); -TextComponent location = new TextComponent().label("Location"); -PickerComponent date = PickerComponent.createDate(new Date()).label("Date"); -TextComponent description = new TextComponent().label("Description").multiline(true); -Validator val = new Validator(); -val.addConstraint(title, new LengthConstraint(2)); -val.addConstraint(price, new NumericConstraint(true)); -f.add(tl.createConstraint().widthPercentage(60), title); -f.add(tl.createConstraint().widthPercentage(40), date); -f.add(location); -f.add(price); -f.add(tl.createConstraint().horizontalSpan(2), description); -f.setEditOnShow(title.getField()); -f.show(); ----- This produces the following which looks pretty standard: @@ -797,10 +586,7 @@ Here is a trivial hello world style `Button`: [source,java] ---- -Form hi = new Form("Button"); -Button b = new Button("My Button"); -hi.add(b); -b.addActionListener((e) -> Log.p("Clicked")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava030Snippet.java[tag=the-components-of-codename-one-java-030,indent=0] ---- .Simple button in the iOS styling, notice iOS doesn't have borders on buttons... @@ -810,12 +596,7 @@ Such a button can be styled to look like a link using code like this or by makin [source,java] ---- -Form hi = new Form("Button"); -Button b = new Button("Link Button"); -b.getAllStyles().setBorder(Border.createEmpty()); -b.getAllStyles().setTextDecoration(Style.TEXT_DECORATION_UNDERLINE); -hi.add(b); -b.addActionListener((e) -> Log.p("Clicked")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java[tag=the-components-of-codename-one-java-031,indent=0] ---- .Button styled to look like a link @@ -837,9 +618,7 @@ For this purpose you have got the theme constant `hasRaisedButtonBool` which wil [source,java] ---- -if(UIManager.getInstance().isThemeConstant("hasRaisedButtonBool", false)) { - // that means we can use a raised button -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava032Snippet.java[tag=the-components-of-codename-one-java-032,indent=0] ---- To enable this you have the `RaisedButton` UIID that derives from `Button` and will act like it except for the places where `hasRaisedButtonBool` is true in which case it will look like this: @@ -856,12 +635,7 @@ image::img/raised-flat-buttons-purple.png[Purple raised button,scaledwidth=40%] [source,java] ---- -Form f = new Form("Pixel Perfect", BoxLayout.y()); -Button b = new Button("Raised Button", "RaisedButton"); -Button r = new Button("Flat Button"); -f.add(b); -f.add(r); -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava033Snippet.java[tag=the-components-of-codename-one-java-033,indent=0] ---- ==== Ripple effect @@ -887,22 +661,6 @@ The `CheckBox` can be added to a `Container` like any other `Component` but the Notice in the sample below that you associate all the radio buttons with a group but don't do anything with the group as the radio buttons keep the reference internally. You also show the opposite side functionality and icon behavior: -[source,java] ----- -CheckBox cb1 = new CheckBox("CheckBox No Icon"); -cb1.setSelected(true); -CheckBox cb2 = new CheckBox("CheckBox With Icon", icon); -CheckBox cb3 = new CheckBox("CheckBox Opposite True", icon); -CheckBox cb4 = new CheckBox("CheckBox Opposite False", icon); -cb3.setOppositeSide(true); -cb4.setOppositeSide(false); -RadioButton rb1 = new RadioButton("Radio 1"); -RadioButton rb2 = new RadioButton("Radio 2"); -RadioButton rb3 = new RadioButton("Radio 3", icon); -new ButtonGroup(rb1, rb2, rb3); -rb2.setSelected(true); -hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3); ----- .RadioButton & CheckBox usage image::img/components-radiobutton-checkbox.png[RadioButton & CheckBox usage,scaledwidth=20%] @@ -919,22 +677,6 @@ IMPORTANT: Invoking `setToggle(true)` implicitly converts the `UIID` to `ToggleB You can convert the sample above to use toggle buttons as such: -[source,java] ----- -CheckBox cb1 = CheckBox.createToggle("CheckBox No Icon"); -cb1.setSelected(true); -CheckBox cb2 = CheckBox.createToggle("CheckBox With Icon", icon); -CheckBox cb3 = CheckBox.createToggle("CheckBox Opposite True", icon); -CheckBox cb4 = CheckBox.createToggle("CheckBox Opposite False", icon); -cb3.setOppositeSide(true); -cb4.setOppositeSide(false); -ButtonGroup bg = new ButtonGroup(); -RadioButton rb1 = RadioButton.createToggle("Radio 1", bg); -RadioButton rb2 = RadioButton.createToggle("Radio 2", bg); -RadioButton rb3 = RadioButton.createToggle("Radio 3", icon, bg); -rb2.setSelected(true); -hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3); ----- .Toggle button converted sample image::img/components-toggle-buttons.png[Toggle button converted sample,scaledwidth=20%] @@ -943,11 +685,6 @@ That's half the story though: to get the full effect of some cool toggle button For example, to enclose the `CheckBox` components in a vertical `ComponentGroup` and the `RadioButton's` in a horizontal group, change the last line of the code above as such: -[source,java] ----- -hi.add(ComponentGroup.enclose(cb1, cb2, cb3, cb4)). - add(ComponentGroup.encloseHorizontal(rb1, rb2, rb3)); ----- .Toggle button converted sample wrapped in ComponentGroup image::img/components-toggle-buttons-component-group.png[Toggle button converted sample wrapped in ComponentGroup,scaledwidth=20%] @@ -962,14 +699,7 @@ The following code adds 4 component groups to a `Container` to show the various [source,java] ---- -hi.add("Three Labels"). - add(ComponentGroup.enclose(new Label("GroupElementFirst UIID"), new Label("GroupElement UIID"), new Label("GroupElementLast UIID"))). - add("One Label"). - add(ComponentGroup.enclose(new Label("GroupElementOnly UIID"))). - add("Three Buttons"). - add(ComponentGroup.enclose(new Button("ButtonGroupFirst UIID"), new Button("ButtonGroup UIID"), new Button("ButtonGroupLast UIID"))). - add("One Button"). - add(ComponentGroup.enclose(new Button("ButtonGroupOnly UIID"))); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava037Snippet.java[tag=the-components-of-codename-one-java-037,indent=0] ---- .ComponentGroup adapts the UIID's of the components added so you can style them @@ -1006,42 +736,6 @@ NOTE: The `MultiButton` was inspired by the aesthetics of the `UITableView` iOS A common source of confusion in the `MultiButton` is the difference between the icon and the emblem, since both may have an icon image associated with them. The icon is an image representing the entry while the emblem is an optional visual representation of the action that will be undertaken when the element is pressed. Both may be used simultaneously or individually of one another: -[source,java] ----- -MultiButton twoLinesNoIcon = new MultiButton("MultiButton"); -twoLinesNoIcon.setTextLine2("Line 2"); -MultiButton oneLineIconEmblem = new MultiButton("Icon + Emblem"); -oneLineIconEmblem.setIcon(icon); -oneLineIconEmblem.setEmblem(emblem); -MultiButton twoLinesIconEmblem = new MultiButton("Icon + Emblem"); -twoLinesIconEmblem.setIcon(icon); -twoLinesIconEmblem.setEmblem(emblem); -twoLinesIconEmblem.setTextLine2("Line 2"); - -MultiButton twoLinesIconEmblemHorizontal = new MultiButton("Icon + Emblem"); -twoLinesIconEmblemHorizontal.setIcon(icon); -twoLinesIconEmblemHorizontal.setEmblem(emblem); -twoLinesIconEmblemHorizontal.setTextLine2("Line 2 Horizontal"); -twoLinesIconEmblemHorizontal.setHorizontalLayout(true); - -MultiButton twoLinesIconCheckBox = new MultiButton("CheckBox"); -twoLinesIconCheckBox.setIcon(icon); -twoLinesIconCheckBox.setCheckBox(true); -twoLinesIconCheckBox.setTextLine2("Line 2"); - -MultiButton fourLinesIcon = new MultiButton("With Icon"); -fourLinesIcon.setIcon(icon); -fourLinesIcon.setTextLine2("Line 2"); -fourLinesIcon.setTextLine3("Line 3"); -fourLinesIcon.setTextLine4("Line 4"); - -hi.add(oneLineIconEmblem). - add(twoLinesNoIcon). - add(twoLinesIconEmblem). - add(twoLinesIconEmblemHorizontal). - add(twoLinesIconCheckBox). - add(fourLinesIcon); ----- .Multiple usage scenarios for the MultiButton image::img/components-multibutton.png[Multiple usage scenarios for the MultiButton,scaledwidth=20%] @@ -1063,12 +757,6 @@ https://www.codenameone.com/javadoc/com/codename1/components/SpanButton.html[Spa Unlike the `MultiButton` it uses the `TextArea` internally to break lines seamlessly. The `SpanButton` is far simpler than the `MultiButton` and as a result isn't as configurable: -[source,java] ----- -SpanButton sb = new SpanButton("SpanButton is a composite component (lead component) that looks/acts like a Button but can break lines rather than crop them when the text is very long."); -sb.setIcon(icon); -hi.add(sb); ----- .The SpanButton Component image::img/components-spanbutton.png[The SpanButton Component,scaledwidth=20%] @@ -1084,21 +772,6 @@ https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html[Span One of the features of label that moved into `SpanLabel` to some extent is the ability to position the icon. For example, unlike a `Label` the icon position is determined by the layout manager of the composite so `setIconPosition` accepts a `BorderLayout` constraint: -[source,java] ----- -SpanLabel d = new SpanLabel("Default SpanLabel that can seamlessly line break when the text is really long."); -d.setIcon(icon); -SpanLabel l = new SpanLabel("NORTH Positioned Icon SpanLabel that can seamlessly line break when the text is really long."); -l.setIcon(icon); -l.setIconPosition(BorderLayout.NORTH); -SpanLabel r = new SpanLabel("SOUTH Positioned Icon SpanLabel that can seamlessly line break when the text is really long."); -r.setIcon(icon); -r.setIconPosition(BorderLayout.SOUTH); -SpanLabel c = new SpanLabel("EAST Positioned Icon SpanLabel that can seamlessly line break when the text is really long."); -c.setIcon(icon); -c.setIconPosition(BorderLayout.EAST); -hi.add(d).add(l).add(r).add(c); ----- .The SpanLabel Component image::img/components-spanlabel.png[The SpanLabel Component,scaledwidth=20%] @@ -1115,8 +788,7 @@ The image below was generated based on the default use of the `OnOffSwitch`: [source,java] ---- -OnOffSwitch onOff = new OnOffSwitch(); -hi.add(onOff); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava041Snippet.java[tag=the-components-of-codename-one-java-041,indent=0] ---- .The OnOffSwitch component as it appears on/off on iOS (top) and on Android (bottom) @@ -1137,21 +809,6 @@ constraints such as https://www.codenameone.com/javadoc/com/codename1/ui/validat This sample below continues from the place where the <> stopped by adding validation to that code: -[source,java] ----- -Validator v = new Validator(); -v.addConstraint(firstName, new LengthConstraint(2)). - addConstraint(surname, new LengthConstraint(2)). - addConstraint(url, RegexConstraint.validURL()). - addConstraint(email, RegexConstraint.validEmail()). - addConstraint(phone, new RegexConstraint(phoneRegex, "Must be valid phone number")). - addConstraint(num1, new LengthConstraint(4)). - addConstraint(num2, new LengthConstraint(4)). - addConstraint(num3, new LengthConstraint(4)). - addConstraint(num4, new LengthConstraint(4)); - -v.addSubmitButtons(submit); ----- .Validation & Regular Expressions image::img/validation-regex-masking-1.png[Validation and Regular Expressions,scaledwidth=20%] @@ -1172,21 +829,12 @@ TIP: This style of animation is often nicknamed "washing machine" as it spins en `InfiniteProgress` can be used in one of two ways either by embedding the component into the UI through something like this: -[source,java] ----- -myContainer.add(new InfiniteProgress()); ----- `InfiniteProgress` can also appear over the entire screen, thus blocking all input. This tints the background while the infinite progress rotates: [source,java] ---- -Dialog ip = new InfiniteProgress().showInifiniteBlocking(); - -// do some long operation here using invokeAndBlock or do something in a separate thread and callback later -// when you are done just call - -ip.dispose(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava044Snippet.java[tag=the-components-of-codename-one-java-044,indent=0] ---- .Infinite progress @@ -1213,32 +861,6 @@ image::img/components-infinitescrolladapter.png[InfiniteScrollAdapter demo code The first step is creating the webservice call, you won't go into too much detail here as webservices & IO are discussed later in the guide: -[source,java] ----- -int pageNumber = 1; -java.util.List> fetchPropertyData(String text) { - try { - ConnectionRequest r = new ConnectionRequest(); - r.setPost(false); - r.setUrl("http://api.nestoria.co.uk/api"); - r.addArgument("pretty", "0"); - r.addArgument("action", "search_listings"); - r.addArgument("encoding", "json"); - r.addArgument("listing_type", "buy"); - r.addArgument("page", "" + pageNumber); - pageNumber++; - r.addArgument("country", "uk"); - r.addArgument("place_name", text); - NetworkManager.getInstance().addToQueueAndWait(r); - Map result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r.getResponseData()), "UTF-8")); - Map response = (Map)result.get("response"); - return (java.util.List>)response.get("listings"); - } catch(Exception err) { - Log.e(err); - return null; - } -} ----- IMPORTANT: The demo code here doesn't do any error handling! This is a bad practice and it's taken here to keep the code short and readable. Proper error handling is used in the Property Cross demo. @@ -1246,44 +868,6 @@ The `fetchPropertyData` is a simplistic tool that fetches the next page of listi Now that you have a webservice lets proceed to create the UI. Check out the code annotations below: -[source,java] ----- -Form hi = new Form("InfiniteScrollAdapter", new BoxLayout(BoxLayout.Y_AXIS)); - -Style s = UIManager.getInstance().getComponentStyle("MultiLine1"); -FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s); -EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 3), false); // <1> - -InfiniteScrollAdapter.createInfiniteScroll(hi.getContentPane(), () -> { // <2> - java.util.List> data = fetchPropertyData("Leeds"); // <3> - MultiButton[] cmps = new MultiButton[data.size()]; - for(int iter = 0 ; iter < cmps.length ; iter++) { - Map currentListing = data.get(iter); - if(currentListing == null) { // <4> - InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), new Component[0], false); - return; - } - String thumb_url = (String)currentListing.get("thumb_url"); - String guid = (String)currentListing.get("guid"); - String summary = (String)currentListing.get("summary"); - cmps[iter] = new MultiButton(summary); - cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid, thumb_url)); - } - InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), cmps, true); // <5> -}, true); // <6> ----- - -<1> Placeholder is essential for the https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html[URLImage] class which this guide covers at a different place. - -<2> The `InfiniteScrollAdapter` accepts a runnable which is invoked every time you reach the edge of the scrolling. You used a closure instead of the typical run() method override. - -<3> This is a blocking call, after the method completes you will have all the data you need. Notice that this method doesn't block the EDT illegally. - -<4> If there is no more data you call the `addMoreComponents` method with a false argument. This indicates that there is no more data to fetch. - -<5> Here you add the actual components to the end of the form. Notice that you *must not* invoke the `add`/`remove` method of `Container`. Those might conflict with the work of the `InfiniteScrollAdapter`. - -<6> You pass true to show that the data isn't "prefilled" so the method should be invoked when the `Form` is first shown IMPORTANT: Don't violate the EDT in the callback. It's invoked on the event dispatch thread and it's crucial @@ -1297,35 +881,6 @@ Converting the code above to an `InfiniteContainer` is pretty simple you moved a Unlike the `InfiniteScrollAdapter` you can't use the `ContentPane` directly so you've to use a `BorderLayout` and place the `InfiniteContainer` there: -[source,java] ----- -Form hi = new Form("InfiniteContainer", new BorderLayout()); - -Style s = UIManager.getInstance().getComponentStyle("MultiLine1"); -FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s); -EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 3), false); - -InfiniteContainer ic = new InfiniteContainer() { - @Override - public Component[] fetchComponents(int index, int amount) { - java.util.List> data = fetchPropertyData("Leeds"); - MultiButton[] cmps = new MultiButton[data.size()]; - for(int iter = 0 ; iter < cmps.length ; iter++) { - Map currentListing = data.get(iter); - if(currentListing == null) { - return null; - } - String thumb_url = (String)currentListing.get("thumb_url"); - String guid = (String)currentListing.get("guid"); - String summary = (String)currentListing.get("summary"); - cmps[iter] = new MultiButton(summary); - cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid, thumb_url)); - } - return cmps; - } -}; -hi.add(BorderLayout.CENTER, ic); ----- === List, MultiList, renderers & models @@ -1408,12 +963,6 @@ When working with lists, you want the list to handle the scrolling (otherwise it It's also recommended to place the list in the `CENTER` location of a https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html[BorderLayout] to produce the most effective results. For example: -[source,java] ----- -form.setScrollable(false); -form.setLayout(new BorderLayout()); -form.add(BorderLayout.CENTER, myList); ----- ==== MultiList & DefaultListModel @@ -1425,24 +974,6 @@ The full power of the `ListModel` is still available and allows you to create a Here is a simple example of a `MultiList` containing a highly popular subject matter: -[source,java] ----- -Form hi = new Form("MultiList", new BorderLayout()); - -ArrayList> data = new ArrayList<>(); - -data.add(createListEntry("A Game of Thrones", "1996")); -data.add(createListEntry("A Clash Of Kings", "1998")); -data.add(createListEntry("A Storm Of Swords", "2000")); -data.add(createListEntry("A Feast For Crows", "2005")); -data.add(createListEntry("A Dance With Dragons", "2011")); -data.add(createListEntry("The Winds of Winter", "2016 (please, please, please)")); -data.add(createListEntry("A Dream of Spring", "Ugh")); - -DefaultListModel> model = new DefaultListModel<>(data); -MultiList ml = new MultiList(model); -hi.add(BorderLayout.CENTER, ml); ----- .Basic usage of the MultiList & DefaultListModel] image::img/components-multilist.png[Basic usage of the MultiList and DefaultListModel,scaledwidth=20%] @@ -1451,25 +982,14 @@ image::img/components-multilist.png[Basic usage of the MultiList and DefaultList [source,java] ---- -private Map createListEntry(String name, String date) { - Map entry = new HashMap<>(); - entry.put("Line1", name); - entry.put("Line2", date); - return entry; -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava050Snippet.java[tag=the-components-of-codename-one-java-050,indent=0] ---- One major piece is missing here: the cover images for the books. A simple approach would be to place the image objects into the entries using the "icon" property as such: [source,java] ---- -private Map createListEntry(String name, String date, Image cover) { - Map entry = new HashMap<>(); - entry.put("Line1", name); - entry.put("Line2", date); - entry.put("icon", cover); - return entry; -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava051Snippet.java[tag=the-components-of-codename-one-java-051,indent=0] ---- .With cover images in place @@ -1485,76 +1005,9 @@ You will fake it a bit but notice that 1M components won't be created even if yo The https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html[ListModel] interface can be implemented by anyone in this case you did a stupid simple implementation: -[source,java] ----- -class GRMMModel implements ListModel> { - @Override - public Map getItemAt(int index) { - int idx = index % 7; - switch(idx) { - case 0: - return createListEntry("A Game of Thrones " + index, "1996"); - case 1: - return createListEntry("A Clash Of Kings " + index, "1998"); - case 2: - return createListEntry("A Storm Of Swords " + index, "2000"); - case 3: - return createListEntry("A Feast For Crows " + index, "2005"); - case 4: - return createListEntry("A Dance With Dragons " + index, "2011"); - case 5: - return createListEntry("The Winds of Winter " + index, "2016 (please, please, please)"); - default: - return createListEntry("A Dream of Spring " + index, "Ugh"); - } - } - - @Override - public int getSize() { - return 1000000; - } - - @Override - public int getSelectedIndex() { - return 0; - } - - @Override - public void setSelectedIndex(int index) { - } - - @Override - public void addDataChangedListener(DataChangedListener l) { - } - - @Override - public void removeDataChangedListener(DataChangedListener l) { - } - - @Override - public void addSelectionListener(SelectionListener l) { - } - - @Override - public void removeSelectionListener(SelectionListener l) { - } - - @Override - public void addItem(Map item) { - } - - @Override - public void removeItem(int index) { - } -} ----- You can now replace the existing model by removing all the model related logic and changing the constructor call as such: -[source,java] ----- -MultiList ml = new MultiList(new GRMMModel()); ----- .It took ages to scroll this far... This goes to a million... image::img/components-millionbooks.png[It took ages to scroll this far... This goes to a million...,scaledwidth=20%] @@ -1564,50 +1017,14 @@ image::img/components-millionbooks.png[It took ages to scroll this far... This g The Renderer is a simple interface with 2 methods: -[source,java] ----- -public interface ListCellRenderer { - //This method is called by the List for each item, when the List paints itself. - public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected); - - //This method returns the List animated focus which is animated when list selection changes - public Component getListFocusComponent(List list); -} ----- The most simple/naive implementation may choose to implement the renderer as follows: -[source,java] ----- -public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){ - return new Label(value.toString()); -} - -public Component getListFocusComponent(List list){ - return null; -} ----- This will compile and work, but won't give you much, notice that you won't see the `List` selection move on the List, this is because the renderer returns a https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] with the same style regardless if it's selected or not. Now make it a bit more useful: -[source,java] ----- -public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){ - Label l = new Label(value.toString()); -if (isSelected) { - l.setFocus(true); - l.getAllStyles().setBgTransparency(100); - } else { - l.setFocus(false); - l.getAllStyles().setBgTransparency(0); - } - return l; -} public Component getListFocusComponent(List list){ - return null; -} ----- In this renderer you set the `Label.setFocus(true)` if it's selected, calling to this method doesn't give the focus to the Label, it renders the label as selected. @@ -1617,64 +1034,9 @@ That's still not efficient because you create a new `Label` each time the method To make the code tighter, keep a reference to the `Component` or extend it as https://www.codenameone.com/javadoc/com/codename1/ui/list/DefaultListCellRenderer.html[DefaultListCellRenderer] does: -[source,java] ----- -class MyRenderer extends Label implements ListCellRenderer { - public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected){ - setText(value.toString()); - if (isSelected) { - setFocus(true); - getAllStyles().setBgTransparency(100); - } else { - setFocus(false); - getAllStyles().setBgTransparency(0); - } - return this; - } - } -} ----- Now look at a more advanced Renderer: -[source,java] ----- -class ContactsRenderer extends Container implements ListCellRenderer { - - private Label name = new Label(""); - private Label email = new Label(""); - private Label pic = new Label(""); - - private Label focus = new Label(""); - - public ContactsRenderer() { - setLayout(new BorderLayout()); - addComponent(BorderLayout.WEST, pic); - Container cnt = new Container(new BoxLayout(BoxLayout.Y_AXIS)); - name.getAllStyles().setBgTransparency(0); - name.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM)); - email.getAllStyles().setBgTransparency(0); - cnt.addComponent(name); - cnt.addComponent(email); - addComponent(BorderLayout.CENTER, cnt); - - focus.getStyle().setBgTransparency(100); - } - - public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) { - - Contact person = (Contact) value; - name.setText(person.getName()); - email.setText(person.getEmail()); - pic.setIcon(person.getPic()); - return this; - } - - public Component getListFocusComponent(List list) { - return focus; - } -} ----- In this renderer you want to render a `Contact` object to the Screen, you build the `Component` in the constructor and in the getListCellRendererComponent you update the Labels' texts according to the `Contact` object. @@ -1682,16 +1044,6 @@ Notice that in this renderer you return a focus `Label` with semi transparency, For example, you can change the focus `Component` to have an icon: -[source,java] ----- -focus.getAllStyles().setBgTransparency(100); -try { - focus.setIcon(Image.createImage("/duke.png")); - focus.setAlignment(Component.RIGHT); -} catch (IOException ex) { - ex.printStackTrace(); -} ----- ==== Generic list cell renderer @@ -1734,48 +1086,7 @@ Here is a simple example of a list with checkboxes that gets updated automatical [source,java] ---- -com.codename1.ui.List list = new com.codename1.ui.List(createGenericListCellRendererModelData()); -list.setRenderer(new GenericListCellRenderer(createGenericRendererContainer(), createGenericRendererContainer())); - - -private Container createGenericRendererContainer() { - Label name = new Label(); - name.setFocusable(true); - name.setName("Name"); - Label surname = new Label(); - surname.setFocusable(true); - surname.setName("Surname"); - CheckBox selected = new CheckBox(); - selected.setName("Selected"); - selected.setFocusable(true); - Container c = BorderLayout.center(name). - add(BorderLayout.SOUTH, surname). - add(BorderLayout.WEST, selected); - c.setUIID("ListRenderer"); - return c; -} - -private Object[] createGenericListCellRendererModelData() { - Map[] data = new HashMap[5]; - data[0] = new HashMap<>(); - data[0].put("Name", "Shai"); - data[0].put("Surname", "Almog"); - data[0].put("Selected", Boolean.TRUE); - data[1] = new HashMap<>(); - data[1].put("Name", "Chen"); - data[1].put("Surname", "Fishbein"); - data[1].put("Selected", Boolean.TRUE); - data[2] = new HashMap<>(); - data[2].put("Name", "Ofir"); - data[2].put("Surname", "Leitner"); - data[3] = new HashMap<>(); - data[3].put("Name", "Yaniv"); - data[3].put("Surname", "Vakarat"); - data[4] = new HashMap<>(); - data[4].put("Name", "Meirav"); - data[4].put("Surname", "Nachmanovitch"); - return data; -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava060Snippet.java[tag=the-components-of-codename-one-java-060,indent=0] ---- .GenericListCellRenderer demo code @@ -1792,25 +1103,13 @@ This can be achieved with a custom renderer, but that's a pretty difficult task. Normally, to build the model for a renderer of this type, you use something like: -[source,java] ----- -map.put("componentName", "Component Value"); ----- What if you want componentName to be red? Just use: -[source,java] ----- -map.put("componentName_uiid", "red"); ----- This will apply the UIID "red" to the component, which you can then style in the theme. Notice that once you start doing this, you need to define this entry for all entries, for example: -[source,java] ----- -map.put("componentName_uiid", "blue"); ----- Otherwise the component will stay red for the next entry (since the renderer acts like a rubber stamp). @@ -1822,15 +1121,6 @@ As you might guess this triggers a performance penalty that's paid with every re `setRenderingPrototype` accepts a "fake" value that represents a reasonably large amount of data and it will be used to calculate the preferred size. For example: for a multiList that should render 2 lines of text with 20 characters and a 5mm square icon you can do something like this: -[source,java] ----- -Map proto = new HashMap<>(); -map.put("Line1", "WWWWWWWWWWWWWWWWWWWW"); -map.put("Line2", "WWWWWWWWWWWWWWWWWWWW"); -int mm5 = Display.getInstance().convertToPixels(5, true); -map.put("icon", Image.create(mm5, mm5)); -myMultiList.setRenderingPrototype(map); ----- ==== ComboBox @@ -1863,20 +1153,6 @@ Since a `ComboBox` is a `List` you can use everything you learned about a `List` For example: the demo below uses the GRRM demo data from above to build a `ComboBox`: -[source,java] ----- -Form hi = new Form("ComboBox", new BoxLayout(BoxLayout.Y_AXIS)); -ComboBox> combo = new ComboBox<> ( - createListEntry("A Game of Thrones", "1996"), - createListEntry("A Clash Of Kings", "1998"), - createListEntry("A Storm Of Swords", "2000"), - createListEntry("A Feast For Crows", "2005"), - createListEntry("A Dance With Dragons", "2011"), - createListEntry("The Winds of Winter", "2016 (please, please, please)"), - createListEntry("A Dream of Spring", "Ugh")); - -combo.setRenderer(new GenericListCellRenderer<>(new MultiButton(), new MultiButton())); ----- .GRRM ComboBox image::img/components-combobox.png[GRRM ComboBox,scaledwidth=25%] @@ -1893,12 +1169,6 @@ The interesting part about the slider is that it has two separate style `UIID’ `Slider` is highly customizable for example: a slider can be used to replicate a 5-star rating widget as such. Notice that this slider will work when its given its preferred size otherwise more stars will appear. That's why you place it within a `FlowLayout`: -[source,java] ----- -Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add(FlowLayout.encloseCenter(createStarRankSlider())); -hi.show(); ----- The slider itself is initialized in the code below. Notice that you can achieve almost the same result using a theme by setting the `Slider` & `SliderFull` UIID's (both in selected & unselected states). @@ -1907,37 +1177,7 @@ In fact doing this in the theme might be superior as you could use one image tha [[slider-stars-demo]] [source,java] ---- -private void initStarRankStyle(Style s, Image star) { - s.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH); - s.setBorder(Border.createEmpty()); - s.setBgImage(star); - s.setBgTransparency(0); -} - -private Slider createStarRankSlider() { - Slider starRank = new Slider(); - starRank.setEditable(true); - starRank.setMinValue(0); - starRank.setMaxValue(10); - Font fnt = Font.createTrueTypeFont("native:MainLight", "native:MainLight"). - derive(Display.getInstance().convertToPixels(5, true), Font.STYLE_PLAIN); - Style s = new Style(0xffff33, 0, fnt, (byte)0); - Image fullStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage(); - s.setOpacity(100); - s.setFgColor(0); - Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage(); - initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar); - initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(), emptyStar); - initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar); - initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar); - starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5, fullStar.getHeight())); - return starRank; -} -private void showStarPickingForm() { - Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS)); - hi.add(FlowLayout.encloseCenter(createStarRankSlider())); - hi.show(); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava067Snippet.java[tag=the-components-of-codename-one-java-067,indent=0] ---- .Star Slider set to 5 (its between 0 - 10) @@ -1954,23 +1194,6 @@ TIP: `Table` is based on the https://www.codenameone.com/javadoc/com/codename1/u Here is a trivial sample of using the standard table component: -[source,java] ----- -Form hi = new Form("Table", new BorderLayout()); -TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] { - {"Row 1", "Row A", "Row X"}, - {"Row 2", "Row B", "Row Y"}, - {"Row 3", "Row C", "Row Z"}, - {"Row 4", "Row D", "Row K"}, - }) { - public boolean isCellEditable(int row, int col) { - return col != 0; - } - }; -Table table = new Table(model); -hi.add(BorderLayout.CENTER, table); -hi.show(); ----- .Simple Table usage image::img/components-table.png[Simple Table usage,scaledwidth=20%] @@ -1981,32 +1204,6 @@ The more "interesting" capabilities of the `Table` class can be utilized through For example: -[source,java] ----- -Form hi = new Form("Table", new BorderLayout()); -TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] { - {"Row 1", "Row A", "Row X"}, - {"Row 2", "Row B can now stretch", null}, - {"Row 3", "Row C", "Row Z"}, - {"Row 4", "Row D", "Row K"}, - }) { - public boolean isCellEditable(int row, int col) { - return col != 0; - } - }; -Table table = new Table(model) { - @Override - protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { - TableLayout.Constraint con = super.createCellConstraint(value, row, column); - if(row == 1 && column == 1) { - con.setHorizontalSpan(2); - } - con.setWidthPercentage(33); - return con; - } -}; -hi.add(BorderLayout.CENTER, table); ----- .Table with spanning & fixed widths to 33% image::img/components-table-with-spanning.png[Table with spanning and fixed widths to 33%,scaledwidth=20%] @@ -2014,48 +1211,7 @@ image::img/components-table-with-spanning.png[Table with spanning and fixed widt To customize the table cell behavior you can derive the `Table` to create a "renderer like" widget, but unlike the list this component is "kept" and used as is. This means you can bind listeners to this component and work with it as you would with any other component in Codename One. The example above can be extended to include far more capabilities: -[source,java] ----- -Table table = new Table(model) { - @Override - protected Component createCell(Object value, int row, int column, boolean editable) { // <1> - Component cell; - if(row == 1 && column == 1) { // <2> - Picker p = new Picker(); - p.setType(Display.PICKER_TYPE_STRINGS); - p.setStrings("Row B can now stretch", "This is a good value", "So Is This", "Better than text field"); - p.setSelectedString((String)value); // <3> - p.setUIID("TableCell"); - p.addActionListener((e) -> getModel().setValueAt(row, column, p.getSelectedString())); // <4> - cell = p; - } else { - cell = super.createCell(value, row, column, editable); - } - if(row > -1 && row % 2 == 0) { // <5> - // pinstripe effect - cell.getAllStyles().setBgColor(0xeeeeee); - cell.getAllStyles().setBgTransparency(255); - } - return cell; - } - - @Override - protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { - TableLayout.Constraint con = super.createCellConstraint(value, row, column); - if(row == 1 && column == 1) { - con.setHorizontalSpan(2); - } - con.setWidthPercentage(33); - return con; - } -}; ----- -<1> The `createCell` method is invoked once per component but is similar conceptually to the `List` renderer. Notice that it doesn't return a "rubber stamp" though, it returns a full component. -<2> You apply the picker to one cell for simplicities sake. -<3> You need to set the value of the component manually, this is crucial since the `Table` doesn't "see" this. -<4> You need to track the event and update the model in this case as the `Table` isn't aware of the data change. -<5> You set the "pinstripe" effect by coloring even rows. Notice that unlike renderers you need to apply the coloring once as the `Components` are stateful. .Table with customize cells using the pinstripe effect image::img/components-table-pinstripe.png[Table with customize cells using the pinstripe effect,scaledwidth=20%] @@ -2065,37 +1221,6 @@ image::img/components-table-pinstripe-edit.png[Picker table cell during edit,sca To line wrap table cells you can override the `createCell` method and return a https://www.codenameone.com/javadoc/com/codename1/ui/TextArea.html[TextArea] instead of a https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html[TextField] since the `TextArea` defaults to the multi-line behavior this should work seamlessly. For example: -[source,java] ----- -Form hi = new Form("Table", new BorderLayout()); -TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] { - {"Row 1", "Row A", "Row X"}, - {"Row 2", "Row B can now stretch very long line that should span multiple rows as much as possible", "Row Y"}, - {"Row 3", "Row C", "Row Z"}, - {"Row 4", "Row D", "Row K"}, - }) { - public boolean isCellEditable(int row, int col) { - return col != 0; - } - }; -Table table = new Table(model) { - @Override - protected Component createCell(Object value, int row, int column, boolean editable) { - TextArea ta = new TextArea((String)value); - ta.setUIID("TableCell"); - return ta; - } - - @Override - protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { - TableLayout.Constraint con = super.createCellConstraint(value, row, column); - con.setWidthPercentage(33); - return con; - } -}; -hi.add(BorderLayout.CENTER, table); -hi.show(); ----- TIP: Notice that you don't need to do anything else as binding to the `TextArea` is built-in to the `Table`. @@ -2111,21 +1236,6 @@ image::img/components-table-multiline-landscape.png[Multiline table cell in land Sorting tables by clicking the titles is something that should work out of the box by using an API like `setSortSupported(true)`: -[source,java] ----- -Form hi = new Form("Table", new BorderLayout()); -TableModel model = new DefaultTableModel(new String[] {"Col 1", "Col 2", "Col 3"}, new Object[][] { - {"Row 1", "Row A", 1}, - {"Row 2", "Row B", 4}, - {"Row 3", "Row C", 7.5}, - {"Row 4", "Row D", 2.24}, - }); -Table table = new Table(model); -table.setSortSupported(true); -hi.add(BorderLayout.CENTER, table); -hi.add(NORTH, new Button("Button")); -hi.show(); ----- Notice this works with numbers, Strings and might work with dates but you can support any object type by overriding the method `protected Comparator createColumnSortComparator(int column)` which should return a comparator for your custom object type in the column. @@ -2137,45 +1247,6 @@ Like the `Table` it works in consort with a model to construct its user interfac The data of the `Tree` arrives from a model for example: this: -[source,java] ----- -class StringArrayTreeModel implements TreeModel { - String[][] arr = new String[][] { - {"Colors", "Letters", "Numbers"}, - {"Red", "Green", "Blue"}, - {"A", "B", "C"}, - {"1", "2", "3"} - }; - - public Vector getChildren(Object parent) { - if(parent == null) { - Vector v = new Vector(); - for(int iter = 0 ; iter < arr[0].length ; iter++) { - v.addElement(arr[0][iter]); - } - return v; - } - Vector v = new Vector(); - for(int iter = 0 ; iter < arr[0].length ; iter++) { - if(parent == arr[0][iter]) { - if(arr.length > iter + 1 && arr[iter + 1] != null) { - for(int i = 0 ; i < arr[iter + 1].length ; i++) { - v.addElement(arr[iter + 1][i]); - } - } - } - } - return v; - } - - public boolean isLeaf(Object node) { - Vector v = getChildren(node); - return v == null || v.size() == 0; - } -} - -Tree dt = new Tree(new StringArrayTreeModel()); ----- Will result in this: @@ -2186,26 +1257,6 @@ NOTE: Since `Tree` is hierarchy based you can't have a simple model like you've A more practical "real world" example would be working with XML data. You can use something like this to show an XML `Tree`: -[source,java] ----- -Form hi = new Form("XML Tree", new BorderLayout()); -InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/build.xml"); -try(Reader r = new InputStreamReader(is, "UTF-8");) { - Element e = new XMLParser().parse(r); - Tree xmlTree = new Tree(new XMLTreeModel(e)) { - @Override - protected String childToDisplayLabel(Object child) { - if(child instanceof Element) { - return ((Element)child).getTagName(); - } - return child.toString(); - } - }; - hi.add(BorderLayout.CENTER, xmlTree); -} catch(IOException err) { - Log.e(err); -} ----- NOTE: The `try(Stream)` syntax is a try with resources clogic that implicitly closes the stream. @@ -2214,34 +1265,6 @@ image::img/components-tree-xml.png[XML Tree,scaledwidth=20%] The model for the XML hierarchy is implemented as such: -[source,java] ----- -class XMLTreeModel implements TreeModel { - private Element root; - public XMLTreeModel(Element e) { - root = e; - } - - public Vector getChildren(Object parent) { - if(parent == null) { - Vector c = new Vector(); - c.addElement(root); - return c; - } - Vector result = new Vector(); - Element e = (Element)parent; - for(int iter = 0 ; iter < e.getNumChildren() ; iter++) { - result.addElement(e.getChildAt(iter)); - } - return result; - } - - public boolean isLeaf(Object node) { - Element e = (Element)node; - return e.getNumChildren() == 0; - } -} ----- [[sharebutton-section]] === ShareButton @@ -2256,23 +1279,7 @@ In the sample code below you take a screenshot which is saved to https://www.cod [source,java] ---- -Form hi = new Form("ShareButton"); -ShareButton sb = new ShareButton(); -sb.setText("Share Screenshot"); -hi.add(sb); - -Image screenshot = Image.createImage(hi.getWidth(), hi.getHeight()); -hi.revalidate(); -hi.setVisible(true); -hi.paintComponent(screenshot.getGraphics(), true); - -String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png"; -try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile);) { - ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1); -} catch(IOException err) { - Log.e(err); -} -sb.setImageToShare(imageFile, "image/png"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava076Snippet.java[tag=the-components-of-codename-one-java-076,indent=0] ---- .The share button running on the simulator @@ -2298,28 +1305,11 @@ The listener is always invoked on the EDT, exactly once per share request. [source,java] ---- -ShareButton sb = new ShareButton(); -sb.setTextToShare("Check this out!"); -sb.setShareResultListener(result -> { - if (result.isSharedTo()) { - Log.p("Shared to " + result.getPackageName()); - } else if (result.isDismissed()) { - Log.p("User dismissed the share sheet"); - } else if (result.isFailed()) { - Log.p("Share failed: " + result.getError()); - } -}); -form.add(sb); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava077Snippet.java[tag=the-components-of-codename-one-java-077,indent=0] ---- If you call `Display` directly instead of using `ShareButton`, pass the listener as the final argument to the new overload: -[source,java] ----- -Display.getInstance().share( - "Check this out!", imagePath, "image/png", sourceRect, - result -> handleResult(result)); ----- When the share is dispatched through the non-native fallback dialog (platforms without `isNativeShareSupported`), the listener still fires: it reports `DISMISSED` when the user picks the Cancel button, and the underlying `ShareService` implementations report `SHARED_TO` once they call `ShareService.finish()`. Custom `ShareService` subclasses can call `deliverResult(ShareResult.failed("..."))` from inside `share(...)` to publish a failure explicitly; otherwise `finish()` falls back to a default `SHARED_TO(commandName)`. @@ -2343,20 +1333,6 @@ For a single call the helper writes four files at the root of the extension bund Call the builder from a Maven plugin, an Ant task or a one-shot `main`: -[source,java] ----- -import com.codename1.util.IOSShareExtensionBuilder; - -new IOSShareExtensionBuilder() - .setExtensionName("MyShareExtension") - .setDisplayName("Share to MyApp") - .setHostBundleId("com.example.myapp") - .setAppGroupId("group.com.example.myapp.shared") - .acceptText(true) - .acceptURLs(true) - .acceptImages(true) - .writeAppext(new File("src/main/resources/MyShareExtension.ios.appext")); ----- The next iOS build picks up the `.ios.appext` archive automatically; no Xcode steps are required. @@ -2368,10 +1344,7 @@ The host app reads the most recent payload from the same App Group at startup or [source,objective-c] ---- -// Inside an iOS native interface -NSUserDefaults* shared = - [[NSUserDefaults alloc] initWithSuiteName:@"group.com.example.myapp.shared"]; -NSDictionary* payload = [shared dictionaryForKey:@"cn1.shareExtension.payload"]; +include::../demos/ios/src/main/objectivec/TheComponentsOfCodenameOneSnippets.m[tag=the-components-of-codename-one-objective-c-001,indent=0] ---- The payload dictionary contains: @@ -2406,17 +1379,7 @@ Since `Tabs` are a `Container` its a common mistake to try and add a `Tab` using [source,java] ---- -Form hi = new Form("Tabs", new BorderLayout()); - -Tabs t = new Tabs(); -Style s = UIManager.getInstance().getComponentStyle("Tab"); -FontImage icon1 = FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, s); - -Container container1 = BoxLayout.encloseY(new Label("Label1"), new Label("Label2")); -t.addTab("Tab1", icon1, container1); -t.addTab("Tab2", new SpanLabel("Some text directly in the tab")); - -hi.add(BorderLayout.CENTER, t); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava080Snippet.java[tag=the-components-of-codename-one-java-080,indent=0] ---- .Simple usage of Tabs @@ -2426,45 +1389,7 @@ A common usage for `Tabs` is the swipe to proceed effect which is common in iOS [source,java] ---- -Form hi = new Form("Swipe Tabs", new LayeredLayout()); -Tabs t = new Tabs(); -t.hideTabs(); - -Style s = UIManager.getInstance().getComponentStyle("Button"); -FontImage radioEmptyImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, s); -FontImage radioFullImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, s); -((DefaultLookAndFeel)UIManager.getInstance().getLookAndFeel()).setRadioButtonImages(radioFullImage, radioEmptyImage, radioFullImage, radioEmptyImage); - -Container container1 = BoxLayout.encloseY(new Label("Swipe the tab to see more"), - new Label("You can put anything here")); -t.addTab("Tab1", container1); -t.addTab("Tab2", new SpanLabel("Some text directly in the tab")); - -RadioButton firstTab = new RadioButton(""); -RadioButton secondTab = new RadioButton(""); -firstTab.setUIID("Container"); -secondTab.setUIID("Container"); -new ButtonGroup(firstTab, secondTab); -firstTab.setSelected(true); -Container tabsFlow = FlowLayout.encloseCenter(firstTab, secondTab); - -hi.add(t); -hi.add(BorderLayout.south(tabsFlow)); - -t.addSelectionListener((i1, i2) -> { - switch(i2) { - case 0: - if(!firstTab.isSelected()) { - firstTab.setSelected(true); - } - break; - case 1: - if(!secondTab.isSelected()) { - secondTab.setSelected(true); - } - break; - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava081Snippet.java[tag=the-components-of-codename-one-java-081,indent=0] ---- .Swipeable Tabs with an iOS carousel effect page 1 @@ -2488,30 +1413,15 @@ To enable in a custom theme: [source,css] ---- -#Constants { - tabsAnimatedIndicatorBool: true; - tabsAnimatedIndicatorDurationInt: 200; /* tween duration in ms */ - tabsAnimatedIndicatorThicknessMm: 1; /* underline thickness */ -} - -TabIndicator { - /* The indicator picks up its color from this UIID's fg. If the - UIID isn't defined or has fgColor == 0, the indicator falls - back to the currently-selected tab's fgColor. */ - color: #007aff; - background-color: transparent; - padding: 0; - margin: 0; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=the-components-of-codename-one-css-001,indent=0] ---- -To toggle programmatically (e.g. add it to a Tabs instance whose theme +To toggle programmatically (for example, add it to a Tabs instance whose theme hasn't enabled it): [source,java] ---- -Tabs tabs = new Tabs(); -tabs.setAnimatedIndicator(true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava082Snippet.java[tag=the-components-of-codename-one-java-082,indent=0] ---- The indicator animates its `x` / `width` from the previously selected @@ -2521,6 +1431,39 @@ spec). Rapid double-taps start the animation from the *current interpolated position* rather than from a stale baseline, so the indicator chains cleanly. +[[mediamanager-section]] +=== MediaManager & MediaPlayer + +// HTML_ONLY_START +IMPORTANT: `MediaPlayer` is a *peer component*, understanding this is crucial if your application depends on such a component. You can learn about peer components and their issues https://www.codenameone.com/manual/advanced-topics.html#native-peer-components[here]. +// HTML_ONLY_END +//// +//PDF_ONLY +IMPORTANT: `MediaPlayer` is a *peer component*, understanding this is crucial if your application depends on such a component. You can learn about peer components and their issues <>. +//// + +The https://www.codenameone.com/javadoc/com/codename1/components/MediaPlayer.html[MediaPlayer] allows you to control video playback. To use the `MediaPlayer` you need to first load the `Media` object from the https://www.codenameone.com/javadoc/com/codename1/media/MediaManager.html[MediaManager]. + +The `MediaManager` is the core class responsible for media interaction in Codename One. + +TIP: You should also check out the https://www.codenameone.com/javadoc/com/codename1/capture/Capture.html[Capture] class for things that aren't covered by the `MediaManager`. + +In the demo code below you use the gallery functionality to pick a video from the device's video gallery: + +[source,java] +---- +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava083Snippet.java[tag=the-components-of-codename-one-java-083,indent=0] +---- + +.Video playback running on the simulator +image::img/components-mediaplayer.png[Video playback running on the simulator,scaledwidth=25%] + + +.Video playback running on an Android device. Notice the native playback controls that appear when the video is tapped +image::img/components-mediaplayer-android.png[Video playback running on an Android device. Notice the native playback controls that appear when the video is tapped,scaledwidth=25%] + +IMPORTANT: Video playback in the simulator will work with JavaFX enabled. This is the default for Java 8 or newer so recommend using that. + === ImageViewer The https://www.codenameone.com/javadoc/com/codename1/components/ImageViewer.html[ImageViewer] allows you to inspect, zoom and pan into an image. It also allows swiping between images if @@ -2530,12 +1473,6 @@ IMPORTANT: The `ImageViewer` is a complex rich component designed for user inter You can use the `ImageViewer` as a tool to view a single image which allows you to zoom in/out to that image as such: -[source,java] ----- -Form hi = new Form("ImageViewer", new BorderLayout()); -ImageViewer iv = new ImageViewer(duke); -hi.add(BorderLayout.CENTER, iv); ----- TIP: You can simulate pinch to zoom on the simulator by dragging the right button away from the top left corner to zoom in and towards the top left corner to zoom out. On Mac touchpads you can drag two fingers to achieve that. @@ -2550,16 +1487,7 @@ You can work with a list of images to produce a swiping effect for the image vie [source,java] ---- -Form hi = new Form("ImageViewer", new BorderLayout()); - -Image red = Image.createImage(100, 100, 0xffff0000); -Image green = Image.createImage(100, 100, 0xff00ff00); -Image blue = Image.createImage(100, 100, 0xff0000ff); -Image gray = Image.createImage(100, 100, 0xffcccccc); - -ImageViewer iv = new ImageViewer(red); -iv.setImageList(new DefaultListModel<>(red, green, blue, gray)); -hi.add(BorderLayout.CENTER, iv); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava085Snippet.java[tag=the-components-of-codename-one-java-085,indent=0] ---- .An ImageViewer with many elements is indistinguishable from a single ImageViewer except for swipe @@ -2569,14 +1497,6 @@ Notice that you use a https://www.codenameone.com/javadoc/com/codename1/ui/list/ `ImageViewer` also supports optional side arrows (material font icons) and an optional thumbnail strip for direct image navigation: -[source,java] ----- -ImageViewer iv = new ImageViewer(red); -iv.setImageList(new DefaultListModel<>(red, green, blue, gray)); -iv.setNavigationArrowsVisible(true); -iv.setThumbnailsVisible(true); -iv.setThumbnailBarHeight(6f); // Optional, defaults to 6mm ----- When arrows are enabled, tapping near the left/right edge will move to the previous/next image. When thumbnails are enabled, tapping a thumbnail jumps directly to that image. @@ -2591,83 +1511,6 @@ TIP: `EncodedImage's` aren't always fully loaded and so when you swipe if the im You can dynamically download images directly into the `ImageViewer` with a custom list model like this: -[source,java] ----- -Form hi = new Form("ImageViewer", new BorderLayout()); -final EncodedImage placeholder = EncodedImage.createFromImage( - FontImage.createMaterial(FontImage.MATERIAL_SYNC, s). - scaled(300, 300), false); - -class ImageList implements ListModel { - private int selection; - private String[] imageURLs = { - "http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/300px-AGameOfThrones.jpg", - "http://awoiaf.westeros.org/images/thumb/3/39/AClashOfKings.jpg/300px-AClashOfKings.jpg", - "http://awoiaf.westeros.org/images/thumb/2/24/AStormOfSwords.jpg/300px-AStormOfSwords.jpg", - "http://awoiaf.westeros.org/images/thumb/a/a3/AFeastForCrows.jpg/300px-AFeastForCrows.jpg", - "http://awoiaf.westeros.org/images/7/79/ADanceWithDragons.jpg" - }; - private Image[] images; - private EventDispatcher listeners = new EventDispatcher(); - - public ImageList() { - this.images = new EncodedImage[imageURLs.length]; - } - - public Image getItemAt(final int index) { - if(images[index] == null) { - images[index] = placeholder; - Util.downloadUrlToStorageInBackground(imageURLs[index], "list" + index, (e) -> { - try { - images[index] = EncodedImage.create(Storage.getInstance().createInputStream("list" + index)); - listeners.fireDataChangeEvent(index, DataChangedListener.CHANGED); - } catch(IOException err) { - err.printStackTrace(); - } - }); - } - return images[index]; - } - - public int getSize() { - return imageURLs.length; - } - - public int getSelectedIndex() { - return selection; - } - - public void setSelectedIndex(int index) { - selection = index; - } - - public void addDataChangedListener(DataChangedListener l) { - listeners.addListener(l); - } - - public void removeDataChangedListener(DataChangedListener l) { - listeners.removeListener(l); - } - - public void addSelectionListener(SelectionListener l) { - } - - public void removeSelectionListener(SelectionListener l) { - } - - public void addItem(Image item) { - } - - public void removeItem(int index) { - } -}; - -ImageList imodel = new ImageList(); - -ImageViewer iv = new ImageViewer(imodel.getItemAt(0)); -iv.setImageList(imodel); -hi.add(BorderLayout.CENTER, iv); ----- .Dynamically fetching an image URL from the internet footnote:[Image was fetched from http://awoiaf.westeros.org/index.php/Portal:Books] image::img/components-imageviewer-dynamic.png[Dynamically fetching an image URL from the internet,scaledwidth=20%] @@ -2685,23 +1528,6 @@ You can use `ScaleImageLabel`/`ScaleImageButton` interchangeably. The major diff Here is a simple example that also shows the difference between the `scale to fill` and `scale to fit` modes: -[source,java] ----- -TableLayout tl = new TableLayout(2, 2); -Form hi = new Form("ScaleImageButton/Label", tl); -Style s = UIManager.getInstance().getComponentStyle("Button"); -Image icon = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s); -ScaleImageLabel fillLabel = new ScaleImageLabel(icon); -fillLabel.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); -ScaleImageButton fillButton = new ScaleImageButton(icon); -fillButton.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); -hi.add(tl.createConstraint().widthPercentage(20), new ScaleImageButton(icon)). - add(tl.createConstraint().widthPercentage(80), new ScaleImageLabel(icon)). - add(fillLabel). - add(fillButton); -hi.show(); ----- - .ScaleImageLabel/Button, the top row includes scale to fit versions (the default) whereas the bottom row includes the scale to fill versions image::img/components-scaleimage.png[ScaleImageLabel/Button the top row includes scale to fit versions (the default) whereas the bottom row includes the scale to fill versions,scaledwidth=25%] @@ -2732,19 +1558,6 @@ The basic functionality of the `Toolbar` includes the ability to add a command t The code below provides a brief overview of these options: -[source,java] ----- -Toolbar.setGlobalToolbar(true); - -Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); -hi.getToolbar().addCommandToLeftBar("Left", icon, (e) -> Log.p("Clicked")); -hi.getToolbar().addCommandToRightBar("Right", icon, (e) -> Log.p("Clicked")); -hi.getToolbar().addCommandToOverflowMenu("Overflow", icon, (e) -> Log.p("Clicked")); -hi.getToolbar().addCommandToSideMenu("Sidemenu", icon, (e) -> Log.p("Clicked")); -hi.show(); ----- - - .The Toolbar image::img/components-toolbar.png[The Toolbar,scaledwidth=25%] @@ -2766,55 +1579,7 @@ The customization of the title area allows for some pretty powerful UI effects f [[Advanced-search-code]] [source,java] ---- -Toolbar.setGlobalToolbar(true); -Style s = UIManager.getInstance().getComponentStyle("Title"); - -Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); -TextField searchField = new TextField("", "Toolbar Search"); <1> -searchField.getHintLabel().setUIID("Title"); -searchField.setUIID("Title"); -searchField.getAllStyles().setAlignment(Component.LEFT); -hi.getToolbar().setTitleComponent(searchField); -FontImage searchIcon = FontImage.createMaterial(FontImage.MATERIAL_SEARCH, s); -searchField.addDataChangeListener((i1, i2) -> { <2> - String t = searchField.getText(); - if(t.length() < 1) { - for(Component cmp : hi.getContentPane()) { - cmp.setHidden(false); - cmp.setVisible(true); - } - } else { - t = t.toLowerCase(); - for(Component cmp : hi.getContentPane()) { - String val = null; - if(cmp instanceof Label) { - val = ((Label)cmp).getText(); - } else { - if(cmp instanceof TextArea) { - val = ((TextArea)cmp).getText(); - } else { - val = (String)cmp.getPropertyValue("text"); - } - } - boolean show = val != null && val.toLowerCase().indexOf(t) > -1; - cmp.setHidden(!show); <3> - cmp.setVisible(show); - } - } - hi.getContentPane().animateLayout(250); -}); -hi.getToolbar().addCommandToRightBar("", searchIcon, (e) -> { - searchField.startEditingAsync(); <4> -}); - -hi.add("A Game of Thrones"). - add("A Clash Of Kings"). - add("A Storm Of Swords"). - add("A Feast For Crows"). - add("A Dance With Dragons"). - add("The Winds of Winter"). - add("A Dream of Spring"); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava090Snippet.java[tag=the-components-of-codename-one-java-090,indent=0] ---- <1> You use a `TextField` the whole time and style it to make it (and its hint) look like a regular title. An alternative way is to replace the title component dynamically. @@ -2844,66 +1609,6 @@ You can customize the appearance of the search bar by using the UIID's: `Toolbar In the sample below you fetch all the contacts from the device and enable search through them, notice it expects and image called `duke.png` which is the default Codename One icon renamed and placed in the src folder: -[source,java] ----- -Image duke = null; -try { - duke = Image.createImage("/duke.png"); -} catch(IOException err) { - Log.e(err); -} -int fiveMM = Display.getInstance().convertToPixels(5); -final Image finalDuke = duke.scaledWidth(fiveMM); -Toolbar.setGlobalToolbar(true); -Form hi = new Form("Search", BoxLayout.y()); -hi.add(new InfiniteProgress()); -Display.getInstance().scheduleBackgroundTask(()-> { - // this will take a while... - Contact[] cnts = Display.getInstance().getAllContacts(true, true, true, true, false, false); - Display.getInstance().callSerially(() -> { - hi.removeAll(); - for(Contact c : cnts) { - MultiButton m = new MultiButton(); - m.setTextLine1(c.getDisplayName()); - m.setTextLine2(c.getPrimaryPhoneNumber()); - Image pic = c.getPhoto(); - if(pic != null) { - m.setIcon(fill(pic, finalDuke.getWidth(), finalDuke.getHeight())); - } else { - m.setIcon(finalDuke); - } - hi.add(m); - } - hi.revalidate(); - }); -}); - -hi.getToolbar().addSearchCommand(e -> { - String text = (String)e.getSource(); - if(text == null || text.length() == 0) { - // clear search - for(Component cmp : hi.getContentPane()) { - cmp.setHidden(false); - cmp.setVisible(true); - } - hi.getContentPane().animateLayout(150); - } else { - text = text.toLowerCase(); - for(Component cmp : hi.getContentPane()) { - MultiButton mb = (MultiButton)cmp; - String line1 = mb.getTextLine1(); - String line2 = mb.getTextLine2(); - boolean show = line1 != null && line1.toLowerCase().indexOf(text) > -1 || - line2 != null && line2.toLowerCase().indexOf(text) > -1; - mb.setHidden(!show); - mb.setVisible(show); - } - hi.getContentPane().animateLayout(150); - } -}, 4); - -hi.show(); ----- ==== South Component @@ -2911,10 +1616,6 @@ A common feature in side menu bar is the ability to add a component to the "sout Notice that this feature works with the on-top and permanent versions of the side menu and not with the legacy versions: -[source,java] ----- -toolbar.setComponentToSideMenuSouth(myComponent); ----- This places the component below the side menu bar. Notice that this component controls its entire UIID & is separate from the `SideNavigationPanel` UIID so if you set that component you might want to place it within a container that has the `SideNavigationPanel` UIID so it will blend with the rest of the UI. @@ -2928,29 +1629,7 @@ The code below shows off an attractive title based on a book by GRRM on top of t [source,java] ---- -Toolbar.setGlobalToolbar(true); - -Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); -EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(hi.getWidth(), hi.getWidth() / 5, 0xffff0000), true); -URLImage background = URLImage.createToStorage(placeholder, "400px-AGameOfThrones.jpg", - "http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/400px-AGameOfThrones.jpg"); -background.fetch(); -Style stitle = hi.getToolbar().getTitleComponent().getUnselectedStyle(); -stitle.setBgImage(background); -stitle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); -stitle.setPaddingUnit(Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS); -stitle.setPaddingTop(15); -SpanButton credit = new SpanButton("This excerpt is from A Wiki Of Ice And Fire. Please check it out by clicking here!"); -credit.addActionListener((e) -> Display.getInstance().execute("http://awoiaf.westeros.org/index.php/A_Game_of_Thrones")); -hi.add(new SpanLabel("A Game of Thrones is the first of seven planned novels in A Song of Ice and Fire, an epic fantasy series by American author George R. R. Martin. It was first published on 6 August 1996. The novel was nominated for the 1998 Nebula Award and the 1997 World Fantasy Award,[1] and won the 1997 Locus Award.[2] The novella Blood of the Dragon, comprising the Daenerys Targaryen chapters from the novel, won the 1997 Hugo Award for Best Novella. ")). - add(new Label("Plot introduction", "Heading")). - add(new SpanLabel("A Game of Thrones is set in the Seven Kingdoms of Westeros, a land reminiscent of Medieval Europe. In Westeros the seasons last for years, sometimes decades, at a time.\n\n" + - "Fifteen years prior to the novel, the Seven Kingdoms were torn apart by a civil war, known alternately as \"Robert's Rebellion\" and the \"War of the Usurper.\" Prince Rhaegar Targaryen kidnapped Lyanna Stark, arousing the ire of her family and of her betrothed, Lord Robert Baratheon (the war's titular rebel). The Mad King, Aerys II Targaryen, had Lyanna's father and eldest brother executed when they demanded her safe return. Her second brother, Eddard, joined his boyhood friend Robert Baratheon and Jon Arryn, with whom they had been fostered as children, in declaring war against the ruling Targaryen dynasty, securing the allegiances of House Tully and House Arryn through a network of dynastic marriages (Lord Eddard to Catelyn Tully and Lord Arryn to Lysa Tully). The powerful House Tyrell continued to support the King, but House Lannister and House Martell both stalled due to insults against their houses by the Targaryens. The civil war climaxed with the Battle of the Trident, when Prince Rhaegar was killed in battle by Robert Baratheon. The Lannisters finally agreed to support King Aerys, but then brutally... ")). - add(credit); - -ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200); -hi.getAnimationManager().onTitleScrollAnimation(title); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava093Snippet.java[tag=the-components-of-codename-one-java-093,indent=0] ---- .The Toolbar starts with the large URLImage fetched from the web @@ -2966,8 +1645,7 @@ Most the code above creates the "look" of the application. The key piece of code [source,java] ---- -ComponentAnimation title = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200); -hi.getAnimationManager().onTitleScrollAnimation(title); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava094Snippet.java[tag=the-components-of-codename-one-java-094,indent=0] ---- In the first line you create a style animation that will translate the style from the current settings to the destination UIID (the first argument) within 200 pixels of scrolling. You then bind this animation to the title scrolling animation event. @@ -2990,10 +1668,7 @@ The `BrowserComponent` can point at an arbitrary URL to load it: [source,java] ---- -Form hi = new Form("Browser", new BorderLayout()); -BrowserComponent browser = new BrowserComponent(); -browser.setURL("https://www.codenameone.com/"); -hi.add(BorderLayout.CENTER, browser); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava095Snippet.java[tag=the-components-of-codename-one-java-095,indent=0] ---- .Browser Component showing the Codename One website on the simulator @@ -3009,8 +1684,7 @@ The `BrowserComponent` has full support for executing local web pages from withi [source,java] ---- -BrowserComponent wb = new BrowserComponent(); -wb.setURL("jar:///Page.html"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava096Snippet.java[tag=the-components-of-codename-one-java-096,indent=0] ---- IMPORTANT: On Android a native indicator might show up when the web page is loading. This can be disabled using the `Display.getInstance().setProperty("WebLoadingHidden", "true");` call. You need to invoke this once. @@ -3032,10 +1706,7 @@ The fix is to launch the IDE (and therefore the Simulator JVM) with `JAVA_HOME` [source,bash] ---- -#!/bin/bash -export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 -export LD_LIBRARY_PATH=$JAVA_HOME/lib:$JAVA_HOME/lib/server${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} -exec /path/to/your/ide "$@" +include::../demos/common/src/main/snippets/developer-guide/the-components-of-codename-one.sh[tag=the-components-of-codename-one-bash-001,indent=0] ---- After relaunching the IDE through this wrapper, `BrowserComponent` instantiation succeeds in the Simulator. Any JDK from 11 through 25 that's supported for running the Simulator works -- the important part is that `LD_LIBRARY_PATH` resolves to that JDK's `lib` and `lib/server` directories so the bundled `libjcef.so` finds a compatible `libjawt`/`libjvm` pair. @@ -3058,11 +1729,7 @@ For web developers this isn't enough since hierarchies are used often to represe [source,java] ---- -try { - browserComponent.setURLHierarchy("/htmlFile.html"); -} catch(IOException err) { - ... -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava097Snippet.java[tag=the-components-of-codename-one-java-097,indent=0] ---- Notice that the path is relative to the HTML directory and starts with `/` but inside the HTML files you should use @@ -3083,29 +1750,7 @@ The `shouldNavigate` indicates to the native code whether navigation should proc [source,java] ---- -Form hi = new Form("BrowserComponent", new BorderLayout()); -BrowserComponent bc = new BrowserComponent(); -bc.setPage( "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "

Demo

\n" + - " \n" + - "", null); -hi.add(BorderLayout.CENTER, bc); -bc.setBrowserNavigationCallback((url) -> { - if(url.startsWith("http://click")) { - Display.getInstance().callSerially(() -> bc.execute("fnc('

You clicked!

')")); - return false; - } - return true; -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava098Snippet.java[tag=the-components-of-codename-one-java-098,indent=0] ---- @@ -3128,10 +1773,6 @@ Codename One 4.0 introduced a new API for interacting with JavaScript in Codenam The old API provided a synchronous wrapper around an inherently asynchronous process, and made extensive use of `invokeAndBlock()` underneath the covers. This resulted in a nice API with high-level abstractions that played with a synchronous programming model, but it came with a price-tag in performance, complexity, and predictability. Let’s take a simple example, getting a reference to the "window" object: -[source,java] ----- -JSObject window = ctx.get("window"); ----- This code looks harmless enough, but this is actually expensive. It issues a command to the `BrowserComponent`, and uses `invokeAndBlock()` to wait for the command to go through and send back a response. `invokeAndBlock()` is a magical tool that allows you to "block" without blocking the EDT, but it has its costs, and shouldn’t be overused. Most of the Codename One APIs that use `invokeAndBlock()` show this in their name. For example: `Component.animateLayoutAndWait()`. This provides you the expectation that this call could take some time, and helps to alert you to the underlying cost. @@ -3143,20 +1784,9 @@ The new API fully embraces the asynchronous nature of JavaScript. It uses callba NOTE: In all the sample code below, you can assume that variables named `bc` represent an instance of https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html[BrowserComponent]: -[source,java] ----- -bc.execute( - "callback.onSuccess(3+4)", - res -> Log.p("The result was "+res.getInt()) -); ----- This code should output "The result was 7" to the console. It's fully asynchronous, so you can include this code anywhere without worrying about it "bogging down" your code. The full signature of this form of the https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html#execute-java.lang.String-com.codename1.util.SuccessCallback-[execute()] method is: -[source,java] ----- -public void execute(String js, SuccessCallback callback) ----- The first parameter is a JavaScript expression. This JavaScript *MUST* call either `callback.onSuccess(result)` or `callback.onError(message, errCode)` at some point in order for your callback to be called. @@ -3170,11 +1800,6 @@ As mentioned before, the new API also provides an `executeAndWait()` wrapper for For example: -[source, java] ----- -JSRef res = bc.executeAndWait("callback.onSuccess(3+4)"); -Log.p("The result was "+res.Int()); ----- Prints `The result was 7`. @@ -3184,13 +1809,6 @@ IMPORTANT: When using the `andWait()` variant, it's critical that your JavaScrip The callbacks you pass to `execute()` and `executeAndWait()` are single-use callbacks. You can’t, for example, store the `callback` variable on the JavaScript side for later use (for example: to respond to a button click event). If you need a "multi-use" callback, you should use the `addJSCallback()` method instead. Its usage looks identical to `execute()`, the difference is that the callback will live on after its first use. For example: Consider the following code: -[source,java] ----- -bc.execute( - "$('#somebutton').click(function(){callback.onSuccess('Button was clicked')})", - res -> Log.p(res.toString()) -); ----- The above example, assumes that jQuery is loaded in the webpage that you're interacting with, and you're adding a click handler to a button with ID "somebutton." The click handler calls your callback. @@ -3198,13 +1816,6 @@ If you run this example, the first time the button is clicked, you’ll see "But You need to change this code to use the `addJSCallback()` method as follows: -[source,java] ----- -bc.addJSCallback( - "$('#somebutton').click(function(){callback.onSuccess('Button was clicked')})", - res -> Log.p(res.toString()) -); ----- Now it will work no matter how many times the button is clicked. @@ -3214,16 +1825,6 @@ Often, the JavaScript expressions that you execute will include parameters from For example, suppose you want to pass a string with text to set in a textarea within the webpage. You can do something like: -[source,java] ----- -bc.execute( - "jQuery('#bio').text(${0}); jQuery('#age').text(${1})", - new Object[]{ - "A multi-line\n string with \"quotes\"", - 27 - } -); ----- The gist is that you embed placeholders in the JavaScript expression that are replaced by the corresponding entry in an array of parameters. The `${0}` placeholder is replaced by the first item in the parameters array, the `${1}` placeholder is replaced by the 2nd, etc. @@ -3233,42 +1834,18 @@ The new API also includes a https://www.codenameone.com/javadoc/com/codename1/ui For example: You might want to create a proxy for the https://developer.mozilla.org/en-You/docs/Web/API/Window/location[window.location] object so that you can access its properties more from Java: -[source,java] ----- -JSProxy location = bc.createJSProxy("window.location"); ----- Then you can retrieve its properties using the `get()` method: -[source,java] ----- -location.get("href", res -> Log.p("location.href="+res)); ----- Or synchronously: -[source,java] ----- -JSRef href = location.getAndWait("href"); -Log.p("location.href="+href); ----- You can also set its properties: -[source,java] ----- -location.set("href", "http://www.google.com"); ----- And call its methods: -[source,java] ----- -location.call("replace", new Object[]{"http://www.google.com"}, - res -> Log.p("Return value was "+res) -); ----- - ===== Legacy JSObject support @@ -3278,27 +1855,7 @@ This section describes the now deprecated `JSObject` approach. It's here for ref [source,java] ---- -Form hi = new Form("BrowserComponent", new BorderLayout()); -BrowserComponent bc = new BrowserComponent(); -bc.setPage( "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "

Demo

\n" + - " \n" + - "", null); -TextField tf = new TextField(); -hi.add(BorderLayout.CENTER, bc). - add(BorderLayout.SOUTH, tf); -bc.addWebEventListener("onLoad", (e) -> bc.execute("fnc('

Hello World

')")); -tf.addActionListener((e) -> bc.execute("fnc('

" + tf.getText() +"

')")); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava111Snippet.java[tag=the-components-of-codename-one-java-111,indent=0] ---- .JavaScript code was invoked to append text into the browser image above @@ -3347,63 +1904,15 @@ NOTE: This conversion table is more verbose than necessary, since JavaScript fun You can access JavaScript variables from the context by using code like this: -[source,java] ----- -Form hi = new Form("BrowserComponent", new BorderLayout()); -BrowserComponent bc = new BrowserComponent(); -bc.setPage( "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - "

This will appear twice...

\n" + - " \n" + - "", null); -hi.add(BorderLayout.CENTER, bc); -bc.addWebEventListener("onLoad", (e) -> { - // Create a JavaScript context for this BrowserComponent - JavascriptContext ctx = new JavascriptContext(bc); - - String pageContent = (String)ctx.get("document.body.innerHTML"); - hi.add(BorderLayout.SOUTH, pageContent); - hi.revalidate(); -}); -hi.show(); ----- .The contents were copied from the DOM and placed in the south position of the form image::img/components-browsercomponent-context.png[The contents were copied from the DOM and placed in the south position of the form,scaledwidth=20%] Notice that when you work with numeric values or anything related to the types mentioned above your code must be aware of the typing. For example: in this case the type is `Double` and not `String`: -[source,java] ----- -Double outerWidth = (Double)ctx.get("window.outerWidth"); ----- You can also query the context for objects and change their value for example: -[source,java] ----- -Form hi = new Form("BrowserComponent", new BorderLayout()); -BrowserComponent bc = new BrowserComponent(); -bc.setPage( "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - "

Please Wait...

\n" + - " \n" + - "", null); -hi.add(BorderLayout.CENTER, bc); -bc.addWebEventListener("onLoad", (e) -> { - // Create a JavaScript context for this BrowserComponent - JavascriptContext ctx = new JavascriptContext(bc); - - JSObject jo = (JSObject)ctx.get("window"); - jo.set("location", "https://www.codenameone.com/"); -}); ----- This code effectively navigates to the Codename One home page by fetching the DOM's window object and setting its `location` property to https://www.codenameone.com/[https://www.codenameone.com/]. @@ -3436,20 +1945,7 @@ The following sample places a `RichTextArea` in a form and wires a couple of for [source,java] ---- -Form hi = new Form("Compose", new BorderLayout()); -RichTextArea editor = new RichTextArea(); -editor.setPlaceholder("Write something..."); -editor.setHtml("

Trip itinerary

Meet at the main lobby.

"); - -Toolbar tb = hi.getToolbar(); -tb.addCommandToRightBar("B", null, e -> editor.bold()); -tb.addCommandToRightBar("I", null, e -> editor.italic()); -tb.addCommandToRightBar("List", null, e -> editor.insertUnorderedList()); -tb.addCommandToRightBar("Save", null, e -> - editor.getHtml(html -> Log.p("User wrote: " + html))); - -hi.add(BorderLayout.CENTER, editor); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava115Snippet.java[tag=the-components-of-codename-one-java-115,indent=0] ---- .RichTextArea with a formatting toolbar editing styled content @@ -3461,11 +1957,6 @@ TIP: To keep a formatting toolbar in sync with the selection (for example, to hi Reading the edited content is asynchronous: -[source,java] ----- -editor.getHtml(html -> storage.save(html)); // full HTML markup -editor.getText(text -> index(text)); // plain text, markup stripped ----- === CodeEditor @@ -3473,14 +1964,7 @@ The https://www.codenameone.com/javadoc/com/codename1/ui/CodeEditor.html[CodeEdi [source,java] ---- -Form hi = new Form("Editor", new BorderLayout()); -CodeEditor editor = new CodeEditor(); -editor.setLanguage("java"); -editor.setTheme("light"); // or "dark" -editor.setShowLineNumbers(true); -editor.setText("public class Main {\n\n}"); -hi.add(BorderLayout.CENTER, editor); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava117Snippet.java[tag=the-components-of-codename-one-java-117,indent=0] ---- The highlighting language is selected with `setLanguage(String)` and understands common languages such as `java`, `kotlin`, `javascript`, `python`, `css`, `xml`, `json` and `c`. The content is exchanged with `setText(String)` and the asynchronous `getText(SuccessCallback)`, and `setReadOnly(boolean)` turns the editor into a syntax-highlighted code viewer. @@ -3492,19 +1976,6 @@ image::img/components-codeeditor.png[CodeEditor showing Java with the completion Code completion is driven by a https://www.codenameone.com/javadoc/com/codename1/ui/CodeCompletionProvider.html[CodeCompletionProvider]. The editor calls the provider as the user types (or when completion is explicitly triggered), passing the full text and the caret offset; the provider returns its proposals asynchronously, which makes it suitable both for fast in-memory completion and for completion backed by a remote language server. -[source,java] ----- -editor.setCompletionProvider((ed, code, cursor, results) -> { - String prefix = currentWord(code, cursor); - List out = new ArrayList<>(); - for (String member : new String[] {"println(", "print(", "printf(", "flush()"}) { - if (member.startsWith(prefix)) { - out.add(new CodeCompletion(member).setType("method")); - } - } - results.onSucess(out); // an empty list hides the popup -}); ----- Each https://www.codenameone.com/javadoc/com/codename1/ui/CodeCompletion.html[CodeCompletion] carries the text shown in the popup, the text inserted when it's chosen (which may differ), an optional category used to badge the entry (`"method"`, `"keyword"`, `"class"`, ...) and an optional detail string such as a signature. @@ -3525,10 +1996,7 @@ For example: This is a trivial use case that can work well for smaller sample si [source,java] ---- -Form hi = new Form("Auto Complete", new BoxLayout(BoxLayout.Y_AXIS)); -AutoCompleteTextField ac = new AutoCompleteTextField("Short", "Shock", "Sholder", "Shrek"); -ac.setMinimumElementsShownInPopup(5); -hi.add(ac); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava119Snippet.java[tag=the-components-of-codename-one-java-119,indent=0] ---- .Autocomplete Text Field @@ -3537,60 +2005,6 @@ image::img/components-autocomplete.png[Autocomplete Text Field,scaledwidth=30%] For example, if you wish to query a database or a web service you will need to derive the class and perform more advanced filtering by overriding the `filter` method: -[source,java] ----- -public void showForm() { - final DefaultListModel options = new DefaultListModel<>(); - AutoCompleteTextField ac = new AutoCompleteTextField(options) { - @Override - protected boolean filter(String text) { - if(text.length() == 0) { - return false; - } - String[] l = searchLocations(text); - if(l == null || l.length == 0) { - return false; - } - - options.removeAll(); - for(String s : l) { - options.addItem(s); - } - return true; - } - - }; - ac.setMinimumElementsShownInPopup(5); - hi.add(ac); - hi.add(new SpanLabel("This demo requires a valid google API key to be set below " - + "you can get this key for the webservice (not the native key) by following the instructions here: " - + "https://developers.google.com/places/web-service/get-api-key")); - hi.add(apiKey); - hi.getToolbar().addCommandToRightBar("Get Key", null, e -> Display.getInstance().execute("https://developers.google.com/places/web-service/get-api-key")); - hi.show(); -} - -TextField apiKey = new TextField(); - -String[] searchLocations(String text) { - try { - if(text.length() > 0) { - ConnectionRequest r = new ConnectionRequest(); - r.setPost(false); - r.setUrl("https://maps.googleapis.com/maps/api/place/autocomplete/json"); - r.addArgument("key", apiKey.getText()); - r.addArgument("input", text); - NetworkManager.getInstance().addToQueueAndWait(r); - Map result = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(r.getResponseData()), "UTF-8")); - String[] res = Result.fromContent(result).getAsStringArray("//description"); - return res; - } - } catch(Exception err) { - Log.e(err); - } - return null; -} ----- .Autocomplete Text Field with a webservice image::img/dynamic-autocomplete.png[Autocomplete Text Field with a webservice,scaledwidth=20%] @@ -3608,59 +2022,7 @@ The following source code presents an autocomplete text field with images in the [source,java] ---- -final String[] characters = { "Tyrion Lannister", "Jaime Lannister", "Cersei Lannister", "Daenerys Targaryen", - "Jon Snow", "Petyr Baelish", "Jorah Mormont", "Sansa Stark", "Arya Stark", "Theon Greyjoy" - // snipped the rest for clarity -}; - -Form current = new Form("AutoComplete", BoxLayout.y()); - -AutoCompleteTextField ac = new AutoCompleteTextField(characters); - -final int size = Display.getInstance().convertToPixels(7); -final EncodedImage placeholder = EncodedImage.createFromImage(Image.createImage(size, size, 0xffcccccc), true); - -final String[] actors = { "Peter Dinklage", "Nikolaj Coster-Waldau", "Lena Headey"}; // <1> -final Image[] pictures = { - URLImage.createToStorage(placeholder, "tyrion","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/tyrion-lannister-512x512.jpg"), - URLImage.createToStorage(placeholder, "jaime","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/jamie-lannister-512x512.jpg"), - URLImage.createToStorage(placeholder, "cersei","http://i.lv3.hbo.com/assets/images/series/game-of-thrones/character/s5/cersei-lannister-512x512.jpg") -}; - -ac.setCompletionRenderer(new ListCellRenderer() { - private final Label focus = new Label(); // <2> - private final Label line1 = new Label(characters[0]); - private final Label line2 = new Label(actors[0]); - private final Label icon = new Label(pictures[0]); - private final Container selection = BorderLayout.center( - BoxLayout.encloseY(line1, line2)).add(BorderLayout.EAST, icon); - - @Override - public Component getListCellRendererComponent(com.codename1.ui.List list, Object value, int index, boolean isSelected) { - for(int iter = 0 ; iter < characters.length ; iter++) { - if(characters[iter].equals(value)) { - line1.setText(characters[iter]); - if(actors.length > iter) { - line2.setText(actors[iter]); - icon.setIcon(pictures[iter]); - } else { - line2.setText(""); // <3> - icon.setIcon(placeholder); - } - break; - } - } - return selection; - } - - @Override - public Component getListFocusComponent(com.codename1.ui.List list) { - return focus; - } -}); -current.add(ac); - -current.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava121Snippet.java[tag=the-components-of-codename-one-java-121,indent=0] ---- <1> you have duplicate arrays that are partial for clarity. This is a separate list of data element but you can fetch the more data from anywhere @@ -3693,33 +2055,7 @@ The sample below includes al picker types: [source,java] ---- -Form hi = new Form("Picker", new BoxLayout(BoxLayout.Y_AXIS)); -Picker datePicker = new Picker(); -datePicker.setType(Display.PICKER_TYPE_DATE); -Picker dateTimePicker = new Picker(); -dateTimePicker.setType(Display.PICKER_TYPE_DATE_AND_TIME); -Picker timePicker = new Picker(); -timePicker.setType(Display.PICKER_TYPE_TIME); -Picker stringPicker = new Picker(); -stringPicker.setType(Display.PICKER_TYPE_STRINGS); -Picker durationPicker = new Picker(); -durationPicker.setType(Display.PICKER_TYPE_DURATION); -Picker minuteDurationPicker = new Picker(); -minuteDurationPicker.setType(Display.PICKER_TYPE_DURATION_MINUTES); -Picker hourDurationPicker = new Picker(); -hourDurationPicker.setType(Display.PICKER_TYPE_DURATION_HOURS); - -datePicker.setDate(new Date()); -dateTimePicker.setDate(new Date()); -timePicker.setTime(10 * 60); // 10:00AM = Minutes since midnight -stringPicker.setStrings("A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows", - "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring"); -stringPicker.setSelectedString("A Game of Thrones"); - -hi.add(datePicker).add(dateTimePicker).add(timePicker) - .add(stringPicker).add(durationPicker) - .add(minuteDurationPicker).add(hourDurationPicker); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava122Snippet.java[tag=the-components-of-codename-one-java-122,indent=0] ---- @@ -3759,21 +2095,6 @@ A common use case is to format date values based on a specific appearance and `P When using lightweight picker mode (`setUseLightweightPopup(true)`), you can add custom quick-action buttons to the popup. This is useful for actions like setting the date to "Today" or "+7 Days" without scrolling the wheels manually: -[source,java] ----- -Picker picker = new Picker(); -picker.setType(Display.PICKER_TYPE_DATE); -picker.setUseLightweightPopup(true); -picker.setDate(new Date()); - -picker.addLightweightPopupButton("Today", () -> picker.setDate(new Date())); - -picker.addLightweightPopupButton("+7 Days", () -> { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DAY_OF_MONTH, 7); - picker.setDate(cal.getTime()); -}, Picker.LightweightPopupButtonPlacement.BELOW_SPINNER); ----- Button placement options are: @@ -3788,38 +2109,14 @@ that can be exposed by swiping the component to the side. This swipe gesture is commonly used in touch interfaces to expose features such as delete, edit etc. It's trivial to use this component by determining the components placed on top and bottom (the revealed component): -[source,java] ----- -SwipeableContainer swip = new SwipeableContainer(bottom, top); ----- You can combine some demos above including the <> to rank GRRM's books in an interactive way: -[source,java] ----- -Form hi = new Form("Swipe", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add(createRankWidget("A Game of Thrones", "1996")). - add(createRankWidget("A Clash Of Kings", "1998")). - add(createRankWidget("A Storm Of Swords", "2000")). - add(createRankWidget("A Feast For Crows", "2005")). - add(createRankWidget("A Dance With Dragons", "2011")). - add(createRankWidget("The Winds of Winter", "TBD")). - add(createRankWidget("A Dream of Spring", "TBD")); -hi.show(); - -public SwipeableContainer createRankWidget(String title, String year) { - MultiButton button = new MultiButton(title); - button.setTextLine2(year); - return new SwipeableContainer(FlowLayout.encloseCenterMiddle(createStarRankSlider()), - button); -} ----- .SwipableContainer showing a common use case of ranking on swipe image::img/components-swipablecontainer.png[SwipableContainer showing a common use case of ranking on swipe,scaledwidth=20%] - === EmbeddedContainer https://www.codenameone.com/javadoc/com/codename1/ui/util/EmbeddedContainer.html[EmbeddedContainer] solves a problem that exists within the GUI builder and the class makes no sense outside of the context of the GUI builder. @@ -3848,35 +2145,6 @@ image::img/mapcomponent.png[Map Component,scaledwidth=30%] The screenshot above was produced using the following code: -[source,java] ----- -Form map = new Form("Map"); -map.setLayout(new BorderLayout()); -map.setScrollable(false); -final MapComponent mc = new MapComponent(); - -try { - //get the current location from the Location API - Location loc = LocationManager.getLocationManager().getCurrentLocation(); - - Coord lastLocation = new Coord(loc.getLatitude(), loc.getLongtitude()); - Image i = Image.createImage("/blue_pin.png"); - PointsLayer pl = new PointsLayer(); - pl.setPointIcon(i); - PointLayer p = new PointLayer(lastLocation, "You Are Here", i); - p.setDisplayName(true); - pl.addPoint(p); - mc.addLayer(pl); -} catch (IOException ex) { - ex.printStackTrace(); -} -mc.zoomToLayers(); - -map.addComponent(BorderLayout.CENTER, mc); -map.addCommand(new BackCommand()); -map.setBackCommand(new BackCommand()); -map.show(); ----- The example below shows how to integrate the https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html[MapComponent] with the Google https://www.codenameone.com/javadoc/com/codename1/location/Location.html[Location] API. Make sure to get your secret API key from the Google https://www.codenameone.com/javadoc/com/codename1/location/Location.html[Location] data API at: @@ -3885,90 +2153,6 @@ https://developers.google.com/maps/documentation/places/ .MapComponent with Google Location API image::img/map-loc.png[MapComponent with Google Location API,scaledwidth=25%] -[source,java] ----- - - final Form map = new Form("Map"); - map.setLayout(new BorderLayout()); - map.setScrollable(false); - final MapComponent mc = new MapComponent(); - Location loc = LocationManager.getLocationManager().getCurrentLocation(); - //use the code from above to show you on the map - putMeOnMap(mc); - map.addComponent(BorderLayout.CENTER, mc); - map.addCommand(new BackCommand()); - map.setBackCommand(new BackCommand()); - - ConnectionRequest req = new ConnectionRequest() { - - protected void readResponse(InputStream input) throws IOException { - JSONParser p = new JSONParser(); - Hashtable h = p.parse(new InputStreamReader(input)); - // "status" : "REQUEST_DENIED" - String response = (String)h.get("status"); - if(response.equals("REQUEST_DENIED")){ - System.out.println("make sure to obtain a key from " - + "https://developers.google.com/maps/documentation/places/"); - progress.dispose(); - Dialog.show("Info", "make sure to obtain an application key from " - + "google places api's" - , "Ok", null); - return; - } - - final Vector v = (Vector) h.get("results"); - - Image im = Image.createImage("/red_pin.png"); - PointsLayer pl = new PointsLayer(); - pl.setPointIcon(im); - pl.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent evt) { - PointLayer p = (PointLayer) evt.getSource(); - System.out.println("pressed " + p); - - Dialog.show("Details", "" + p.getName(), "Ok", null); - } - }); - - for (int i = 0; i < v.size(); i++) { - Hashtable entry = (Hashtable) v.elementAt(i); - Hashtable geo = (Hashtable) entry.get("geometry"); - Hashtable loc = (Hashtable) geo.get("location"); - Double lat = (Double) loc.get("lat"); - Double lng = (Double) loc.get("lng"); - PointLayer point = new PointLayer(new Coord(lat.doubleValue(), lng.doubleValue()), - (String) entry.get("name"), null); - pl.addPoint(point); - } - progress.dispose(); - - mc.addLayer(pl); - map.show(); - mc.zoomToLayers(); - - } - }; - req.setUrl("https://maps.googleapis.com/maps/api/place/search/json"); - req.setPost(false); - req.addArgument("location", "" + loc.getLatitude() + "," + loc.getLongtitude()); - req.addArgument("radius", "500"); - req.addArgument("types", "food"); - req.addArgument("sensor", "false"); - - //get your own key from https://developers.google.com/maps/documentation/places/ - //and replace it here. - String key = "yourAPIKey"; - - req.addArgument("key", key); - - NetworkManager.getInstance().addToQueue(req); - } - catch (IOException ex) { - ex.printStackTrace(); - } - } ----- === Chart Component @@ -4055,74 +2239,6 @@ For example: the colors, fonts, styles, to use. You can check out the https://github.com/codenameone/codenameone-demos/tree/master/ChartsDemo[ChartsDemo] app for specific examples, but here is a high-level view of some code that creates a Pie Chart: -[source,java] ----- -/** - * Creates a renderer for the specified colors. - */ -private DefaultRenderer buildCategoryRenderer(int[] colors) { - DefaultRenderer renderer = new DefaultRenderer(); - renderer.setLabelsTextSize(15); - renderer.setLegendTextSize(15); - renderer.setMargins(new int[]{20, 30, 15, 0}); - for (int color : colors) { - SimpleSeriesRenderer r = new SimpleSeriesRenderer(); - r.setColor(color); - renderer.addSeriesRenderer(r); - } - return renderer; -} - -/** - * Builds a category series using the provided values. - * - * @param titles the series titles - * @param values the values - * @return the category series - */ -protected CategorySeries buildCategoryDataset(String title, double[] values) { - CategorySeries series = new CategorySeries(title); - int k = 0; - for (double value : values) { - series.add("Project " + ++k, value); - } - - return series; -} - -public Form createPieChartForm() { - - // Generate the values - double[] values = new double[]{12, 14, 11, 10, 19}; - - // Set up the renderer - int[] colors = new int[]{ColorUtil.BLUE, ColorUtil.GREEN, ColorUtil.MAGENTA, ColorUtil.YELLOW, ColorUtil.CYAN}; - DefaultRenderer renderer = buildCategoryRenderer(colors); - renderer.setZoomButtonsVisible(true); - renderer.setZoomEnabled(true); - renderer.setChartTitleTextSize(20); - renderer.setDisplayValues(true); - renderer.setShowLabels(true); - SimpleSeriesRenderer r = renderer.getSeriesRendererAt(0); - r.setGradientEnabled(true); - r.setGradientStart(0, ColorUtil.BLUE); - r.setGradientStop(0, ColorUtil.GREEN); - r.setHighlighted(true); - - // Create the chart ... pass the values and renderer to the chart object. - PieChart chart = new PieChart(buildCategoryDataset("Project budget", values), renderer); - - // Wrap the chart in a Component so we can add it to a form - ChartComponent c = new ChartComponent(chart); - - // Create a form and show it. - Form f = new Form("Budget"); - f.setLayout(new BorderLayout()); - f.addComponent(BorderLayout.CENTER, c); - return f; - -} ----- === Calendar @@ -4132,14 +2248,6 @@ NOTE: You recommend developers use the <> rather than Simple usage of the `Calendar` class looks something like this: -[source,java] ----- -Form hi = new Form("Calendar", new BorderLayout()); -Calendar cld = new Calendar(); -cld.addActionListener((e) -> Log.p("You picked: " + new Date(cld.getSelectedDay()))); -hi.add(BorderLayout.CENTER, cld); ----- - .The Calendar component image::img/components-calendar.png[The Calendar component,scaledwidth=20%] @@ -4150,53 +2258,21 @@ The https://www.codenameone.com/javadoc/com/codename1/components/ToastBar.html[T Simple usage of the `ToastBar` class looks something like this: -[source,java] ----- -Status status = ToastBar.getInstance().createStatus(); -status.setMessage("Downloading your file..."); -status.show(); - -// ... Later on when download completes -status.clear(); ----- .ToastBar with message image::img/components-statusbar-message.png[ToastBar with message,scaledwidth=20%] You can show a progress indicator in the ToastBar like this: -[source,java] ----- -Status status = ToastBar.getInstance().createStatus(); -status.setMessage("Hello world"); -status.setShowProgressIndicator(true); -status.show(); ----- .Status Message with Progress Bar image::img/components-statusbar.png[Status Message with Progress Bar,scaledwidth=20%] You can automatically clear a status message/progress after a timeout using the `setExpires` method as such: -[source,java] ----- -Status status = ToastBar.getInstance().createStatus(); -status.setMessage("Hello world"); -status.setExpires(3000); // only show the status for 3 seconds, then have it automatically clear -status.show(); ----- You can also delay the showing of the status message using `showDelayed` as such: -[source,java] ----- -Status status = ToastBar.getInstance().createStatus(); -status.setMessage("Hello world"); -status.showDelayed(300); // Wait 300 ms to show the status - -// ... Some time later, clear the status... This may be before it shows at all -status.clear(); ----- .ToastBar with a multiline message image::img/components-statusbar-multiline.png[ToastBar with a multiline message,scaledwidth=20%] @@ -4212,21 +2288,7 @@ This simple example shows you how you can undo any addition to the UI in a simil [source,java] ---- -Form hi = new Form("Undo", BoxLayout.y()); -Button add = new Button("Add"); - -add.addActionListener(e -> { - Label l = new Label("Added this"); - hi.add(l); - hi.revalidate(); - ToastBar.showMessage("Added, click here to undo...", FontImage.MATERIAL_UNDO, - ee -> { - l.remove(); - hi.revalidate(); - }); -}); -hi.add(add); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava134Snippet.java[tag=the-components-of-codename-one-java-134,indent=0] ---- === SignatureComponent @@ -4237,19 +2299,7 @@ Simple usage of the `SignatureComponent` class looks like: [source,java] ---- -Form hi = new Form("Signature Component"); -hi.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); -hi.add("Enter Your Name:"); -hi.add(new TextField()); -hi.add("Signature:"); -SignatureComponent sig = new SignatureComponent(); -sig.addActionListener((evt)-> { - System.out.println("The signature was changed"); - Image img = sig.getSignatureImage(); - // Now we can do whatever we want with the image of this signature. -}); -hi.addComponent(sig); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava135Snippet.java[tag=the-components-of-codename-one-java-135,indent=0] ---- .The signature Component @@ -4264,21 +2314,7 @@ Simple usage of the `Accordion` class looks like: [source,java] ---- -Form f = new Form("Accordion", new BoxLayout(BoxLayout.Y_AXIS)); -f.setScrollableY(true); -Accordion accr = new Accordion(); -accr.addContent("Item1", new SpanLabel("The quick brown fox jumps over the lazy dog\n" - + "The quick brown fox jumps over the lazy dog")); -accr.addContent("Item2", new SpanLabel("The quick brown fox jumps over the lazy dog\n" - + "The quick brown fox jumps over the lazy dog\n " - + "The quick brown fox jumps over the lazy dog\n " - + "The quick brown fox jumps over the lazy dog\n " - + "")); - -accr.addContent("Item3", BoxLayout.encloseY(new Label("Label"), new TextField(), new Button("Button"), new CheckBox("CheckBox"))); - -f.add(accr); -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava136Snippet.java[tag=the-components-of-codename-one-java-136,indent=0] ---- .The Accordion Component @@ -4290,13 +2326,7 @@ https://www.codenameone.com/javadoc/com/codename1/components/FloatingHint.html[F [source,java] ---- -Form hi = new Form("Floating Hint", BoxLayout.y()); -TextField first = new TextField("", "First Field"); -TextField second = new TextField("", "Second Field"); -hi.add(new FloatingHint(first)). - add(new FloatingHint(second)). - add(new Button("Go")); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava137Snippet.java[tag=the-components-of-codename-one-java-137,indent=0] ---- .The FloatingHint component with one component that contains text and another that doesn't @@ -4311,9 +2341,7 @@ It has a drop shadow to distinguish it from the UI underneath and it can hide tw [source,java] ---- -FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD); -fab.addActionListener(e -> ToastBar.showErrorMessage("Not implemented yet...")); -fab.bindFabToContainer(form.getContentPane()); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava138Snippet.java[tag=the-components-of-codename-one-java-138,indent=0] ---- Which will place a `+` sign button that will perform the action. Or you can create a nested action @@ -4321,10 +2349,7 @@ where a click on the button will produce a submenu for users to pick from for ex [source,java] ---- -FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD); -fab.createSubFAB(FontImage.MATERIAL_PEOPLE, ""); -fab.createSubFAB(FontImage.MATERIAL_IMPORT_CONTACTS, ""); -fab.bindFabToContainer(form.getContentPane()); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava139Snippet.java[tag=the-components-of-codename-one-java-139,indent=0] ---- .FloatingActionButton with submenu expanded @@ -4336,26 +2361,11 @@ Those familiar with this widget know that there are many nuances to this UI that Floating buttons can also be used to badge an arbitrary component in the style popularized by iOS/macOS. A badge appears in the top right corner and includes special numeric details such as `unread count.`. -The code below adds a simple badge to an icon button: +The code below adds a simple badge to a chat button: [source,java] ---- -Form hi = new Form("Badge"); - -Button chat = new Button(""); -FontImage.setMaterialIcon(chat, FontImage.MATERIAL_CHAT, 7); - -FloatingActionButton badge = FloatingActionButton.createBadge("33"); -hi.add(badge.bindFabToContainer(chat, Component.RIGHT, Component.TOP)); - -TextField changeBadgeValue = new TextField("33"); -changeBadgeValue.addDataChangedListener((i, ii) -> { - badge.setText(changeBadgeValue.getText()); - badge.getParent().revalidate(); -}); -hi.add(changeBadgeValue); - -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java[tag=the-components-of-codename-one-java-140,indent=0] ---- The code above results in this, notice you can type into the text field to change the badge value: @@ -4367,26 +2377,12 @@ image::img/badge-floating-button.png[Badge floating button in action,scaledwidth The split pane component is a bit desktop specific but works reasonably well on devices. To get the image below you changed `SalesDemo.java` in the kitchen sink by changing this: -[source,java] ----- -private Container encloseInMaximizableGrid(Component cmp1, Component cmp2) { - GridLayout gl = new GridLayout(2, 1); - Container grid = new Container(gl); - gl.setHideZeroSized(true); - - grid.add(encloseInMaximize(grid, cmp1)). - add(encloseInMaximize(grid, cmp2)); - return grid; -} ----- To: [source,java] ---- -private Container encloseInMaximizableGrid(Component cmp1, Component cmp2) { - return new SplitPane(SplitPane.VERTICAL_SPLIT, cmp1, cmp2, "25%", "50%", "75%"); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava142Snippet.java[tag=the-components-of-codename-one-java-142,indent=0] ---- .Split Pane in the Kitchen Sink Demo diff --git a/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc b/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc index 050bc3d2bf3..6b13389e09a 100644 --- a/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc +++ b/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc @@ -11,15 +11,6 @@ TIP: This has huge advantages for your code. You can normally assume that all co You can visualize the EDT as a loop such as this: -[source,java] ----- -while(codenameOneRunning) { - performEventCallbacks(); - performCallSeriallyCalls(); - drawGraphicsAndAnimations(); - sleepUntilNextEDTCycle(); -} ----- Normally, every call you receive from Codename One will occur on the EDT. For example, every event, calls to paint(), and lifecycle calls (start etc.) should all occur on the EDT. @@ -44,46 +35,14 @@ Codename One includes helper methods in the https://www.codenameone.com/javadoc/ IMPORTANT: The Runnable passed to the `callSerially` and `callSeriallyAndWait` methods isn't a `Thread`. Use the `Runnable` interface as a convenient callback interface: -[source,java] ----- -// this code is executing in a separate thread -final String res = methodThatTakesALongTime(); -Display.getInstance().callSerially(new Runnable() { - public void run() { - // this occurs on the EDT so I can make changes to UI components - resultLabel.setText(res); - } -}); ----- TIP: You can write this code more concisely using Java 8 lambda code as such: -[source,java] ----- -// this code is executing in a separate thread -String res = methodThatTakesALongTime(); -Display.getInstance().callSerially(() -> resultLabel.setText(res)); ----- This allows code to leave the EDT and then later on return to it to perform things within the EDT. The `callSeriallyAndWait(Runnable)` method blocks the current thread until the method completes, this is useful for cases such as user notification e.g.: -[source,java] ----- -// this code is executing in a separate thread -methodThatTakesALongTime(); -Display.getInstance().callSeriallyAndWait(() -> { - // this occurs on the EDT so I can make changes to UI components - globalFlag = Dialog.show("Are You Sure?", "Do you want to continue?", "Continue", "Stop"); -}); -// this code is executing the separate thread -// global flag was already set by the call above -if(!globalFlag) { - return; -} -otherMethod(); ----- TIP: If you're unsure use `callSerially`. The use cases for `callSeriallyAndWait` are rare. When you do need to wait, the overload that accepts a timeout lets background threads marshal results back to the EDT without risking an indefinite stall if the EDT is busy. @@ -140,92 +99,29 @@ a remarkably powerful tool most Swing developers don't know about. This is best explained by an example. Typical Java code reads as a single sequence as such: -[source,java] ----- -doOperationA(); -doOperationB(); -doOperationC(); ----- This works well normally but on the EDT it might be a problem, if one of the operations is slow it might slow the whole EDT (painting, event processing etc.). Normally you can move operations into a separate thread e.g.: -[source,java] - ----- -doOperationA(); -new Thread() { - public void run() { - doOperationB(); - } -}).start(); -doOperationC(); ----- This means that operation C will happen in parallel to operation B which might be a problem... + For example, instead of using operation names lets use a more "real world" example: -[source,java] ----- -updateUIToLoadingStatus(); -readAndParseFile(); -updateUIWithContentOfFile(); ----- Notice that the first and last operations must be conducted on the EDT but the middle operation might be slow! Since `updateUIWithContentOfFile` needs `readAndParseFile` to occur before it starts doing the new thread won't be enough. A simplistic approach is to do something like this: -[source,java] ----- -updateUIToLoadingStatus(); -new Thread() { - public void run() { - readAndParseFile(); - updateUIWithContentOfFile(); - } -}).start(); ----- However, `updateUIWithContentOfFile` should be executed on the EDT and not on a random thread. The right way to do this would therefore be something like this: -[source,java] ----- -updateUIToLoadingStatus(); -new Thread() { - public void run() { - readAndParseFile(); - Display.getInstance().callSerially(new Runnable() { - public void run() { - updateUIWithContentOfFile(); - } - }); - } -}).start(); ----- This is legal and would work reasonably well, however it gets complicated as you add more and more features that need to be chained serially after all these are 3 methods! Invoke and block solves this in a unique way you can get almost the exact same behavior by using this: -[source,java] ----- -updateUIToLoadingStatus(); -Display.getInstance().invokeAndBlock(new Runnable() { - public void run() { - readAndParseFile(); - } -}); -updateUIWithContentOfFile(); ----- Or this with Java 8 syntax: -[source,java] ----- -updateUIToLoadingStatus(); -Display.getInstance().invokeAndBlock(() -> readAndParseFile()); -updateUIWithContentOfFile(); ----- Invoke and block effectively blocks the current EDT in a legal way. It spawns a separate thread that runs the `run()` method and when that run method completes it goes back to the EDT. @@ -238,12 +134,7 @@ Even if you never call `invokeAndBlock` directly you're probably using it indire [source,java] ---- -public void actionPerformed(ActionEvent ev) { - // will return true if the user clicks "OK" - if(!Dialog.show("Question", "How Are You", "OK", "Not OK")) { - // ask what went wrong... - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheEdtEventDispatchThreadJava012Snippet.java[tag=the-edt-event-dispatch-thread-java-012,indent=0] ---- Notice that the dialog show method will block the calling thread until the user clicks OK or Not OK... @@ -252,30 +143,9 @@ NOTE: Other APIs such as `NetworkManager.addToQueueAndWait()` also make use of t To explain how invokeAndBlock works you can return to the sample above of how the EDT works: -[source,java] ----- -while(codenameOneRunning) { - performEventCallbacks(); - performCallSeriallyCalls(); - drawGraphicsAndAnimations(); - sleepUntilNextEDTCycle(); -} ----- `invokeAndBlock()` works in a similar way to this pseudo-code: -[source,java] ----- -void invokeAndBlock(Runnable r) { - openThreadForR(r); - while(r is still running) { - performEventCallbacks(); - performCallSeriallyCalls(); - drawGraphicsAndAnimations(); - sleepUntilNextEDTCycle(); - } -} ----- The EDT is effectively "blocked" but `invokeAndBlock` "redoes" it from within... diff --git a/docs/developer-guide/Theme-Basics.asciidoc b/docs/developer-guide/Theme-Basics.asciidoc index 928b59ba7b7..e1507d371c7 100644 --- a/docs/developer-guide/Theme-Basics.asciidoc +++ b/docs/developer-guide/Theme-Basics.asciidoc @@ -21,25 +21,12 @@ Codename One themes are effectively a set of UIIDs mapped to a https://www.coden You can also add many resource files to a project and work with them. In code, initialize a theme like this in your main class: -[source,java] ----- -private Resources theme; - -public void init(Object context) { - theme = UIManager.initFirstTheme("/theme"); -} ----- The `initFirstTheme` method is a helper method that hides some `try`/`catch` logic and some verbosity. It could be expressed as: [source,java] ---- -try { - theme = Resources.openLayered("/theme"); - UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); -} catch(IOException e){ - e.printStackTrace(); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ThemeBasicsJava002Snippet.java[tag=theme-basics-java-002,indent=0] ---- === Customizing your theme @@ -386,10 +373,6 @@ Derive allows you to inherit the behavior of a UIID and extend it with some cust For example: Lets say you created a component that's supposed to look like a title, you could do something like: -[source,java] ----- -cmp.setUIID("Title"); ----- However, title might sometimes be aligned to the left (based on theme) and you always want your component to be center aligned. For example, you don't want that to affect the actual titles in the app... @@ -440,14 +423,6 @@ NOTE: You should notice that font sizing is inconsistent between platforms recom You can load a truetype font from code using: -[source,java] ----- -if(Font.isTrueTypeFileSupported()) { - Font myFont = Font.createTrueTypeFont(fontName, fontFileName); - myFont = myFont.derive(sizeInPixels, Font.STYLE_PLAIN); - // do something with the font -} ----- Notice that, in code, pixel sizes are supported, so it’s up to you to decide how to convert that. Recommend using millimeters with the `convertToPixels` method. You also need to derive the font with the proper size, unless you want a 0 sized font which isn't useful. @@ -457,101 +432,6 @@ IMPORTANT: Due to licensing restrictions Codename One doesn't bundle Apple's iOS The code below demonstrates all the major fonts available in Codename One with the handlee ttf file posing as a standin for arbitrary TTF: -[source,java] ----- -private Label createForFont(Font fnt, String s) { - Label l = new Label(s); - l.getUnselectedStyle().setFont(fnt); - return l; -} - -public void showForm() { - GridLayout gr = new GridLayout(5); - gr.setAutoFit(true); - Form hi = new Form("Fonts", gr); - - int fontSize = Display.getInstance().convertToPixels(3); - - // requires Handlee-Regular.ttf in the src folder root! - Font ttfFont = Font.createTrueTypeFont("Handlee", "Handlee-Regular.ttf"). - derive(fontSize, Font.STYLE_PLAIN); - - Font smallPlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL); - Font mediumPlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); - Font largePlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE); - Font smallBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL); - Font mediumBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM); - Font largeBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE); - Font smallItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_SMALL); - Font mediumItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); - Font largeItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_LARGE); - - Font smallPlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL); - Font mediumPlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); - Font largePlainMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_LARGE); - Font smallBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL); - Font mediumBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM); - Font largeBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE); - Font smallItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_SMALL); - Font mediumItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); - Font largeItalicMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC, Font.SIZE_LARGE); - - Font smallPlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL); - Font mediumPlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); - Font largePlainProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_LARGE); - Font smallBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL); - Font mediumBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM); - Font largeBoldProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE); - Font smallItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_SMALL); - Font mediumItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_MEDIUM); - Font largeItalicProportionalFont = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_ITALIC, Font.SIZE_LARGE); - - String[] nativeFontTypes = { - "native:MainThin", "native:MainLight", - "native:MainRegular", "native:MainBold", - "native:MainBlack", "native:ItalicThin", - "native:ItalicLight", "native:ItalicRegular", - "native:ItalicBold", "native:ItalicBlack"}; - - for(String s : nativeFontTypes) { - Font tt = Font.createTrueTypeFont(s, s).derive(fontSize, Font.STYLE_PLAIN); - hi.add(createForFont(tt, s)); - } - - hi.add(createForFont(ttfFont, "Handlee TTF Font")). - add(createForFont(smallPlainSystemFont, "smallPlainSystemFont")). - add(createForFont(mediumPlainSystemFont, "mediumPlainSystemFont")). - add(createForFont(largePlainSystemFont, "largePlainSystemFont")). - add(createForFont(smallBoldSystemFont, "smallBoldSystemFont")). - add(createForFont(mediumBoldSystemFont, "mediumBoldSystemFont")). - add(createForFont(largeBoldSystemFont, "largeBoldSystemFont")). - add(createForFont(smallPlainSystemFont, "smallItalicSystemFont")). - add(createForFont(mediumItalicSystemFont, "mediumItalicSystemFont")). - add(createForFont(largeItalicSystemFont, "largeItalicSystemFont")). - - add(createForFont(smallPlainMonospaceFont, "smallPlainMonospaceFont")). - add(createForFont(mediumPlainMonospaceFont, "mediumPlainMonospaceFont")). - add(createForFont(largePlainMonospaceFont, "largePlainMonospaceFont")). - add(createForFont(smallBoldMonospaceFont, "smallBoldMonospaceFont")). - add(createForFont(mediumBoldMonospaceFont, "mediumBoldMonospaceFont")). - add(createForFont(largeBoldMonospaceFont, "largeBoldMonospaceFont")). - add(createForFont(smallItalicMonospaceFont, "smallItalicMonospaceFont")). - add(createForFont(mediumItalicMonospaceFont, "mediumItalicMonospaceFont")). - add(createForFont(largeItalicMonospaceFont, "largeItalicMonospaceFont")). - - add(createForFont(smallPlainProportionalFont, "smallPlainProportionalFont")). - add(createForFont(mediumPlainProportionalFont, "mediumPlainProportionalFont")). - add(createForFont(largePlainProportionalFont, "largePlainProportionalFont")). - add(createForFont(smallBoldProportionalFont, "smallBoldProportionalFont")). - add(createForFont(mediumBoldProportionalFont, "mediumBoldProportionalFont")). - add(createForFont(largeBoldProportionalFont, "largeBoldProportionalFont")). - add(createForFont(smallItalicProportionalFont, "smallItalicProportionalFont")). - add(createForFont(mediumItalicProportionalFont, "mediumItalicProportionalFont")). - add(createForFont(largeItalicProportionalFont, "largeItalicProportionalFont")); - - hi.show(); -} ----- .The fonts running on the ipad simulator on a Mac, notice that this will look different on a PC image::img/theme-font-catalog.png[The fonts running on the ipad simulator on a Mac, notice that this will look different on a PC,scaledwidth=60%] diff --git a/docs/developer-guide/Video-Capture-Constraints.asciidoc b/docs/developer-guide/Video-Capture-Constraints.asciidoc index a0a4c9d9600..9a5595d0459 100644 --- a/docs/developer-guide/Video-Capture-Constraints.asciidoc +++ b/docs/developer-guide/Video-Capture-Constraints.asciidoc @@ -14,19 +14,9 @@ Support for these constraints vary by platform and device, but the API allows yo Suppose you want to allow the user to capture a short (5 second) clip, in a low resolution, appropriate for sharing on a social media platform. You create your `VideoCaptureConstraints` object as follows: -[source,java] ----- -VideoCaptureConstraints cnst = new VideoCaptureConstraint() - .preferredQuality(VideoCaptureConstraints.QUALITY_LOW) - .preferredMaxLength(5); ----- This constraint can then be passed to `Capture.captureVideo()` to get the captured file: -[source,java] ----- -String videoPath = Capture.captureVideo(cnst); ----- === Not all platforms support all constraints @@ -34,24 +24,6 @@ how do you know if your constraints will be obeyed? If the platform doesn't supp For example: -[source,java] ----- -if (cnst.isMaxLengthSupported()) { - // The max length constraint that we specified is supported on this platform. -} else{ - // The max length constraint is NOT supported. - // Check the effective max length constraint value to see if it may be partially - // supported - int effectiveMaxLength = cnst.getMaxLength(); - if (effectiveMaxLength == 0) { - // Max length is not supported at all... The user will be able - // to capture a video without duration restrictions - } else { - // Max length was set to some different value than we set in our - // preferredMaxLength, but the platform is at least trying to accommodate us. - } -} ----- You can probe a constraint to see whether the entire constraint is supported (i.e will be obeyed), or whether any particular aspect of it will be supported using the following methods: @@ -67,39 +39,13 @@ Suppose you want to capture a video with resolution 320×240. You would begin wi [source,java] ---- -VideoCaptureConstraints cnst = new VideoCaptureConstraints() - .preferredWidth(320) - .preferredHeight(240); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoCaptureConstraintsJava004Snippet.java[tag=video-capture-constraints-java-004,indent=0] ---- Explicit width and height constraints aren't well supported across platforms. Android doesn't support them at all. iOS supports 3 specific sizes. JavaScript supports it when running on a desktop browser or on Android - but not on iOS. Etc. Find out if this constraint will be obeyed: -[source,java] ----- -if (cnst.isSizeSupported()) { - // Yay! This platform supports our constraint, so the captured video will - // be exactly 320×240. -} else { - // Not supported... let's see if the platform will at least try to accommodate us - int effectiveWidth = cnst.getWidth(); - int effectiveHeight = cnst.getHeight(); - int quality = cnst.getQuality(); - if (effectiveWidth == 0 && effectiveHeight == 0) { - // This platform has no control over width and height - // In many cases it will try to at least set the quality approximate - if (quality != 0) { - // The platform set the quality for us to try to comply. - // Since 320×240 is pretty small, the quality would probably - // be set to QUALITY_LOW - } - } else { - // The platform couldn't capture at 320×240, but it has provided an - // alternate size that is as close to that as possible. - } -} ----- === Constraint support by platform diff --git a/docs/developer-guide/Video-IO.asciidoc b/docs/developer-guide/Video-IO.asciidoc index 4f41d0a33f5..0278177411d 100644 --- a/docs/developer-guide/Video-IO.asciidoc +++ b/docs/developer-guide/Video-IO.asciidoc @@ -26,12 +26,7 @@ As with other optional platform features, always gate usage with `isSupported()` [source,java] ---- -if (!VideoIO.isSupported()) { - // Video encoding / decoding is not available on this platform - // (for example TV, Watch or Car), so fall back gracefully. - return; -} -VideoIO io = VideoIO.getVideoIO(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java[tag=video-io-java-001,indent=0] ---- NOTE: On the desktop simulator `VideoIO` is backed by an `ffmpeg`/`ffprobe` binary. The @@ -49,25 +44,7 @@ frames, generated animation, anything. [source,java] ---- -String out = FileSystemStorage.getInstance().getAppHomePath() + "/generated.mp4"; -int w = 640, h = 480; -float fps = 30; - -VideoWriter writer = new VideoWriterBuilder() - .path(out) - .width(w).height(h).frameRate(fps) - .videoCodec(VideoIO.CODEC_H264).videoBitRate(4_000_000) - .build(); - -// Each frame is just an Image you fully control: draw whatever you like. -for (int i = 0; i < 90; i++) { // 3 seconds at 30fps - Image frame = Image.createImage(w, h, 0xff000000); - Graphics g = frame.getGraphics(); - g.setColor(0xffffff); - g.fillRect((i * 8) % w, h / 2 - 20, 60, 40); - writer.writeFrame(frame, Math.round(i * 1000f / fps)); -} -writer.close(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java[tag=video-io-java-002,indent=0] ---- To add an audio track, enable it on the builder (`.hasAudio(true)`) and push interleaved @@ -82,40 +59,14 @@ that walks the whole clip emitting evenly spaced frames: [source,java] ---- -VideoReader reader = VideoIO.getVideoIO().openReader(videoPath); -System.out.println(reader.getWidth() + "x" + reader.getHeight() - + " " + reader.getFrameRate() + "fps, " - + reader.getDurationMillis() + "ms"); - -List thumbnails = new ArrayList<>(); - -// Frame accurate single frame (unlike Media.setTime which snaps to key frames): -VideoFrame oneSecond = reader.frameAt(1000); -if (oneSecond != null) { - thumbnails.add(oneSecond.toImage()); -} - -// Resample the (possibly variable frame rate) clip to a constant 10fps stream: -reader.readFrames(10, f -> { - // f.getARGB() / f.toImage() give you the decoded RGBA pixels - thumbnails.add(f.toImage()); - return thumbnails.size() < 50; // stop after 50 frames -}); -reader.close(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java[tag=video-io-java-003,indent=0] ---- The audio track, when present, is decoded to interleaved PCM: [source,java] ---- -VideoReader reader = VideoIO.getVideoIO().openReader(videoPath); -if (reader.hasAudio()) { - AudioBuffer pcm = reader.readAudio(); - System.out.println("Decoded " + pcm.getSize() + " PCM samples at " - + reader.getAudioSampleRate() + "Hz, " - + reader.getAudioChannels() + " channels"); -} -reader.close(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java[tag=video-io-java-004,indent=0] ---- === Discovering available codecs @@ -125,13 +76,7 @@ hardware accelerated encoders): [source,java] ---- -VideoIO io = VideoIO.getVideoIO(); -for (VideoCodec codec : io.getAvailableEncoders()) { - System.out.println(codec.getId() - + " (" + codec.getName() + ")" - + (codec.isHardwareAccelerated() ? " [hardware]" : "")); -} -boolean canEncodeH264 = io.isEncoderSupported(VideoIO.CODEC_H264); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/VideoIoJava001Snippet.java[tag=video-io-java-005,indent=0] ---- === Platform support diff --git a/docs/developer-guide/Wearables.asciidoc b/docs/developer-guide/Wearables.asciidoc index 06b3ed054be..5eb188145b2 100644 --- a/docs/developer-guide/Wearables.asciidoc +++ b/docs/developer-guide/Wearables.asciidoc @@ -31,16 +31,7 @@ and `isDesktop()` checks: [source,java] ---- -Form f = new Form(BoxLayout.y()); -if (CN.isWatch()) { - // Compact, single-column layout suited to a small round/square screen - f.add(new Label("Hi Watch")); - f.getToolbar().setVisible(false); -} else { - // Full phone/tablet layout - f.add(new SpanLabel("Welcome to the full size application")); -} -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WearablesJava001Snippet.java[tag=wearables-java-001,indent=0] ---- On Android `isWatch()` is derived from `PackageManager.FEATURE_WATCH` @@ -90,7 +81,7 @@ Set the build hint: [source,properties] ---- -watchNative.enabled=true +include::../demos/common/src/main/snippets/developer-guide/wearables.properties[tag=wearables-properties-001,indent=0] ---- Alternatively, declare a watch entry point and the watch slice is produced @@ -98,7 +89,7 @@ automatically as part of the regular iOS build: [source,properties] ---- -codename1.watchMain=com.mycompany.myapp.MyWatchMain +include::../demos/common/src/main/snippets/developer-guide/wearables.properties[tag=wearables-properties-002,indent=0] ---- If you don't declare a distinct `watchMain`, the watch app reuses your phone @@ -191,14 +182,14 @@ required -- you only need to mark the build as a watch app. [source,properties] ---- -android.wear=true +include::../demos/common/src/main/snippets/developer-guide/wearables.properties[tag=wearables-properties-003,indent=0] ---- This injects the watch hardware feature into the manifest: [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/wearables.xml[tag=wearables-xml-001,indent=0] ---- By default it also declares the app *standalone*, so it installs and runs @@ -206,7 +197,7 @@ directly on the watch without a paired phone app: [source,xml] ---- - +include::../demos/common/src/main/snippets/developer-guide/wearables.xml[tag=wearables-xml-002,indent=0] ---- Setting `android.wear=true` also raises the minimum SDK to API 23 (the Wear OS diff --git a/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc b/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc index 630d5fdd360..3f30b38cc97 100644 --- a/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc +++ b/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc @@ -27,9 +27,7 @@ ports) into your local Maven cache: [source,bash] ---- -$ git clone https://github.com/codenameone/CodenameOne -$ cd CodenameOne/maven -$ mvn install +include::../demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh[tag=working-with-codenameone-sources-bash-001,indent=0] ---- The default build runs every module and its unit tests. Append `-DskipTests` @@ -46,9 +44,7 @@ can reference your locally built SDK artifacts: [source,bash] ---- -$ git clone https://github.com/shannah/cn1-maven-archetypes -$ cd cn1-maven-archetypes -$ mvn install +include::../demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh[tag=working-with-codenameone-sources-bash-002,indent=0] ---- === Running tests selectively @@ -60,14 +56,14 @@ Run any of these from the `CodenameOne/maven` directory: + [source,bash] ---- -$ mvn -pl core-unittests test +include::../demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh[tag=working-with-codenameone-sources-bash-003,indent=0] ---- * Integration tests (simulator and build client): + [source,bash] ---- -$ mvn -pl tests -am verify +include::../demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.sh[tag=working-with-codenameone-sources-bash-004,indent=0] ---- Refer to the READMEs in `maven/core-unittests` and `maven/tests` for more @@ -82,11 +78,7 @@ project), adjust the `pom.xml` properties to reference your snapshot version: [source,xml] ---- - - - {cn1-snapshot-version} - {cn1-snapshot-version} - +include::../demos/common/src/main/snippets/developer-guide/working-with-codenameone-sources.xml[tag=working-with-codenameone-sources-xml-001,indent=0] ---- Open the project in your IDE and build or run it. Maven will resolve the local diff --git a/docs/developer-guide/Working-With-Javascript.asciidoc b/docs/developer-guide/Working-With-Javascript.asciidoc index 84a060ba021..ffb474312b4 100644 --- a/docs/developer-guide/Working-With-Javascript.asciidoc +++ b/docs/developer-guide/Working-With-Javascript.asciidoc @@ -20,24 +20,11 @@ Class1.java [source,java] ---- -import com.codename1.io.Log; -class Class1 { - public static int getValue() { - Log.p("Hello world"); - return 1; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava001Snippet.java[tag=working-with-javascript-java-001,indent=0] ---- Class2.java -[source,java] ----- -class Class2 { - public static int value = Class1.getValue(); - -} ----- This fails because `Class2` calls Class1.getValue() in its static initializer, and `getValue()` calls `Log.p()`, which, underneath the covers, writes to Storage - which involves some synchronous network access in the JavaScript port (that is, it uses `wait()` and `notify()` under the hood. @@ -45,9 +32,7 @@ If you remove the call to `Log.p()` from `getValue()`, as follows: [source,java] ---- - public static int getValue() { - return 1; - } +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava003Snippet.java[tag=working-with-javascript-java-003,indent=0] ---- Then everything would be fine. @@ -225,7 +210,7 @@ By default, the CORS proxy is used for HTTP requests to URLS at a different doma [source,java] ---- -Display.getInstance().setProperty("javascript.useProxyForSameDomain", "true"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava004Snippet.java[tag=working-with-javascript-java-004,indent=0] ---- *Why would you want to do this?* @@ -265,12 +250,7 @@ You can customize this splash screen by modifying the HTML source inside the *cn [source,html] ---- -
- - - -

...Loading...

-
+include::../demos/javascript/src/main/snippets/developer-guide/working-with-javascript.html[tag=working-with-javascript-html-001,indent=0] ---- [id="debugging", reftext="Debugging in Chrome"] @@ -447,20 +427,14 @@ The solution for this is to add a *variable* into the URL as follows: [source,javascript] ---- -{ - "JavaScript" : { - "libs" : [ - "//maps.googleapis.com/maps/api/js?v=3.exp&key={{javascript.googlemaps.key}}" - ] - } -} +include::../demos/javascript/src/main/snippets/developer-guide/working-with-javascript.js[tag=working-with-javascript-javascript-001,indent=0] ---- The `{{javascript.googlemaps.key}}` variable will be replaced with the value of the `javascript.googlemaps.key` build hint by the build server, so the resulting include you see in the index.html page will be something like: [source,html] ---- - +include::../demos/javascript/src/main/snippets/developer-guide/working-with-javascript.html[tag=working-with-javascript-html-002,indent=0] ---- [id="environment_variables", reftext="Browser Environment Variables"] @@ -543,10 +517,7 @@ Then in your app's `init()` method, add the following: [source,java] ---- -Display d = Display.getInstance(); -if (d.getProperty("User-Agent", "Unknown").indexOf("Android") != -1) { - d.setProperty("javascript.native.theme", "/androidTheme.res"); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava005Snippet.java[tag=working-with-javascript-java-005,indent=0] ---- === Disabling the 'OnBeforeUnload' handler @@ -564,18 +535,7 @@ NOTE: Some browsers don't allow you to specify the message that's displayed in t [source,java] ---- -Form f = new Form("Test Before Unload", BoxLayout.y()); -CheckBox enableBeforeUnload = new CheckBox("Enable Before Unload"); -enableBeforeUnload.setSelected(true); -enableBeforeUnload.addActionListener(e->{ - if (enableBeforeUnload.isSelected()) { - CN.setProperty("platformHint.javascript.beforeUnloadMessage", "Are you sure you want to leave this page? It might be bad"); - } else { - CN.setProperty("platformHint.javascript.beforeUnloadMessage", null); - } -}); -f.add(enableBeforeUnload); -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava006Snippet.java[tag=working-with-javascript-java-006,indent=0] ---- [id="pwa_deployment", reftext="Deploying as a Progressive Web App"] @@ -656,16 +616,7 @@ For example: [source,java] ---- -CN.setProperty("platformHint.javascript.backsideHooksInterval", "1000"); - -// Now your app will process media.play() and Display.execute(...) calls -// once per second (1000ms). If play() or execute() has been called anytime -// in that second (since the last poll), it will seamlessly process the -// request. - - -// To disable polling, just set it to an interval 0 or lower. -// for example, CN.setProperty("platformHint.javascript.backsideHooksInterval", "0"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava007Snippet.java[tag=working-with-javascript-java-007,indent=0] ---- WARNING: Don't abuse this feature. You should enable this polling when necessary. For example, If your app enables the user to listen for voice commands, enable polling for the period of time that it's listening. When the user wants to stop listening, you should also stop the polling by setting the interval to "0." @@ -694,8 +645,7 @@ To preserve the iframe and its JavaScript state across deinitialization cycles, [source,java] ---- -BrowserComponent browser = new BrowserComponent(); -browser.putClientProperty("HTML5Peer.removeOnDeinitialize", Boolean.FALSE); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithJavascriptJava008Snippet.java[tag=working-with-javascript-java-008,indent=0] ---- This keeps the iframe in the DOM even when the component is deinitialized, preserving all JavaScript state. diff --git a/docs/developer-guide/Working-With-Linux.asciidoc b/docs/developer-guide/Working-With-Linux.asciidoc index cb475017319..e5f85703891 100644 --- a/docs/developer-guide/Working-With-Linux.asciidoc +++ b/docs/developer-guide/Working-With-Linux.asciidoc @@ -60,7 +60,7 @@ target. Build it with: [source,bash] ---- -mvn -pl common package -Dcodename1.platform=linux -Dcodename1.buildTarget=local-linux-device cn1:build +include::../demos/common/src/main/snippets/developer-guide/working-with-linux.sh[tag=working-with-linux-bash-001,indent=0] ---- The builder translates the app to C with ParparVM's `linux` app type, generates a @@ -74,13 +74,7 @@ the port links. On Debian/Ubuntu: [source,bash] ---- -sudo apt-get install -y \ - cmake ninja-build pkg-config \ - libgtk-3-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libglib2.0-dev \ - libfontconfig1-dev libfreetype-dev libcurl4-openssl-dev \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libwebkit2gtk-4.1-dev libsecret-1-dev libnotify-dev libgeoclue-2-dev \ - libepoxy-dev libegl1-mesa-dev libgles2-mesa-dev +include::../demos/common/src/main/snippets/developer-guide/working-with-linux.sh[tag=working-with-linux-bash-002,indent=0] ---- The `*-dev` packages are needed on the *build* host. The machine that *runs* the diff --git a/docs/developer-guide/Working-With-Windows.asciidoc b/docs/developer-guide/Working-With-Windows.asciidoc index 2a96a1b0565..33aa88bc93e 100644 --- a/docs/developer-guide/Working-With-Windows.asciidoc +++ b/docs/developer-guide/Working-With-Windows.asciidoc @@ -81,7 +81,7 @@ above. Trigger it like any other cloud target: [source,bash] ---- -mvn -pl common package -Dcodename1.platform=windows -Dcodename1.buildTarget=windows-device +include::../demos/common/src/main/snippets/developer-guide/working-with-windows.sh[tag=working-with-windows-bash-001,indent=0] ---- The convenience goal `mvn -pl common cn1:buildWin32` does the same thing. A regular @@ -151,14 +151,7 @@ point your project settings at it: [source,properties] ---- -# codenameone_settings.properties -codename1.windows.signing.certificate=/secure/path/codesign.p12 -codename1.windows.signing.password=your-pkcs12-password -# optional signature metadata + timestamping (shown with their defaults): -codename1.arg.windows.signing.name=Acme Corp -codename1.arg.windows.signing.url=https://acme.example -codename1.arg.windows.signing.timestampUrl=http://timestamp.digicert.com -codename1.arg.windows.signing.digest=sha256 +include::../demos/common/src/main/snippets/developer-guide/working-with-windows.properties[tag=working-with-windows-properties-001,indent=0] ---- You configure the certificate once and it's used for *both local and cloud diff --git a/docs/developer-guide/Working-With-iOS.asciidoc b/docs/developer-guide/Working-With-iOS.asciidoc index 0bf69ce3572..71889dac989 100644 --- a/docs/developer-guide/Working-With-iOS.asciidoc +++ b/docs/developer-guide/Working-With-iOS.asciidoc @@ -90,20 +90,6 @@ Notifications can either be set up as one-time or as repeating. ===== Example sending notification -[source,java] ------ -LocalNotification n = new LocalNotification(); -n.setId("demo-notification"); -n.setAlertBody("it's time to take a break and look at me"); -n.setAlertTitle("Break Time!"); -n.setAlertSound("beep-01a.mp3"); - -Display.getInstance().scheduleLocalNotification( - n, - System.currentTimeMillis() + 10 * 1000, // fire date/time - LocalNotification.REPEAT_MINUTE // Whether to repeat and what frequency -); ------ NOTE: On iOS, scheduling a local notification with an `id` that already exists now replaces the earlier scheduled notification with that same `id` instead of keeping duplicates. @@ -119,40 +105,13 @@ The API for receiving/handling local notifications is also like push. Your appli [source,java] ----- -public void localNotificationReceived(String notificationId) +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/WorkingWithIosJava002Snippet.java[tag=working-with-ios-java-002,indent=0] ----- The `notificationId` parameter will match the `id` value of the notification as set using `LocalNotification.setId()`. ===== Example receiving notification -[source,java] ------ -public class BackgroundLocationDemo implements LocalNotificationCallback { - //... - - public void init(Object context) { - //... - } - - public void start() { - //... - - } - - public void stop() { - //... - } - - public void destroy() { - //... - } - - public void localNotificationReceived(String notificationId) { - System.out.println("Received local notification "+notificationId); - } -} ------ NOTE: `localNotificationReceived()` is called when the user responds to the notification by tapping on the alert. If the user doesn't click on the notification, then this event handler will never be fired. @@ -160,10 +119,6 @@ NOTE: `localNotificationReceived()` is called when the user responds to the noti Repeating notifications will continue until they're canceled by the app. You can cancel a single notification by calling: -[source,java] ------ -Display.getInstance().cancelLocalNotification(notificationId); ------ Where `notificationId` is the string id that was set for the notification using `LocalNotification.setId()`. @@ -236,7 +191,7 @@ You can disable the strict URL checks from Apple by using the venerable `ios.pli [source,xml] ----- -NSAppTransportSecurityNSAllowsArbitraryLoads +include::../demos/common/src/main/snippets/developer-guide/working-with-ios.xml[tag=working-with-ios-xml-001,indent=0] ----- For example, it seems that Apple will reject your app if you include that and don't have a good reason. diff --git a/docs/developer-guide/Working-with-Mac-OS-X.asciidoc b/docs/developer-guide/Working-with-Mac-OS-X.asciidoc index ca8d4dac2f8..491953deba5 100644 --- a/docs/developer-guide/Working-with-Mac-OS-X.asciidoc +++ b/docs/developer-guide/Working-with-Mac-OS-X.asciidoc @@ -126,7 +126,7 @@ To generate an Xcode project locally that you can open and run on the Mac: [source,shell] ---- -mvn -B -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-source package +include::../demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh[tag=working-with-mac-os-x-shell-001,indent=0] ---- The output is written to: @@ -143,7 +143,7 @@ To produce a release-ready signed Mac app on the Codename One build server: [source,shell] ---- -mvn -B -Dcodename1.platform=ios -Dcodename1.buildTarget=mac-os-x-native package +include::../demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh[tag=working-with-mac-os-x-shell-002,indent=0] ---- Mac distribution requires *its own certificates and provisioning profile* — Apple issues separate _Mac App Distribution_ (3rd Party Mac Developer Application) and _Developer ID Application_ certificates that aren't interchangeable with the _iPhone Distribution_ certificate used for iOS App Store builds. Configure them via the parallel `codename1.mac.*` hints: @@ -177,10 +177,7 @@ After archiving in Xcode, run: [source,shell] ---- -xcodebuild -exportArchive \ - -archivePath build/.xcarchive \ - -exportOptionsPlist dist/ExportOptions-AppStore-Mac.plist \ - -exportPath build/export +include::../demos/common/src/main/snippets/developer-guide/working-with-mac-os-x.sh[tag=working-with-mac-os-x-shell-003,indent=0] ---- ==== Entitlements diff --git a/docs/developer-guide/appendix_goal_build.adoc b/docs/developer-guide/appendix_goal_build.adoc index fcac2b2a5b8..13e9d94edbe 100644 --- a/docs/developer-guide/appendix_goal_build.adoc +++ b/docs/developer-guide/appendix_goal_build.adoc @@ -10,7 +10,7 @@ This goal is bound to the `package` phase of the <>, so you do [source,bash] ---- -mvn cn1:build -Dcodename1.platform=javase -Dcodename1.buildTarget=mac-os-x-desktop +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-build.sh[tag=appendix-goal-build-bash-001,indent=0] ---- ==== Properties diff --git a/docs/developer-guide/appendix_goal_clone.adoc b/docs/developer-guide/appendix_goal_clone.adoc index c36e3120a03..1cfeb4c4e35 100644 --- a/docs/developer-guide/appendix_goal_clone.adoc +++ b/docs/developer-guide/appendix_goal_clone.adoc @@ -8,9 +8,7 @@ Clones the current project as a new project with a different _groupId_, _artifac [source,bash] ---- -mvn cn1:clone \ - -DgroupId=com.example.newgroup \ - -DartifactId=newapp \ +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-clone.sh[tag=appendix-goal-clone-bash-001,indent=0] ---- NOTE: This command is formatted for the bash prompt (for example, Linux or Mac). It will work on Windows also if you use bash. If you're on Windows and are using PowerShell or the regular command prompt, then you'll need to modify the command slightly. In particular, the entire command would need to be on a single line. (Remove the '\' at the end of each line, and merge lines together, with space between the command-line flags) diff --git a/docs/developer-guide/appendix_goal_create_gui_form.adoc b/docs/developer-guide/appendix_goal_create_gui_form.adoc index 67c512e617b..d2ecb84be83 100644 --- a/docs/developer-guide/appendix_goal_create_gui_form.adoc +++ b/docs/developer-guide/appendix_goal_create_gui_form.adoc @@ -7,7 +7,7 @@ The `create-gui-form` goal will generate a GUI form that can be edited in the GU [source,bash] ---- -mvn cn1:create-gui-form -DclassName=com.example.MyForm +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-create-gui-form.sh[tag=appendix-goal-create-gui-form-bash-001,indent=0] ---- The above example will generate a GUIBuilder form with the provided class name. It effectively generates two files: @@ -21,7 +21,7 @@ You can then open the GUI builder to edit this form using the `cn1:guibuilder` g [source,bash] ---- -mvn cn1:guibuilder -DclassName=com.example.MyForm +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-create-gui-form.sh[tag=appendix-goal-create-gui-form-bash-002,indent=0] ---- ==== Parameters @@ -33,4 +33,4 @@ guiType:: The kind of GUI component to generate. Supports `Form`, `Dialog`, and `Container`. Default value is "Form" autoLayout:: -Whether to use autolayout. This is boolean (true/false), and the default value is `true`. \ No newline at end of file +Whether to use autolayout. This is boolean (true/false), and the default value is `true`. diff --git a/docs/developer-guide/appendix_goal_designer.adoc b/docs/developer-guide/appendix_goal_designer.adoc index 3cd6a596408..7c657ee8512 100644 --- a/docs/developer-guide/appendix_goal_designer.adoc +++ b/docs/developer-guide/appendix_goal_designer.adoc @@ -8,7 +8,7 @@ NOTE: If your project is using CSS it may not have a theme.res file, thus this g [source,bash] ---- -mvn cn1:designer +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-designer.sh[tag=appendix-goal-designer-bash-001,indent=0] ---- -This will open the Codename One designer. \ No newline at end of file +This will open the Codename One designer. diff --git a/docs/developer-guide/appendix_goal_generate_app_project.adoc b/docs/developer-guide/appendix_goal_generate_app_project.adoc index 9d826dd3220..64ad7975e41 100644 --- a/docs/developer-guide/appendix_goal_generate_app_project.adoc +++ b/docs/developer-guide/appendix_goal_generate_app_project.adoc @@ -11,11 +11,7 @@ Because there is no existing project, you will need to provide the full maven pa [source,bash] ---- -mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ - -DsourceProject=/path/to/my/ProjectTemplate \ - -DgroupId=com.example \ - -DartifactId=myapp \ - -Dcn1Version=$CN1VERSION +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.sh[tag=appendix-goal-generate-app-project-bash-001,indent=0] ---- NOTE: Substitute the https://search.maven.org/artifact/com.codenameone/codenameone-maven-plugin[latest Codename One plugin version] for the `$CN1VERSION` in the above example. @@ -93,19 +89,7 @@ An XML snippet containing any more Maven dependencies that should be added to th [source,rpf] ---- -template.mainName=MyApp -template.packageName=com.example -template.type=maven - -[dependencies] -==== - - com.codenameone - googlemaps-lib - 1.0.1 - pom - -==== +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-app-project.txt[tag=appendix-goal-generate-app-project-rpf-001,indent=0] ---- ==== Sample: The bare-bones Kotlin app project diff --git a/docs/developer-guide/appendix_goal_generate_archetype.adoc b/docs/developer-guide/appendix_goal_generate_archetype.adoc index 0af0ee3e6f3..45d584676a5 100644 --- a/docs/developer-guide/appendix_goal_generate_archetype.adoc +++ b/docs/developer-guide/appendix_goal_generate_archetype.adoc @@ -34,94 +34,7 @@ Here is a sample template file: [source,text] ----- -#set( $symbol_pound = '#' ) -#set( $symbol_dollar = '$' ) -#set( $symbol_escape = '\' ) -package ${package}; -/* -[archetype] <1> ----- -extends=../cn1app-archetype <2> -groupId=com.codenameone.archetypes -artifactId=helloworld2-archetype -version=7.0-SNAPSHOT ----- - -[dependencies] <3> ----- - -com.codenameone.libs -filechooser-lib -1.0-SNAPSHOT -pom - ----- - */ -import static com.codename1.ui.CN.*; -import com.codename1.ui.*; -import com.codename1.ui.layouts.*; -import com.codename1.io.*; -import com.codename1.ui.plaf.*; -import com.codename1.ui.util.Resources; - -/** - * This file was generated by Codename One for the purpose - * of building native mobile applications using Java. - */ -public class ${mainName} { - - private Form current; - private Resources theme; - - public void init(Object context) { - // use two network threads instead of one - updateNetworkThreadCount(2); - - theme = UIManager.initFirstTheme("/theme"); - - // Enable Toolbar on all Forms by default - Toolbar.setGlobalToolbar(true); - - // Pro feature - Log.bindCrashProtection(true); - - addNetworkErrorListener(err -> { - // prevent the event from propagating - err.consume(); - if(err.getError()!= null) { - Log.e(err.getError()); - } - Log.sendLogAsync(); - Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null); - }); - } - - - public void start() { - if(current!= null){ - current.show(); - return; - } - //WebSocket sock; - Form hi = new Form("Hi World", BoxLayout.y()); - hi.add(new Label("Hello World")); - - hi.show(); - } - - public void stop() { - current = getCurrentForm(); - if(current instanceof Dialog) { - ((Dialog)current).dispose(); - current = getCurrentForm(); - } - } - - public void destroy() { - } - -} - +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-001,indent=0] ----- <1> The `[archetype]` section specifies details about the archetype project to be generated. For example, the `groupId`, `artifactId`, etc. <2> The `extends` property in the `[archetype]` section is required, and points to the location of the archetype project that this template is based on. Path is relative to the location of the template file. @@ -139,13 +52,7 @@ This is a required section and specifies both the location of the base archetype [source,text] ----- -[archetype] ----- -extends=../cn1app-archetype -groupId=com.codenameone.archetypes -artifactId=helloworld2-archetype -version=7.0-SNAPSHOT ----- +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-002,indent=0] ----- **Properties** @@ -187,15 +94,7 @@ Specify more dependencies that should be injected into the `` sect [source,text] ---- -[dependencies] ---- - - com.codenameone.libs - filechooser-lib - 1.0-SNAPSHOT - pom - ---- +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-003,indent=0] ---- ==== @@ -207,18 +106,7 @@ CSS content that should be injected into the theme.css file of the project. This [source,text] ---- -[css] ---- -#Constants { - includeNativeBool: true; -} -Button { - color:green; - border:1px solid green; - border-radius: 2mm; - margin: 5mm; -} ---- +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-004,indent=0] ---- ==== @@ -230,13 +118,7 @@ Properties that should be appended to the `codenameone_settings.properties` file [source,text] ---- -[properties] ---- -codename1.arg.win.desktop-vm=zuluFx8-32bit -codename1.arg.win.desktopExtractDll=true -codename1.arg.win.launchOnStart=true -codename1.arg.win.runAfterInstall=true ---- +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-005,indent=0] ---- ==== @@ -248,56 +130,6 @@ Files contains a list of files that should be created in the archetype project. [source,text] ----- -[files] ----- -src/main/guibuilder/__mainName__MainForm.gui -src/main/java/__mainName__MainForm.java ----- - -[file:src/main/guibuilder/__mainName__MainForm.gui] ----- - - - - - - - ----- - -[file:src/main/java/__mainName__MainForm.java] ----- -package ${package}; -public class ${mainName}MainForm extends com.codename1.ui.Form { -public ${mainName}MainForm() { -this(com.codename1.ui.util.Resources.getGlobalResources()); -} - - public ${mainName}MainForm(com.codename1.ui.util.Resources resourceObjectInstance) { - initGuiBuilderComponents(resourceObjectInstance); - } - -//-- DON'T EDIT BELOW THIS LINE!!! - protected com.codename1.ui.Button gui_Button = new com.codename1.ui.Button(); - - -// - private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) { - setLayout(new com.codename1.ui.layouts.LayeredLayout()); - setInlineStylesTheme(resourceObjectInstance); - setScrollableY(true); - setInlineStylesTheme(resourceObjectInstance); - setTitle("MyForm"); - setName("MyForm"); - gui_Button.setText("Click Me"); - gui_Button.setInlineStylesTheme(resourceObjectInstance); - gui_Button.setName("Button"); - addComponent(gui_Button); - ((com.codename1.ui.layouts.LayeredLayout)gui_Button.getParent().getLayout()).setInsets(gui_Button, "auto auto auto auto").setReferenceComponents(gui_Button, "-1 -1 -1 -1").setReferencePositions(gui_Button, "0.0 0.0 0.0 0.0"); - }// - -//-- DON'T EDIT ABOVE THIS LINE!!! -} ----- +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-archetype.txt[tag=appendix-goal-generate-archetype-text-006,indent=0] ----- ==== diff --git a/docs/developer-guide/appendix_goal_generate_cn1lib_project.adoc b/docs/developer-guide/appendix_goal_generate_cn1lib_project.adoc index 2f2cdb9b0b9..63029f62090 100644 --- a/docs/developer-guide/appendix_goal_generate_cn1lib_project.adoc +++ b/docs/developer-guide/appendix_goal_generate_cn1lib_project.adoc @@ -16,12 +16,7 @@ You can run the `generate-cn1lib-project` goal as follows: [source,bash] ---- -mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-cn1lib-project \ - -DsourceProject=/path/to/MyLegacyAntLibraryProject \ - -DgroupId=com.example \ - -DartifactId=my-maven-lib \ - -Dversion=1.0-SNAPSHOT \ - -U +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.sh[tag=appendix-goal-generate-cn1lib-project-bash-001,indent=0] ---- NOTE: Make sure you substitute the https://search.maven.org/search?q=a:codenameone-maven-plugin[latest codenameone-maven-plugin version] for the `$CN1VERSION` in this command. @@ -39,20 +34,14 @@ For example: [source,bash] ---- -cd my-maven-lib -mvn install +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.sh[tag=appendix-goal-generate-cn1lib-project-bash-002,indent=0] ---- After running the "install" command, you should be able to add your library as a dependency to a <> project using the following dependency: [source,xml] ---- - - com.example - my-maven-lib-lib - 1.0-SNAPSHOT - pom - +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-cn1lib-project.xml[tag=appendix-goal-generate-cn1lib-project-xml-001,indent=0] ---- IMPORTANT: Notice that the `artifactId` has an extra "-lib" appended. That is, it's `my-maven-lib-lib` and not `my-maven-lib`. This is because the `artifactId` that you specify in the `generate-cn1lib-project` goal is used for the "root" module of the multimodule maven project. The actual "lib" project that you can use as a Maven dependency is the "lib" submodule, which uses the specified artifactId with a "-lib" suffix. diff --git a/docs/developer-guide/appendix_goal_generate_graphql.adoc b/docs/developer-guide/appendix_goal_generate_graphql.adoc index 450ea174ace..e4d1f4f7eaf 100644 --- a/docs/developer-guide/appendix_goal_generate_graphql.adoc +++ b/docs/developer-guide/appendix_goal_generate_graphql.adoc @@ -24,10 +24,7 @@ as `@Mapped` mappers and `@GrpcClient` clients). [source, bash] ---- -mvn -pl common cn1:generate-graphql \ - -Dcn1.graphql.schema=schema.graphqls \ - -Dcn1.graphql.operations=operations.graphql \ - -Dcn1.graphql.basePackage=com.example.starwars +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-graphql.sh[tag=appendix-goal-generate-graphql-bash-001,indent=0] ---- Configuration: @@ -116,65 +113,14 @@ their `name()`. Built-in scalars map to their boxed Java type The `@GraphQLClient` interface looks like: -[source, java] ----- -@GraphQLClient("https://api.example.com/graphql") -public interface StarWarsApi { - - @Query("query HeroName($episode: Episode) { hero(episode: $episode) { name } }") - void heroName(@Var("episode") Episode episode, - @Header("Authorization") String bearerToken, - OnComplete> callback); - - @Mutation("mutation AddReview($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars } }") - void addReview(@Var("ep") Episode ep, @Var("review") ReviewInput review, - @Header("Authorization") String bearerToken, - OnComplete> callback); - - @Subscription("subscription OnReview($ep: Episode!) { reviewAdded(episode: $ep) { stars } }") - GraphQLSubscription onReview(@Var("ep") Episode ep, - @Header("Authorization") String bearerToken, - GraphQLSubscription.Handler handler); - - static StarWarsApi of(String endpoint) { - return GraphQLClients.create(StarWarsApi.class, endpoint); - } -} ----- Call sites use the static factory. Queries and mutations report through an `OnComplete>`: -[source, java] ----- -StarWarsApi api = StarWarsApi.of("https://api.example.com/graphql"); -api.heroName(Episode.JEDI, "Bearer " + token, response -> { - if (response.isOk()) { - renderHero(response.getData().hero().name()); - } else { - showError(response.getResponseErrorMessage()); - } -}); ----- A subscription returns a `GraphQLSubscription` handle whose `cancel()` ends the stream: -[source, java] ----- -GraphQLSubscription sub = api.onReview(Episode.EMPIRE, "Bearer " + token, - new GraphQLSubscription.Handler() { - public void onNext(GraphQLResponse r) { - addReview(r.getData().reviewAdded().stars()); - } - public void onError(GraphQLResponse r) { - showError(r.getResponseErrorMessage()); - } - public void onComplete() { } - }); -// later: -sub.cancel(); ----- The `Impl` class that performs the request lives in `target/generated-sources` -- the project source never references it diff --git a/docs/developer-guide/appendix_goal_generate_grpc.adoc b/docs/developer-guide/appendix_goal_generate_grpc.adoc index 65a09172416..0c42eeb74bc 100644 --- a/docs/developer-guide/appendix_goal_generate_grpc.adoc +++ b/docs/developer-guide/appendix_goal_generate_grpc.adoc @@ -22,9 +22,7 @@ as `@Mapped` mappers). [source, bash] ---- -mvn -pl common cn1:generate-grpc \ - -Dcn1.grpc.proto=helloworld.proto \ - -Dcn1.grpc.basePackage=com.example.hello +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-grpc.sh[tag=appendix-goal-generate-grpc-bash-001,indent=0] ---- Configuration: @@ -76,38 +74,9 @@ optional `name` attribute so introspection tooling can recover it. The `@GrpcClient` interface looks like: -[source, java] ----- -@GrpcClient("helloworld.Greeter") -public interface GreeterGrpc { - - @Rpc("SayHello") - void sayHello(HelloRequest request, - @Header("Authorization") String bearerToken, - OnComplete> callback); - - static GreeterGrpc of(String baseUrl) { - return GrpcClients.create(GreeterGrpc.class, baseUrl); - } -} ----- Call sites use the static factory: -[source, java] ----- -GreeterGrpc g = GreeterGrpc.of("https://api.example.com"); -HelloRequest req = new HelloRequest(); -req.name = "world"; -g.sayHello(req, "Bearer " + token, response -> { - if (response.isOk()) { - renderGreeting(response.getResponseData().message); - } else { - showError("gRPC status " + response.getResponseCode() - + ": " + response.getResponseErrorMessage()); - } -}); ----- The `GrpcImpl` class that actually performs the gRPC-Web POST lives in `target/generated-sources` -- the project source diff --git a/docs/developer-guide/appendix_goal_generate_native_interfaces.adoc b/docs/developer-guide/appendix_goal_generate_native_interfaces.adoc index 0e6aa06890a..50293736521 100644 --- a/docs/developer-guide/appendix_goal_generate_native_interfaces.adoc +++ b/docs/developer-guide/appendix_goal_generate_native_interfaces.adoc @@ -14,16 +14,14 @@ After creating this (and possibly other) native interfaces in your project, run [source, bash] ---- -mvn cn1:generate-native-interfaces +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-native-interfaces.sh[tag=appendix-goal-generate-native-interfaces-bash-001,indent=0] ---- By default this generates Java/Objective-C stubs. You can optionally include Swift and Kotlin stubs (both off by default): [source,bash] ---- -mvn cn1:generate-native-interfaces \ - -Dcn1.generateNativeInterfaces.swift=true \ - -Dcn1.generateNativeInterfaces.kotlin=true +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-native-interfaces.sh[tag=appendix-goal-generate-native-interfaces-bash-002,indent=0] ---- This will generate the following files (if they don't exist yet). diff --git a/docs/developer-guide/appendix_goal_generate_openapi.adoc b/docs/developer-guide/appendix_goal_generate_openapi.adoc index fc9acf9d78d..6b979b23506 100644 --- a/docs/developer-guide/appendix_goal_generate_openapi.adoc +++ b/docs/developer-guide/appendix_goal_generate_openapi.adoc @@ -19,9 +19,7 @@ bootstrap class (the same splice pattern as `@Mapped` mappers). [source, bash] ---- -mvn -pl common cn1:generate-openapi \ - -Dcn1.openapi.spec=petstore.json \ - -Dcn1.openapi.basePackage=com.example.petstore +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-generate-openapi.sh[tag=appendix-goal-generate-openapi-bash-001,indent=0] ---- Configuration: @@ -74,27 +72,12 @@ and path; parameters are annotated `@Path` / `@Query` / `@Header` / `@Body` so the processor knows how to assemble the `Rest` call. The emitted method shape: -[source, java] +[source,java] ---- -@RestClient -public interface PetApi { - - @GET("/pet/{petId}") - void getPetById(@Path("petId") Long petId, - @Header("Authorization") String bearerToken, - OnComplete> callback); - - @POST("/pet") - void addPet(@Body com.example.petstore.model.Pet body, - @Header("Authorization") String bearerToken, - OnComplete> callback); - - static PetApi of(String baseUrl) { - return RestClients.create(PetApi.class, baseUrl); - } -} +include::../demos/common/src/main/java/com/example/petstore/PetApi.java[tag=appendix-goal-generate-openapi-java-001,indent=0] ---- + Model types are emitted as fully qualified names (the API interface lives in `` and models under `.model`) so the generator never needs to track imports or worry about @@ -102,16 +85,6 @@ collisions between an API class name and a same-named model. Call sites use the static factory: -[source, java] ----- -PetApi api = PetApi.of("https://petstore3.swagger.io/api/v3"); -api.getPetById(123L, "MY_TOKEN", response -> { - if (response.getResponseCode() == 200) { - Pet pet = response.getResponseData(); - renderPet(pet); - } -}); ----- The `ApiImpl` class that actually performs the HTTP call lives in `target/generated-sources` -- the project source never references diff --git a/docs/developer-guide/appendix_goal_guibuilder.adoc b/docs/developer-guide/appendix_goal_guibuilder.adoc index 4cb72447f8f..e760d2f6986 100644 --- a/docs/developer-guide/appendix_goal_guibuilder.adoc +++ b/docs/developer-guide/appendix_goal_guibuilder.adoc @@ -7,7 +7,7 @@ The `guibuilder` goal opens the Codename One GUI builder to edit a specified GUI [source,bash] ---- -mvn cn1:guibuilder -DclassName=com.example.MyForm +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-guibuilder.sh[tag=appendix-goal-guibuilder-bash-001,indent=0] ---- This will open the GUI builder to edit the form whose class is `com.example.MyForm`. diff --git a/docs/developer-guide/appendix_goal_install_cn1lib.adoc b/docs/developer-guide/appendix_goal_install_cn1lib.adoc index 76fb9ab1215..edfde813d66 100644 --- a/docs/developer-guide/appendix_goal_install_cn1lib.adoc +++ b/docs/developer-guide/appendix_goal_install_cn1lib.adoc @@ -10,7 +10,7 @@ NOTE: Using this goal explicitly is a last resort. The best solution for install [source,bash] ---- -mvn cn1:install-cn1lib -Dfile=/path/to/MyLegacyLib.cn1lib +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-install-cn1lib.sh[tag=appendix-goal-install-cn1lib-bash-001,indent=0] ---- This will generate a Maven pom project for this lib inside the "cn1libs" directory, and it will add a dependency inside the common/pom.xml file. diff --git a/docs/developer-guide/appendix_goal_test.adoc b/docs/developer-guide/appendix_goal_test.adoc index cdb4c929087..653c7895e44 100644 --- a/docs/developer-guide/appendix_goal_test.adoc +++ b/docs/developer-guide/appendix_goal_test.adoc @@ -6,14 +6,14 @@ This goal is bound to the `test` phase of <> projects, so that [source,bash] ---- -mvn install +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-test.sh[tag=appendix-goal-test-bash-001,indent=0] ---- or [source,bash] ---- -mvn package +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-test.sh[tag=appendix-goal-test-bash-002,indent=0] ---- Then this goal will be executed automatically. diff --git a/docs/developer-guide/appendix_goal_update.adoc b/docs/developer-guide/appendix_goal_update.adoc index fde4e70c782..72fbab5ba54 100644 --- a/docs/developer-guide/appendix_goal_update.adoc +++ b/docs/developer-guide/appendix_goal_update.adoc @@ -7,7 +7,7 @@ Updates Codename One. This will update the Codename One tools that live inside ` [source,bash] ---- -mvn cn1:update +include::../demos/common/src/main/snippets/developer-guide/appendix-goal-update.sh[tag=appendix-goal-update-bash-001,indent=0] ---- **Parameters** diff --git a/docs/developer-guide/basics.asciidoc b/docs/developer-guide/basics.asciidoc index 1ab277d2bbc..d1f3428fecf 100644 --- a/docs/developer-guide/basics.asciidoc +++ b/docs/developer-guide/basics.asciidoc @@ -55,11 +55,6 @@ For example, https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLa You can define a group of components to have the same preferred width or height by using the `setSameWidth` and `setSameHeight` methods, for example: -[source,java,title="setSameWidth/Height"] ----- -Component.setSameWidth(cmp1, cmp2, cmp3, cmp4); -Component.setSameHeight(cmp5, cmp6, cmp7); ----- Codename One has a `setPreferredSize` method that lets developers explicitly request a component size. But, that caused many problems. For example, preferred size should change with device orientation or similar operations. The API also encouraged accidental hardcoding of UI values such as forcing pixel sizes. As a result, the method was deprecated. @@ -108,16 +103,11 @@ When you add a `Component` to a `Container` with a regular layout you do so with [source,java,title='Adding to a Regular Container'] ---- -Container cnt = new Container(BoxLayout.y()); -cnt.add(new Label("Just Added")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava002Snippet.java[tag=basics-java-002,indent=0] ---- This works great for regular layouts but might not for constraint based layouts. A constraint based layout accepts another argument. For example: `BorderLayout` needs a location for the `Component`: -[source,java,title='Adding to a Regular Container'] ----- -cnt.add(NORTH, new Label("Just Added")); ----- This line assumes you have an `import static com.codename1.ui.CN.*;` in the top of the file. In `BorderLayout` (which is a constraint based layout) placing an item in the `NORTH` places it in the top of the `Container`. @@ -129,46 +119,25 @@ The `CN` class is a thin wrapper around features in `Display`, `NetworkManager`, [source,java] ---- -import static com.codename1.ui.CN.*; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava004Snippet.java[tag=basics-java-004,indent=0] ---- TIP: That's optional, if you don't like static imports you can just write `CN.` for every element From that point on you can write code that looks like this: -[source,java] ----- -callSerially(() -> runThisOnTheEDT()); ----- Instead of: -[source,java] ----- -Display.getInstance().callSerially(() -> runThisOnTheEDT()); ----- The same applies for most network manager calls e.g.: -[source,java] ----- -addToQueue(myConnectionRequest); ----- Instead of: -[source,java] ----- -NetworkManager.getInstance().addToQueue(myConnectionRequest); ----- Some things were changed so you won't have too many conflicts for example, `Log.p` or `Log.e` would have been problematic so you now have: -[source,java] ----- -log("my log message"); -log(myException); ----- Instead of `Display.getInstance().getCurrent()` you now have `getCurrentForm()` since `getCurrent()` is too generic. For most methods you should just be able to remove the `NetworkManager` or `Display` access and it should "just work." @@ -187,13 +156,7 @@ Almost every layout allows you to `add` a component using many variants of the a [source,java,title='Versions of add'] ---- -Container cnt = new Container(BoxLayout.y()); -cnt.add(new Label("Just Added")); // <1> -cnt.addAll(new Label("Adding Multiple"), // <2> - new Label("Second One")); - -cnt.add(new Label("Chaining")). // <3> - add(new Label("Value")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava010Snippet.java[tag=basics-java-010,indent=0] ---- <1> Regular add @@ -204,17 +167,6 @@ cnt.add(new Label("Chaining")). // <3> In the race to make code "`tighter`" you can make this even shorter. Most layout managers have their own custom terse syntax style for example: -[source,java,title='Terse Syntax'] ----- -Container boxY = BoxLayout.encloseY(cmp1, cmp2); // <1> -Container boxX = BoxLayout.encloseX(cmp3, cmp4); -Container flowCenter = FlowLayout. // <2> - encloseCenter(cmp5, cmp6); ----- - -<1> Most layouts have a version of enclose to encapsulate components within (((Encapsulate))) - -<2> `FlowLayout` has variants that support aligning the components on various axis To sum this up, you can use layout managers and nesting to create elaborate UI's that implicitly adapt to different screen sizes and device orientation. @@ -228,25 +180,14 @@ link:https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.htm [source,java] ---- -Form hi = new Form("Flow Layout", new FlowLayout()); -hi.add(new Label("First")). - add(new Label("Second")). - add(new Label("Third")). - add(new Label("Fourth")). - add(new Label("Fifth")); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava012Snippet.java[tag=basics-java-012,indent=0] ---- Flow layout also supports terse syntax shorthand such as: [source,java] ---- -Container flowLayout = FlowLayout.encloseIn( - new Label("First"), - new Label("Second"), - new Label("Third"), - new Label("Fourth"), - new Label("Fifth"))); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava013Snippet.java[tag=basics-java-013,indent=0] ---- Flow layout can be aligned to the left (the default), to the <>, or to the <>. It can also be vertically aligned to the top (the default), <>, or bottom. @@ -275,12 +216,7 @@ You can create a box layout Y using something like this: [source,java] ---- -Form hi = new Form("Box Y Layout", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add(new Label("First")). - add(new Label("Second")). - add(new Label("Third")). - add(new Label("Fourth")). - add(new Label("Fifth")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava014Snippet.java[tag=basics-java-014,indent=0] ---- Which results in <> @@ -293,11 +229,7 @@ Box layout also supports a shorter terse notation which you use here to <> tries to show some unique things you can do with constraints. [[table-layout-constraint-sample]] -[source,java] ----- -TableLayout tl = new TableLayout(2, 3); // <1> -Form hi = new Form("Table Layout Cons", tl); -hi.setScrollable(false); // <2> -hi.add(tl.createConstraint(). // <3> - widthPercentage(20), - new Label("AAA")). - - add(tl.createConstraint(). // <4> - horizontalSpan(2). - heightPercentage(80). - verticalAlign(Component.CENTER). - horizontalAlign(Component.CENTER), - new Label("Span H")). - - add(new Label("BBB")). - - add(tl.createConstraint(). - widthPercentage(60). - heightPercentage(20), - new Label("CCC")). - - add(tl.createConstraint(). - widthPercentage(20), - new Label("DDD")); ----- - -<1> You need the `TableLayout` instance to create constraints. A constraint must be created for every component and must be used with the same layout as the parent container -<2> To get the look in the <> you need to turn scrolling off so the height constraint doesn't take up available height. Otherwise it will miscalculate available height due to scrolling. You can scroll a `TableLayout` but sizing will be different - -<3> You create the constraint and instantly apply width to it. This is a shorthand syntax for the <> - -<4> You can chain constraint creation using a call like this so many constraints apply to a single cell. Notice that you don't span and set width on the same axis (horizontal span + width), doing something like that would create confusing behavior Here is the full code mentioned in item 3: [[full-code-tablelayout-constraint]] -[source,java] ----- -TableLayout.Constraint cn = tl.createConstraint(); -cn.setWidthPercentage(20); -hi.add(cn, new Label("AAA")). ----- [[table-layout-constraints]] .TableLayout constraints can be used to create elaborate UI's @@ -562,19 +414,7 @@ For example: this is a sample usage of `TextModeLayout`: [source,java] ---- -TextModeLayout tl = new TextModeLayout(3, 2); -Form f = new Form("Pixel Perfect", tl); -TextComponent title = new TextComponent().label("Title"); -TextComponent price = new TextComponent().label("Price"); -TextComponent location = new TextComponent().label("Location"); -TextComponent description = new TextComponent().label("Description").multiline(true); - -f.add(tl.createConstraint().horizontalSpan(2), title); -f.add(tl.createConstraint().widthPercentage(30), price); -f.add(tl.createConstraint().widthPercentage(70), location); -f.add(tl.createConstraint().horizontalSpan(2), description); -f.setEditOnShow(title.getField()); -f.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava024Snippet.java[tag=basics-java-024,indent=0] ---- .TextModeLayout on iOS @@ -604,11 +444,6 @@ image::img/layered-layout.png[The X on this button was placed there using the la The code to generate this UI is slightly complex and contains few relevant pieces. The truly relevant piece is this block: -[source,java] ----- -hi.add(LayeredLayout.encloseIn(settingsLabel, - FlowLayout.encloseRight(close))); ----- You're doing three distinct things here: @@ -622,22 +457,7 @@ This is the full source of the example for completeness: [source,java] ---- -Form hi = new Form("Layered Layout"); -int w = Math.min(Display.getInstance().getDisplayWidth(), Display.getInstance().getDisplayHeight()); -Button settingsLabel = new Button(""); -Style settingsStyle = settingsLabel.getAllStyles(); -settingsStyle.setFgColor(0xff); -settingsStyle.setBorder(null); -settingsStyle.setBgColor(0xff00); -settingsStyle.setBgTransparency(255); -settingsStyle.setFont(settingsLabel.getUnselectedStyle().getFont().derive(w / 3, Font.STYLE_PLAIN)); -FontImage.setMaterialIcon(settingsLabel, FontImage.MATERIAL_SETTINGS); -Button close = new Button(""); -close.setUIID("Container"); -close.getAllStyles().setFgColor(0xff0000); -FontImage.setMaterialIcon(close, FontImage.MATERIAL_CLOSE); -hi.add(LayeredLayout.encloseIn(settingsLabel, - FlowLayout.encloseRight(close))); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava026Snippet.java[tag=basics-java-026,indent=0] ---- Forms have a built in layered layout that you can access through `getLayeredPane()`, this allows you to overlay elements on top of the content pane. @@ -655,11 +475,7 @@ As an example, suppose you wanted to position a button in the lower right corner [source,java] ---- -Container cnt = new Container(new LayeredLayout()); -Button btn = new Button("Submit"); -LayeredLayout ll = (LayeredLayout)cnt.getLayout(); -cnt.add(btn); -ll.setInsets(btn, "auto 0 0 auto"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava027Snippet.java[tag=basics-java-027,indent=0] ---- The result is: @@ -668,10 +484,6 @@ image::img/guibuilder-2-insets-1.png[Button positioned in bottom right using ins The thing new here is this line: -[source,java] ----- -ll.setInsets(btn, "auto 0 0 auto"); ----- This is called after `btn` has already been added to the container. It says that you want its insets to be "auto" on the top and left, and `0` on the right and bottom. This *insets* string follows the CSS notation of `top right bottom left` (that's: start on top and go clockwise), and the values of each inset may be provided in pixels (px), millimetres (mm), percent (%), or the special "auto" value. Like CSS, you can also specify the insets using a 1, 2, or 3 values. For example: @@ -688,10 +500,6 @@ NOTE: The "inset bounding box" is the containing box from which a component's in If one inset is fixed (that's: defined in px, mm, or %), and the opposite inset is "auto," then the "auto" inset will allow the component to be its preferred size. If you want to position a component to be centered vertically, and 5mm from the left edge, you could do: -[source,java] ----- -ll.setInsets(btn, "auto auto auto 5mm"); ----- Resulting in: @@ -700,10 +508,6 @@ image::img/guibuilder-2-insets-2.png[Button vertically centered 5mm from left ed Move it to the right edge with: -[source,java] ----- -ll.setInsets(btn, "auto 5mm auto auto"); ----- ===== `%` insets @@ -733,14 +537,7 @@ For example, suppose you want to place a text field in the center of the form (b [source,java] ---- -Container cnt = new Container(new LayeredLayout()); -LayeredLayout ll = (LayeredLayout)cnt.getLayout(); -Button btn = new Button("Submit"); -TextField tf = new TextField(); -cnt.add(tf).add(btn); -ll.setInsets(tf, "auto") - .setInsets(btn, "auto auto auto 0") - .setReferenceComponentLeft(btn, tf, 1f); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava031Snippet.java[tag=basics-java-031,indent=0] ---- This would result in: @@ -750,13 +547,6 @@ image::img/guibuilder-2-insets-3.png[Button's left inset linked to text field,sc The two active lines here are the last two: -[source,java] ----- - .setInsets(btn, "auto auto auto 0") //<1> - .setReferenceComponentLeft(btn, tf, 1f); //<2> ----- -<1> Sets the left inset on `btn` to 0. -<2> Links `btn`'s left inset to `tf` so that it's measured from the text field. The third parameter (`1.0`) is the reference position. This will either be `0` (meaning the reference point is the left edge of the text field), or `1` (meaning the reference point is the right edge of the text field). In this case you set a reference position of `1.0` because you want the button to be aligned to the text field's right edge. NOTE: The reference position is defined as the distance, expressed as a fraction of the reference component's length on the inset's axis, between the reference component's leading (outer) edge and the point from which the inset is measured. A reference position of 0 means that the inset is measured from the leading edge of the reference component. A value of 1.0 means that the inset is measured from the trailing edge of the reference component. A value of 0.5 means that the inset is measured from the center of the reference component. Etc. Any floating point value can be used, though the most common values are 0 and 1. @@ -802,51 +592,7 @@ To show `GridBagLayout` you ported http://docs.oracle.com/Java SE/tutorial/uiswi [source,java] ---- -Button button; -hi.setLayout(new GridBagLayout()); -GridBagConstraints c = new GridBagConstraints(); -//natural height, maximum width -c.fill = GridBagConstraints.HORIZONTAL; -button = new Button("Button 1"); -c.weightx = 0.5; -c.fill = GridBagConstraints.HORIZONTAL; -c.gridx = 0; -c.gridy = 0; -hi.addComponent(c, button); - -button = new Button("Button 2"); -c.fill = GridBagConstraints.HORIZONTAL; -c.weightx = 0.5; -c.gridx = 1; -c.gridy = 0; -hi.addComponent(c, button); - -button = new Button("Button 3"); -c.fill = GridBagConstraints.HORIZONTAL; -c.weightx = 0.5; -c.gridx = 2; -c.gridy = 0; -hi.addComponent(c, button); - -button = new Button("Long-Named Button 4"); -c.fill = GridBagConstraints.HORIZONTAL; -c.ipady = 40; //make this component tall -c.weightx = 0.0; -c.gridwidth = 3; -c.gridx = 0; -c.gridy = 1; -hi.addComponent(c, button); - -button = new Button("5"); -c.fill = GridBagConstraints.HORIZONTAL; -c.ipady = 0; //reset to default -c.weighty = 1.0; //request any extra vertical space -c.anchor = GridBagConstraints.PAGE_END; //bottom of space -c.insets = new Insets(10,0,0,0); //top padding -c.gridx = 1; //aligned with button 2 -c.gridwidth = 2; //2 columns wide -c.gridy = 2; //third row -hi.addComponent(c, button); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava033Snippet.java[tag=basics-java-033,indent=0] ---- Notice that because of the way gridbag works you didn't provide any terse syntax API for it although it should be possible. @@ -866,70 +612,7 @@ Unlike any other layout manager `GroupLayout` adds the components into the conta [source,java] ---- -Form hi = new Form("GroupLayout"); - -Label label1 = new Label(); -Label label2 = new Label(); -Label label3 = new Label(); -Label label4 = new Label(); -Label label5 = new Label(); -Label label6 = new Label(); -Label label7 = new Label(); - -label1.setText("label1"); - -label2.setText("label2"); - -label3.setText("label3"); - -label4.setText("label4"); - -label5.setText("label5"); - -label6.setText("label6"); - -label7.setText("label7"); - -GroupLayout layout = new GroupLayout(hi.getContentPane()); -hi.setLayout(layout); -layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.LEADING) - .add(layout.createSequentialGroup() - .addContainerGap() - .add(layout.createParallelGroup(GroupLayout.LEADING) - .add(layout.createSequentialGroup() - .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(layout.createParallelGroup(GroupLayout.LEADING) - .add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) - .add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .add(layout.createSequentialGroup() - .add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))) - .addContainerGap(296, Short.MAX_VALUE)) -); -layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.LEADING) - .add(layout.createSequentialGroup() - .addContainerGap() - .add(layout.createParallelGroup(GroupLayout.TRAILING) - .add(label2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(LayoutStyle.RELATED) - .add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(layout.createParallelGroup(GroupLayout.LEADING) - .add(label6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .add(label7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addContainerGap(150, Short.MAX_VALUE)) -); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava034Snippet.java[tag=basics-java-034,indent=0] ---- .GroupLayout Matisse generated UI running in Codename One @@ -950,19 +633,6 @@ The API was deprecated to serve as a warning of its experimental status. The best reference for MiG would probably be its http://www.miglayout.com/QuickStart.pdf[quick start guide (PDF link)]. As a reference you ported one of the samples from that PDF to Codename One: -[source,java] ----- -Form hi = new Form("MigLayout", new MigLayout("fillx,insets 0")); - -hi.add(new Label("First")). - add("span 2 2", new Label("Second")). // The component will span 2×2 cells. - add("wrap", new Label("Third")). // Wrap to next row - add(new Label("Forth")). - add("wrap", new Label("Fifth")). // Note that it "jumps over" the occupied cells. - add(new Label("Sixth")). - add(new Label("Seventh")); -hi.show(); ----- .MiG layout sample ported to Codename One image::img/mig-layout.png[MiG layout sample ported to Codename One,scaledwidth=20%] @@ -986,10 +656,6 @@ Themes consist of a set of UIID definitions. Every component in Codename One has For example: see this code where: -[source,java,title='setUIID on TextField'] ----- -nameText.setUIID("Label"); ----- This is a text field component (user input field) but it will look like a `Label`. @@ -997,10 +663,6 @@ Effectively you told the text field that it should use the UIID of `Label` when The UIID's translate the theme elements into a set of `Style` objects. These `Style` objects get their initial values from the theme but can be further manipulated after the fact. To make the text field's foreground color red you could use this code: -[source,java,title='setUIID on TextField'] ----- -nameText.getAllStyles().setFgColor(0xff0000); ----- The color is in hexadecimal `RRGGBB` format so `0xff00` would be green and `0xff0000` would be red. @@ -1033,7 +695,7 @@ You load the theme file using this line of code in the `init(Object)` method in [source,java,title='Theme Loading Code'] ---- -theme = UIManager.initFirstTheme("/theme"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava038Snippet.java[tag=basics-java-038,indent=0] ---- In a CSS project this file is generated automatically from the stylesheet. Legacy applications that still edit the resource file by hand can continue to do so, but new projects should prefer the CSS workflow described below. @@ -1066,7 +728,7 @@ Both goals accept a `className` parameter that points at the fully qualified cla [source,bash] ---- -mvn cn1:create-gui-form -DclassName=com.example.MyForm +include::../demos/common/src/main/snippets/developer-guide/basics.sh[tag=basics-bash-001,indent=0] ---- This generates two files inside the `common` module: @@ -1080,7 +742,7 @@ To open the form in the GUI builder: [source,bash] ---- -mvn cn1:guibuilder -DclassName=com.example.MyForm +include::../demos/common/src/main/snippets/developer-guide/basics.sh[tag=basics-bash-002,indent=0] ---- The full goal reference, including every parameter, lives in <> and <> in the Maven goals appendix. @@ -1157,8 +819,7 @@ Once an event is bound the IDE will open to the event code for example: [source,java] ---- -public void onButton_1ActionEvent(com.codename1.ui.events.ActionEvent ev) { -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava039Snippet.java[tag=basics-java-039,indent=0] ---- TIP: Some IDEs generate the project source code after you explicitly build the project so if your code needs to access variables etc. try building first @@ -1168,7 +829,7 @@ UI is represented as: [source,java] ---- -private com.codename1.ui.Button gui_Button_1 = new com.codename1.ui.Button(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava040Snippet.java[tag=basics-java-040,indent=0] ---- ===== Underlying XML @@ -1185,90 +846,14 @@ You can edit the GUI file directly but changes won't map into the GUI builder un [source,xml] ---- - - - - - - - - +include::../demos/common/src/main/snippets/developer-guide/basics.xml[tag=basics-xml-001,indent=0] ---- This format is simple. The file triggers the following Java source file: [source,java] ---- -package com.mycompany.myapp; - -/** - * GUI builder created Form - * - * @author shai - */ -public class MyForm extends com.codename1.ui.Form { - - public MyForm() { - this(com.codename1.ui.util.Resources.getGlobalResources()); - } - - public MyForm(com.codename1.ui.util.Resources resourceObjectInstance) { - initGuiBuilderComponents(resourceObjectInstance); - } - -//-- DON'T EDIT BELOW THIS LINE!!! - private com.codename1.ui.Label gui_Label_1 = new com.codename1.ui.Label(); - private com.codename1.ui.Button gui_Button_1 = new com.codename1.ui.Button(); - - -// - private void guiBuilderBindComponentListeners() { - EventCallbackClass callback = new EventCallbackClass(); - gui_Button_1.addActionListener(callback); - } - - class EventCallbackClass implements com.codename1.ui.events.ActionListener, com.codename1.ui.events.DataChangedListener { - private com.codename1.ui.Component cmp; - public EventCallbackClass(com.codename1.ui.Component cmp) { - this.cmp = cmp; - } - - public EventCallbackClass() { - } - - public void actionPerformed(com.codename1.ui.events.ActionEvent ev) { - com.codename1.ui.Component sourceComponent = ev.getComponent(); - if(sourceComponent.getParent().getLeadParent() != null) { - sourceComponent = sourceComponent.getParent().getLeadParent(); - } - - if(sourceComponent == gui_Button_1) { - onButton_1ActionEvent(ev); - } - } - - public void dataChanged(int type, int index) { - } - } - private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) { - guiBuilderBindComponentListeners(); - setLayout(new com.codename1.ui.layouts.FlowLayout()); - setTitle("My new title"); - setName("MyForm"); - addComponent(gui_Label_1); - addComponent(gui_Button_1); - gui_Label_1.setText("Hi World"); - gui_Label_1.setName("Label_1"); - gui_Button_1.setText("Click Me"); - gui_Button_1.setName("Button_1"); - }// - -//-- DON'T EDIT ABOVE THIS LINE!!! - public void onButton_1ActionEvent(com.codename1.ui.events.ActionEvent ev) { - } - -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava041Snippet.java[tag=basics-java-041,indent=0] ---- WARNING: Don't touch the code within the don't EDIT comments... diff --git a/docs/developer-guide/component-selector.asciidoc b/docs/developer-guide/component-selector.asciidoc index c3db8024f1d..55332701317 100755 --- a/docs/developer-guide/component-selector.asciidoc +++ b/docs/developer-guide/component-selector.asciidoc @@ -19,20 +19,7 @@ The following snippet creates a button, that, when pressed, will fade itself and [source,java] ---- -import static com.codename1.ui.ComponentSelector.$; - -// ... - -Button slideUp = $(new Button("Slide Up")) // <1> - .setIcon(FontImage.MATERIAL_EXPAND_LESS) // <2> - .addActionListener(e->{ // <3> - $(e) // <4> - .getParent() // <5> - .find(">*") // <6> - .slideUpAndWait(1000) // <7> - .slideDownAndWait(1000); // <8> - }) - .asComponent(Button.class); // <9> +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/ComponentSelectorJava001Snippet.java[tag=component-selector-java-001,indent=0] ---- <1> - Creates a new Button and wraps it in a `ComponentSelector` so that you can use ComponentSelector's fluent API to change the button. <2> - Sets the icon on the button @@ -91,30 +78,6 @@ Some examples above mention the use of "tags." Tags are analogous to "classes" i Here is an example that uses tags to implement table striping so that even rows are a different color than odd rows in a table: -[source,java] ----- -TableLayout tl = new TableLayout(numRows, numCols); -Container table = new Container(tl); -int rowNum = 0; -int colNum = 0; -for (String[] row : data) { - colNum = 0; - for (String cell : row) { - table.add( - tl.createConstraint(rowNum, colNum), - $(new Button(cell)) - .setUIID("Label") - .addTags(rowNum % 2 == 0 ? "even":"odd") - .asComponent() - ); - colNum++; - } - rowNum++; -} -$(".even", table) - .setBgColor(0xcccccc) - .setBgTransparency(255); ----- image::img/component-selector-table-striping.png[Table striping screenshot] @@ -124,40 +87,15 @@ In the above example you add a tag to each label when you add it to the table or ComponentSelector includes wrappers for most of the methods of `com.codename1.ui.plaf.Style` so that you can change the styles of selected components using a fluent API. This was demonstrated a little bit in the table striping example (`setBgColor()` and `setBgTransparency()` were called on even rows of the table). It's worth going over this in a little more detail though as Codename One styles have "state." For example, For the snippet of code: -[source,java] ----- -$(".even", table) - .setBgColor(0xcccccc) - .setBgTransparency(255); ----- Were you setting these values on the "selected" style, the "unselected" style, the "disabled" style, "ALL" styles? In fact you were setting these values on the "current" style. That is, the call to `setBgColor()` caused something like the following loop to happen on all the components in the set: -[source,java] ----- -for (Component c : evenComponents) { - c.getStyle().setBgColor(0xcccccc); -} ----- If a component was in "selected" state, then this would changed the selected style. If it was in pressed state, then it would change the "pressed" style. Etc. What if you wanted to specifically change the styles in the "pressed" state. Then you would call `selectPressedStyle()` prior calling your style mutation methods. For example: -[source,java] ----- -$(".even", table) - .selectPressedStyle() - .setBgColor(0xcccccc) - .setBgTransparency(255); ----- Or, ComponentSelector supports a "state" pseudo-selector that will initialize the selected style so that you don't have to call `selectXXXStyle()` before making changes. For example, The following snippet is equivalent to the previous: -[source,java] ----- -$(".even:pressed", table) - .setBgColor(0xcccccc) - .setBgTransparency(255); ----- The following "state" pseudo-selectors are available: @@ -184,32 +122,6 @@ Also to these basic effects, ComponentSelector wraps all the existing animation For example, Consider this example, that shows a button that replaces itself and all siblings with replacement labels, and then replaces them back: -[source,java] ----- -Button replace = $(new Button("Replace Fade/Slide")) - .setIcon(FontImage.MATERIAL_REDEEM) - .addActionListener(e->{ - $(e).getParent() - .find(">*") // <1> - .replaceAndWait(c->{ // <2> - return $(new Label("Replacement")) // <3> - .putClientProperty("origComponent", c) // <4> - .asComponent(); - }, CommonTransitions.createFade(1000)) // <5> - .replaceAndWait(c->{ - Component orig = (Component)c.getClientProperty("origComponent"); - if (orig != null) { - c.putClientProperty("origComponent", null); - return orig; // <6> - } - return c; - - }, CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, false, 1000)); // <7> - - - }) - .asComponent(Button.class); ----- <1> Finds all siblings of the source button <2> Call `replaceAndWait()` with a mapping function to define the component that should replace each component. This will replace each component in the set with a replacement component in its respective container. This will also return a new ComponentSelector with the set of replacement components. <3> In your "mapper" callback, you will return a new Label component to replace each existing component. diff --git a/docs/developer-guide/css.asciidoc b/docs/developer-guide/css.asciidoc index dc7aa123755..00d4cae0031 100644 --- a/docs/developer-guide/css.asciidoc +++ b/docs/developer-guide/css.asciidoc @@ -42,15 +42,7 @@ The following example creates a simple button with a border, and text aligned ce [source,css] ---- -Button { - text-align: center; - border: 1pt solid gray; - background-color: transparent; -} - -Button.pressed { - background-color: gray; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-001,indent=0] ---- ==== Inheriting properties using `cn1-derive` @@ -59,10 +51,7 @@ The following example defines a custom Button style named "MyButton" that inheri [source,css] ---- -MyButton { - cn1-derive: Button; - background-color: blue; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-002,indent=0] ---- === Special selectors @@ -73,11 +62,7 @@ The `#Device` selector allows you to define which device resolutions this CSS fi [source,css] ---- -#Device { - min-resolution: 120dpi; - max-resolution: 480dpi; - resolution: 480dpi; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-003,indent=0] ---- ==== `#constants` @@ -88,67 +73,7 @@ For example: [source,css] ---- -#Constants { - PopupDialogArrowBool: false; - calTitleDayStyleBool: true; - calTransitionVertBool: false; - calendarLeftImage: "calendar-arrow-left.png"; - calendarRightImage: "calendar-arrow-right.png"; - centeredPopupBool: false; - checkBoxCheckDisFocusImage: "Check-Box_Normal.png"; - checkBoxCheckedFocusImage: "Check-Box_Press.png"; - checkBoxCheckedImage: "Check-Box_Press.png"; - checkBoxOppositeSideBool: true; - checkBoxUncheckedFocusImage: "Check-Box_Normal.png"; - checkBoxUncheckedImage: "Check-Box_Normal.png"; - comboImage: "combo.png"; - commandBehavior: "Side"; - dialogTransitionIn: "fade"; - dialogTransitionOut: "fade"; - dlgButtonCommandUIID: "DialogButton"; - dlgCommandGridBool: true; - dlgInvisibleButtons: #1a1a1a; - formTransitionIn: "empty"; - formTransitionOut: "slide"; - includeNativeBool: true; - menuImage: "of_menu.png"; - noTextModeBool: true; - onOffIOSModeBool: true; - otherPopupRendererBool: false; - pureTouchBool: true; - radioSelectedFocusImage: "Radio_btn_Press.png"; - radioSelectedImage: "Radio_btn_Press.png"; - radioUnselectedFocusImage: "Radio_btn_Normal.png"; - radioUnselectedImage: "Radio_btn_Normal.png"; - sideMenuImage: "menu.png"; - switchMaskImage: "switch-mask-3.png"; - switchOffImage: "switch-off-3.png"; - switchOnImage: "switch-on-3.png"; - tabPlacementInt: 0; - backIconImage: "Back-icon.png"; - articleSourceIconImage: "Source-icon.png"; - articleDateIconImage: "Date-icon.png"; - articleArrowRightImage: "Arrow-right.png"; - articleShareIconImage: "Share-icon.png"; - articleBookmarkIconImage: "Bookmark-icon.png"; - articleTextIconImage: "Text-icon.png"; - articleCommentsIconImage: "Comments-icon.png"; - newsIconImage: "News-icon.png"; - channelsIconImage: "Channels-icon.png"; - bookmarksIconImage: "Bookmarks-icon.png"; - overviewIconImage: "Overview-icon.png"; - calendarIconImage: "Calendar-icon.png"; - timelineIconImage: "Timeline-icon.png"; - profileIconImage: "Profile-icon.png"; - widgetsIconImage: "Widgets-icon.png"; - settingsIconImage: "Settings-icon.png"; - SubmitIconImage: "Submit-icon.png"; - SubmitIconDarkImage: "SubmitButtonLight-icon.png"; - defaultFontSizeInt: 18; - defaultDesktopFontSizeInt: 14; - defaultSourceDPIInt: "0"; - -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-004,indent=0] ---- In the above example, the constants referring to an image name as a string requires that the image exists in one of the following locations: @@ -206,7 +131,7 @@ As of CodenameOne 7.0, you can use variables in your CSS file via the `var()` CS [source,css] ---- -var(--header-color, blue); +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-005,indent=0] ---- The `var()` function can be used inside property values. That is, You can't use it in property names or selectors. @@ -215,7 +140,7 @@ The `var()` function can be used inside property values. That is, You can't use [source,css] ---- -var(, ?) +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-006,indent=0] ---- The `` must begin with two dashes (`--`). @@ -227,25 +152,13 @@ The `` is the fallback value that will be used if the variabl .Example defining and using a CSS variable [source,css] ---- -#Constants { - --main-bg-color: red; -} - -MyContainer { - background-color: var(--main-bg-color); -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-007,indent=0] ---- .Example using a fallback value [source,css] ---- -#Constants { - --main-bg-color: red; -} - -MyContainer { - background-color: var(--main-bg-color, blue); -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-008,indent=0] ---- See the https://developer.mozilla.org/en-You/docs/Web/CSS/var[MDN docs] for more details about the CSS variable spec. @@ -313,47 +226,21 @@ Rounded borders can be achieved in a few different ways. The easiest methods are [source,css] ---- -RoundBorder { - border: 1px #3399ff cn1-round-border; - text-align:center; - margin:2mm; - padding:3mm; -} - -RoundBorderFilled { - background: cn1-round-border; - background-color: #ccc; - text-align:center; - margin:2mm; - padding:3mm; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-009,indent=0] ---- **Examples using `cn1-pill-border`** [source,css] ---- -PillBorder { - border: 1pt #3399ff cn1-pill-border; - text-align:center; -} - -PillBorderFilled { - background: cn1-pill-border; - background-color: #3399ff; - color:white; - text-align:center; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-010,indent=0] ---- **Examples using `border-radius`** [source,css] ---- -RoundRectLabel { - background-color: red; - border-radius: 2mm; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-011,indent=0] ---- `cn1-pill-border` and `cn1-round-border` don't support the standard CSS `box-shadow` property. This is because the `box-shadow` property parameters don't map onto the shadow parameters for the Codename One `RoundBorder` class. To get shadows on the `cn1-pill-border`, you should use one or more of the following CSS properties: @@ -371,11 +258,7 @@ Codename One also exposes per-corner elliptical radius controls that map to the [source,css] ---- -EllipticalBorder { - border-radius: 2mm 4mm 6mm 1mm / 1mm 3mm 5mm 7mm; - cn1-box-shadow-spread: 1.5mm; - cn1-box-shadow-inset: inset; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-012,indent=0] ---- In the example above, the four horizontal radii (`2mm 4mm 6mm 1mm`) populate the `cn1-border-*-radius-x` properties clockwise from the top-left corner. The four values after the slash fill the matching `cn1-border-*-radius-y` entries. Setting `cn1-box-shadow-inset: inset;` converts the shadow into an inset glow that follows the same elliptical curvature. @@ -406,21 +289,7 @@ Any angle in degrees, radians, or `turn`, or the canonical `to ` / `to ` (CSS convention: 0° points [source,css] ---- -MyContainer { background: conic-gradient(red, yellow, green, blue, red); } -MyContainer { background: conic-gradient(from 45deg at 50% 50%, #f06 0%, #fc0 25%, #0c6 50%, #06f 75%, #f06 100%); } +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-015,indent=0] ---- **`repeating-linear-gradient` / `repeating-radial-gradient`** @@ -450,8 +316,7 @@ Identical syntax to their non-repeating counterparts. The stop pattern tiles out [source,css] ---- -MyStripe { background: repeating-linear-gradient(45deg, #eee 0px, #eee 10px, #ccc 10px, #ccc 20px); } -MyTarget { background: repeating-radial-gradient(circle at center, #fff, #fff 8px, #c33 8px, #c33 16px); } +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-016,indent=0] ---- ===== filter and backdrop-filter @@ -463,26 +328,7 @@ CSS `filter` and `backdrop-filter` accept a chain of functions. Two storage form [source,css] ---- -MyOverlay { - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(12px); -} - -MyBlurredImage { - filter: blur(4px); -} - -MyFaded { - filter: brightness(0.7) contrast(1.15); -} - -MyGrayscale { - filter: grayscale(1); -} - -MySepia { - filter: sepia(0.8) saturate(1.1); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-017,indent=0] ---- .`filter: blur()` applied at a graphics primitive level — the framework's screenshot test renders RGB stripes and a gradient, then blurs both via `Graphics.gaussianBlur(...)`. Component-level paint-time integration of `filter:` declarations is in progress; until it lands, set the radius / matrix on a `Style` and consume them via the corresponding `Graphics` primitives manually. @@ -534,10 +380,7 @@ Some images have this density information embedded in the image itself so that t [source,css] ---- -SomeStyle { - background-image: url(images/my-image.png); - cn1-source-dpi: 160; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-018,indent=0] ---- NOTE: `cn1-source-dpi` values are meant to fall into threshold ranges. Values less than or equal to 120, are interpreted as low density. 121 - 160 are medium density (iPhone 3GS). 161 - 320, high density (iPhone 4S). 321 - 480 == HD. 481 and higher == 2HD. In general, you should try to use images that are one of these DPIs exactly: 160, 320, or 480, then images will be scaled up or down to the other densities accordingly. @@ -548,10 +391,7 @@ By default all images are imported as multi-images (unless you define the `defau [source,css] ---- -SomeStyle { - background-image: url(images/my-image.png); - cn1-source-dpi: 0; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-019,indent=0] ---- [NOTE] @@ -560,9 +400,7 @@ You can change the default source DPI for the whole stylesheet by adding `defaul [source,css] ---- -#Constants { - defaultSourceDPIInt: 0; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-020,indent=0] ---- Most application templates in https://www.codenameone.com/initializr[Codename One Initializr] include this constant by default. @@ -587,9 +425,7 @@ For example, Given the CSS directives: [source,css] ---- -MyStyle { - background-image: url(images/mymultiimage.png); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-021,indent=0] ---- The files would look like: @@ -639,41 +475,14 @@ It's quite useful to be able to embed images inside the resource file that's gen [source,css] ---- -Images { - background-image: url(images/NowLogo.png), - url(images/Username-icon.png), - url(images/Password-icon.png), - url(images/Name-icon.png), - url(images/Email-icon.png), - url(images/SeaIce.png), - url(images/Back-icon.png), - url(images/Source-icon.png), - url(images/Date-icon.png), - url(images/Arrow-right.png), - url(images/Share-icon.png), - url(images/Text-icon.png), - url(images/Comments-icon.png), - url(images/RedPlanet.png), - url(images/News-icon.png), - url(images/Channels-icon.png), - url(images/Bookmarks-icon.png), - url(images/Overview-icon.png), - url(images/Calendar-icon.png), - url(images/Timeline-icon.png), - url(images/Profile-icon.png), - url(images/Widgets-icon.png), - url(images/Settings-icon.png), - url(images/Bookmark-icon.png); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-022,indent=0] ---- Then in Java, you might do something like: [source,java] ---- -Resources theme = Resources.openLayered("/theme"); - -Label bookmark = new Label(theme.getImage("Bookmark-icon.png")); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/CssJava001Snippet.java[tag=css-java-001,indent=0] ---- ==== Loading images from URLs @@ -682,9 +491,7 @@ You can also load images from remote URLs. For example: [source,css] ---- -Images { - background-image: url(http://solutions.weblite.ca/logo.png); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-023,indent=0] ---- ==== Generating 9-Piece Image borders @@ -697,9 +504,7 @@ For example: [source,css] ---- -NinePiece { - border-image:url('dashbg_landscape.png'); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-024,indent=0] ---- @@ -709,26 +514,7 @@ If you want more specific "slice" points, you can add the `border-image-slice` p [source,css] ---- -NinePiece { - border-image:url('dashbg_landscape.png'); - border-image-slice:10% 49%; /*vertical horizontal*/ -} - -NinePiece2 { - border-image:url('dashbg_landscape.png'); - border-image-slice:10% 49% 20%; /*top horizontal bottom*/ -} - -NinePiece3 { - border-image:url('dashbg_landscape.png'); - border-image-slice:10% 30% 40% 20%; /*top right bottom left*/ -} - -NinePiece4 { - border-image:url('dashbg_landscape.png'); - border-image-slice:10%; /*all*/ -} - +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-025,indent=0] ---- ==== Image backgrounds @@ -769,10 +555,7 @@ The potential for confusion is mitigated somewhat, but still exists when using C [source,css] ---- -MyContainer { - background-image: url(myimage.png); - cn1-background-type: cn1-image-scaled-fill; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-026,indent=0] ---- === Image compression @@ -789,9 +572,7 @@ By default, https://www.codenameone.com/blog/good-looking-by-default-native-font [source,css] ---- -SideCommand { - font-family: "native:MainThin"; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-027,indent=0] ---- If you omit the `font-family` directive altogether, it will use `native:MainRegular`. The following native fonts are available: @@ -813,29 +594,21 @@ If you want to use a font other than the built-in fonts, you'll need to define t [source,css] ---- -@font-face { - font-family: "Montserrat"; - src: url(res/Montserrat-Regular.ttf); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-028,indent=0] ---- Then you'll be able to reference the font using the specified `font-family` in any CSS element. For example: [source,css] ---- -MyLabel { - font-family: "Montserrat"; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-029,indent=0] ---- The `@font-face` directive's `src` property will accept both local and remote URLs. For example: [source,css] ---- -@font-face { - font-family: "MyFont"; - src: url(http://example.com/path/to/myfont.ttf); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-030,indent=0] ---- In this case, it will download the `myfont.ttf` file to the same directory as the CSS file. From then on it will use that locally downloaded version of the font so that it doesn't have to make a network request for each build. @@ -848,10 +621,7 @@ Fonts hosted on GitHub are accessible using a special `github:` protocol to make [source,css] ---- -@font-face { - font-family: "FontAwesome"; - src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/fontawesome-webfont.ttf); -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-031,indent=0] ---- NOTE: Apparently FontAwesome has removed its public repositories from GitHub so this URL no longer works. @@ -877,9 +647,7 @@ If you add the following to your stylesheet, it will set the default font size t [source,css] ---- -#Constants { - defaultFontSizeInt: 18; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-032,indent=0] ---- TIP: A value of 18 here gives best results across devices. @@ -888,10 +656,7 @@ On the desktop, you may find that 18 is too big. You can define a default font s [source,css] ---- -#Constants { - defaultFontSizeInt: 18; - defaultDesktopFontSizeInt: 14; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-033,indent=0] ---- **** @@ -916,227 +681,7 @@ See link:Supported-Properties#text-decoration[the text-decoration section] in th [source,css] ---- -@font-face { - font-family: "Montserrat"; - src: url(res/Montserrat-Regular.ttf); -} - -@font-face { - font-family: "Montserrat-Bold"; - src: url(res/Montserrat-Bold.ttf); -} - -@font-face { - font-family: "FontAwesome"; - src: url(github://FontAwesome/Font-Awesome/blob/master/fonts/fontawesome-webfont.ttf); -} - -PlainText0p5mm { - font-size: 0.5mm; -} - -PlainText1mm { - font-size: 1mm; -} - -PlainText2mm { - font-size: 2mm; -} - -PlainText5mm { - font-size: 5mm; -} - -PlainText10mm { - font-size: 10mm; -} - -PlainText50mm { - font-size: 50mm; -} - -PlainTextSmall { - font-size: small; -} - -PlainTextMedium { - font-size: medium; -} - -PlainTextLarge { - font-size: large; -} - -PlainText3pt { - font-size: 3pt; -} - -PlainText6pt { - font-size: 6pt; -} - -PlainText12pt { - font-size: 12pt; -} - -PlainText20pt { - font-size: 20pt; -} - -PlainText36pt { - font-size: 36pt; -} - -BoldText { - font-weight: bold; -} - -BoldText1mm { - font-weight: bold; - font-size: 1mm; -} - -BoldText2mm { - font-weight: bold; - font-size: 2mm; -} - -BoldText3mm { - font-weight: bold; - font-size: 3mm; -} - -BoldText5mm { - font-weight: bold; - font-size: 5mm; -} - -ItalicText { - font-style: italic; -} - -ItalicText3mm { - font-style: italic; - font-size: 3mm; -} - -ItalicBoldText { - font-style: italic; - font-weight: bold; -} - -PlainTextUnderline { - text-decoration: underline; -} - -BoldTextUnderline { - text-decoration: underline; - font-weight: bold; -} - -ItalicTextUnderline { - text-decoration: underline; - font-style: italic; -} - -PlainText3d { - text-decoration: cn1-3d; - color:white; - background-color: #3399ff -} - -BoldText3d { - text-decoration: cn1-3d; - font-weight: bold; - color:white; - background-color: #3399ff; -} - -ItalicText3d { - text-decoration: cn1-3d; - font-style: italic; - color:white; - background-color: #3399ff; -} - -PlainText3dLowered { - text-decoration: cn1-3d-lowered; - color:black; - background-color: #3399ff; -} - -BoldText3dLowered { - text-decoration: cn1-3d-lowered; - font-weight: bold; - color:black; - background-color: #3399ff; -} - -ItalicText3dLowered { - text-decoration: cn1-3d-lowered; - font-style: italic; - color:black; - background-color: #3399ff; -} - -PlainText3dShadow { - text-decoration: cn1-3d-shadow-north; - color:white; - background-color: #3399ff; -} - -BoldText3dShadow { - text-decoration: cn1-3d-shadow-north; - font-weight: bold; - color:white; - background-color: #3399ff; -} - -ItalicText3dShadow { - text-decoration: cn1-3d-shadow-north; - font-style: italic; - color:white; - background-color: #3399ff; -} - - -MainThin { - - font-size: 200%; - background: radial-gradient(circle at top left, yellow, blue 100%); -} - -MainRegular0001 { - font-family: "native:MainRegular"; - /*background: cn1-pill-border; - background-color: red;*/ - color: blue; - border: 1px cn1-pill-border blue; - /*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/ - padding: 2mm; -} - -MainRegular0001.pressed { - font-family: "native:MainRegular"; - background: cn1-pill-border blue; - /*background-color: red;*/ - color: white; - border: 1px solid white; - /*box-shadow: 1mm 1mm 0 2mm rgba(0,0,0,1.0);*/ - padding: 2mm; -} - -Heading { - font-size: 4mm; - font-family: "Montserrat-Bold"; - color: black; - padding: 2mm; - text-align: center; -} - -XMLVIewIcon { - font-family: "FontAwesome"; -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-034,indent=0] ---- === Media queries @@ -1152,41 +697,14 @@ Also to the Codename One specific media tokens above, the CSS compilers also rec .Example: Dark-mode overrides with standard CSS media query syntax [source,css] ---- -Button { - color: #222222; -} - -@media (prefers-color-scheme: dark) { - Button { - color: #f0f0f0; - background-color: #000000; - } - - Button.selected { - color: #ff0000; - } -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-035,indent=0] ---- .Example: Different font colors on Android and iOS. On Android, labels will appear green. On iOS, they will appear red. On all other platforms, they will appear black: [source,css] ---- -Label { - color: black; -} - -@media platform-and { - Label { - color: green; - } -} - -@media platform-ios { - Label { - color: red; - } -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-036,indent=0] ---- .Example: Different font colors based on device density. On lower densities, labels will be green. On higher densities, labels will be red. @@ -1236,26 +754,20 @@ You can combine multiple media queries together, separated by a comma. Queries o .Example: Targeting styles to Android devices with high density [source,css] ---- -@media platform-and, density-high { - .... -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-037,indent=0] ---- .Example: Targeting styles to iOS devices with high or low density [source,css] ---- -@media platform-ios, density-high, density-low { - .... -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-038,indent=0] ---- .Example: Targeting Mac Desktop: [source,css] ---- -@media device-desktop, platform-mac { - .... -} +include::../demos/common/src/main/css/developer-guide/css.css[tag=css-css-039,indent=0] ---- ==== Order or precedence @@ -1276,22 +788,14 @@ For example, consider the following simple stylesheet that defines a font size o [source,css] ---- -Label { - font-size: 3mm; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-040,indent=0] ---- During testing, perhaps you find that, on desktop, the fonts are a little bit too small. In this case, you can apply a font-scale constant that only applies to the desktop: [source,css] ---- -#Constants { - device-desktop-font-scale: "1.5"; -} - -Label { - font-size: 3mm; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-041,indent=0] ---- Now, on most devices the Label style will have `3mm` fonts. However, on desktop, it will have `4.5mm` fonts. @@ -1300,28 +804,13 @@ The above would be equivalent to: [source,css] ---- -Label { - font-size: 3mm; -} - -@media device-desktop { - Label { - font-size: 4.5mm; - } -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-042,indent=0] ---- .Example: Font-scaling based on device, platform, and density [source,css] ---- -#Constants { - device-phone-font-scale: "1.5"; - device-tablet-font-scale: "1.2"; - device-desktop-font-scale: "1.4"; - platform-ios-font-scale: "0.9"; - density-low-font-scale: "1.2"; - platform-ios-density-low-font-scale: "1.3"; -} +include::../demos/common/src/main/css/guide-snippets-theme.css[tag=css-css-043,indent=0] ---- IMPORTANT: All matching `font-scale` constants will be applied to the styles. If you define 3 font-scale constants that all match the current runtime environment, they will all be applied. For example, If there are 3 matching font-scale constants with `2.0`, `3.0`, and `4.0`, then fonts will be scaled by 2*3*4=24! diff --git a/docs/developer-guide/graphics.asciidoc b/docs/developer-guide/graphics.asciidoc index 93769980363..968fd224f1e 100644 --- a/docs/developer-guide/graphics.asciidoc +++ b/docs/developer-guide/graphics.asciidoc @@ -34,23 +34,7 @@ A paint method can be implemented as such: [source,java] ---- -// hide the title -Form hi = new Form("", new BorderLayout()); -hi.add(BorderLayout.CENTER, new Component() { - @Override - public void paint(Graphics g) { - // red color - g.setColor(0xff0000); - - // paint the screen in red - g.fillRect(getX(), getY(), getWidth(), getHeight()); - - // draw hi world in white text in the top left corner of the screen - g.setColor(0xffffff); - g.drawString("Hi World", getX(), getY()); - } -}); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java[tag=graphics-java-001,indent=0] ---- .Hi world demo code, notice that the blue bar on top is the iOS7+ status bar @@ -66,16 +50,6 @@ later fill and draw operations. The https://www.codenameone.com/javadoc/com/codename1/ui/LinearGradientPaint.html[`LinearGradientPaint`] class is the most common option and accepts a list of color stops along a line: -[source,java] ----- -LinearGradientPaint gradient = new LinearGradientPaint( - 0, 0, getWidth(), 0, // horizontal gradient - new int[] {0xff4285f4, 0xff34a853, 0xfffbbc05}, - new float[] {0f, 0.5f, 1f} -); -g.setColor(gradient); -g.fillRect(getX(), getY(), getWidth(), getHeight()); ----- The `fillLinearGradient()` convenience methods (with optional `repeat` flag) provide a shorthand when you need a two-color gradient without constructing @@ -92,22 +66,6 @@ method that consumes a `Gradient` value object. `Gradient` is a `Paint` subclass with three concrete forms - `LinearGradient`, `RadialGradient`, `ConicGradient` - following the same pattern as the `Shape` hierarchy: -[source,java] ----- -int[] colors = { 0xffff0080, 0xffff8c00, 0xff40e0d0 }; -float[] stops = { 0f, 0.5f, 1f }; - -g.fillGradient(new LinearGradient(45f, colors, stops), - 0, 0, getWidth(), getHeight()); - -RadialGradient circle = new RadialGradient(colors, stops); -circle.setShape(RadialGradient.SHAPE_CIRCLE) - .setExtent(RadialGradient.EXTENT_FARTHEST_CORNER); -g.fillGradient(circle, 0, 0, getWidth(), getHeight()); - -g.fillGradient(new ConicGradient(colors, stops), - 0, 0, getWidth(), getHeight()); ----- `Gradient` carries the cycle method (`CYCLE_NONE` / `CYCLE_REPEAT` / `CYCLE_REFLECT`) for repeating / reflected fills. Each port implements @@ -151,19 +109,14 @@ it paints within the clipping region of the component hence it won't break the r You can set a painter on a form using code like this: [source,java] ---- -hi.setGlassPane(new Painter() { - @Override - public void paint(Graphics g, Rectangle rect) { - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava004Snippet.java[tag=graphics-java-004,indent=0] ---- Or you can use Java 8 lambdas to tighten the code a bit: [source,java] ---- -hi.setGlassPane((g, rect) -> { -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava005Snippet.java[tag=graphics-java-005,indent=0] ---- @@ -176,23 +129,7 @@ many user interfaces [source,java] ---- -Form hi = new Form("Glass Pane", new BoxLayout(BoxLayout.Y_AXIS)); -Style s = UIManager.getInstance().getComponentStyle("Label"); -s.setFgColor(0xff0000); -s.setBgTransparency(0); -Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s).toImage(); -TextField tf1 = new TextField("My Field"); -tf1.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); -tf1.getAllStyles().setMargin(5, 5, 5, 5); -hi.add(tf1); -hi.setGlassPane((g, rect) -> { - int x = tf1.getAbsoluteX() + tf1.getWidth(); - int y = tf1.getAbsoluteY(); - x -= warningImage.getWidth() / 2; - y += (tf1.getHeight() / 2 - warningImage.getHeight() / 2); - g.drawImage(warningImage, x, y); -}); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava006Snippet.java[tag=graphics-java-006,indent=0] ---- .The glass pane draws the warning sign on the border of the component peeking out @@ -224,35 +161,7 @@ The center of the app is the `DrawingCanvas` class, which extends link:https://w [source,java] ---- -public class DrawingCanvas extends Component { - GeneralPath p = new GeneralPath(); - int strokeColor = 0x0000ff; - int strokeWidth = 10; - - public void addPoint(float x, float y){ - // To be written - } - - @Override - protected void paintBackground(Graphics g) { - super.paintBackground(g); - Stroke stroke = new Stroke( - strokeWidth, - Stroke.CAP_BUTT, - Stroke.JOIN_ROUND, 1f - ); - g.setColor(strokeColor); - - // Draw the shape - g.drawShape(p, stroke); - - } - - @Override - public void pointerPressed(int x, int y) { - addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY()); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava007Snippet.java[tag=graphics-java-007,indent=0] ---- Conceptually this is basic component. You will be overriding the @@ -271,24 +180,6 @@ a stroke of the appropriate width, and sets the color on the graphics context. T The addPoint method is designed to allow you to add points to the drawing. A simple implementation that uses straight lines rather than curves might look like this: -[source,java] ----- -private float lastX = -1; -private float lastY = -1; - -public void addPoint(float x, float y) { - if (lastX == -1) { - // this is the first point... Don't draw a line yet - p.moveTo(x, y); - } else { - p.lineTo(x, y); - } - lastX = x; - lastY = y; - - repaint(); -} ----- You introduced a couple house-keeping member vars (`lastX` and `lastY`) to store the last point that was added that you know whether this is the first tap or a later tap. The first tap triggers a `moveTo()` call, whereas @@ -317,24 +208,6 @@ See the https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.ht You will make use of the link:https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html#quadTo(float,%20float,%20float,%20float)[`quadTo()`] method to append curves to the drawing as follows: -[source,java] ----- -private boolean odd=true; -public void addPoint(float x, float y){ - if ( lastX == -1 ){ - p.moveTo(x, y); - - } else { - float controlX = odd ? lastX : x; - float controlY = odd ? y : lastY; - p.quadTo(controlX, controlY, x, y); - } - odd = !odd; - lastX = x; - lastY = y; - repaint(); -} ----- This change should be straight forward except, perhaps, the business with the `odd` variable. Since quadratic curves require two points (also to the implied starting point), you can't take the last tap @@ -356,22 +229,6 @@ The `DrawingCanvas` example is a bit naive in that it assumes that the device su expected to be drawn. You can fall back gracefully if you make use of the https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html#isShapeSupported()[`Graphics.isShapeSupported()`] method. For example: -[source,java] ----- -@Override -protected void paintBackground(Graphics g) { - super.paintBackground(g); - if ( g.isShapeSupported() ){ - // do my shape drawing code here - } else { - // draw an alternate representation for device - // that doesn't support shapes. - // E.g. You could defer to the Pisces - // library in this case - } - -} ----- === Transforms @@ -417,17 +274,10 @@ For example: [source,java] ---- -public void paint(Graphics g){ - if ( g.isAffineSupported() ){ - // Do something that requires rotation and scaling - - } else { - // Fallback behavior here - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava011Snippet.java[tag=graphics-java-011,indent=0] ---- -=== Example: drawing an analog clock +=== Example: Drawing an analog clock The following sections implement an analog clock component. This will show three key concepts in Codename One's graphics: @@ -449,14 +299,7 @@ Your clock will extend the https://www.codenameone.com/javadoc/com/codename1/ui/ [source,java] ---- -public class AnalogClock extends Component { - Date currentTime = new Date(); - - @Override - public void paintBackground(Graphics g) { - // Draw the clock in this method - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava012Snippet.java[tag=graphics-java-012,indent=0] ---- ==== Setting up the parameters @@ -474,73 +317,12 @@ The clock also exposes the following parameters to help customize how it's rende displayed at quarter points (that's: 12, 3, 6, and 9). Slightly shorter ticks will be displayed at the five-minute marks (that's: where the numbers appear), and the remaining marks (corresponding with seconds) will be short: -[source,java] ----- -// Hard code the padding at 10 pixels for now -double padding = 10; - -// Clock radius -double r = Math.min(getWidth(), getHeight())/2-padding; - -// Center point. -double cX = getX()+getWidth()/2; -double cY = getY()+getHeight()/2; - -//Tick Styles -int tickLen = 10; // short tick -int medTickLen = 30; // at 5-minute intervals -int longTickLen = 50; // at the quarters -int tickColor = 0xCCCCCC; -Stroke tickStroke = new Stroke(2f, Stroke.CAP_BUTT, Stroke.JOIN_ROUND, 1f); ----- ==== Drawing the tick marks For the tick marks, you will use a single https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html[GeneralPath] object, making use of the `moveTo()` and `lineTo()` methods to draw each individual tick: -[source,java] ----- -// Draw a tick for each "second" (1 through 60) -for ( int i=1; i<= 60; i++){ - // default tick length is short - int len = tickLen; - if ( i % 15 == 0 ){ - // Longest tick on quarters (every 15 ticks) - len = longTickLen; - } else if ( i % 5 == 0 ){ - // Medium ticks on the '5's (every 5 ticks) - len = medTickLen; - } - - double di = (double)i; // tick num as double for easier math - - // Get the angle from 12 O'Clock to this tick (radians) - double angleFrom12 = di/60.0*2.0*Math.PI; - - // Get the angle from 3 O'Clock to this tick - // Note: 3 O'Clock corresponds with zero angle in unit circle - // Makes it easier to do the math. - double angleFrom3 = Math.PI/2.0-angleFrom12; - - // Move to the outer edge of the circle at correct position - // for this tick. - ticksPath.moveTo( - (float)(cX+Math.cos(angleFrom3)*r), - (float)(cY-Math.sin(angleFrom3)*r) - ); - - // Draw line inward along radius for length of tick mark - ticksPath.lineTo( - (float)(cX+Math.cos(angleFrom3)*(r-len)), - (float)(cY-Math.sin(angleFrom3)*(r-len)) - ); -} - -// Draw the full shape onto the graphics context. -g.setColor(tickColor); -g.drawShape(ticksPath, tickStroke); ----- TIP: This example uses a little bit of trigonometry to calculate the `(x,y)` coordinates of the tick marks based on the angle and the radius. If math isn't your thing, don't worry. This example makes use of the identities: `x=r*cosθ` and `y=r*sinθ`. @@ -561,53 +343,6 @@ For the purposes of this tutorial, you will use the following strategy. For each 2. Draw number (using `drawString()`) at the clock's center. It should be rendered at the correct point due to your translation. 3. Invert the translation performed in step 1: -[source,java] ----- -for ( int i=1; i<=12; i++){ - // Calculate the string width and height so we can center it properly - String numStr = ""+i; - int charWidth = g.getFont().stringWidth(numStr); - int charHeight = g.getFont().getHeight(); - - double di = (double)i; // number as double for easier math - - // Calculate the position along the edge of the clock where the number should - // be drawn - // Get the angle from 12 O'Clock to this tick (radians) - double angleFrom12 = di/12.0*2.0*Math.PI; - - // Get the angle from 3 O'Clock to this tick - // Note: 3 O'Clock corresponds with zero angle in unit circle - // Makes it easier to do the math. - double angleFrom3 = Math.PI/2.0-angleFrom12; - - // Get diff between number position and clock center - int tx = (int)(Math.cos(angleFrom3)*(r-longTickLen)); - int ty = (int)(-Math.sin(angleFrom3)*(r-longTickLen)); - - // For 6 and 12 we will shift number slightly so they're more even - if ( i == 6 ){ - ty -= charHeight/2; - } else if ( i == 12 ){ - ty += charHeight/2; - } - - // Translate the graphics context by delta between clock center and - // number position - g.translate( - tx, - ty - ); - - - // Draw number at clock center. - g.drawString(numStr, (int)cX-charWidth/2, (int)cY-charHeight/2); - - // Undo translation - g.translate(-tx, -ty); - -} ----- NOTE: This example is, admittedly, a little contrived to allow for a demonstration of the `Graphics.translate()` method. You could have as passed the exact location of the number to `drawString()` rather than draw at the clock @@ -634,21 +369,9 @@ for each hand. For the positioning/angle of each, use the following strategy: For the "second" hand, you will use a simple line from the clock center to the inside edge of the medium tick mark at the 12 o'clock position: -[source,java] ----- -GeneralPath secondHand = new GeneralPath(); -secondHand.moveTo((float)cX, (float)cY); -secondHand.lineTo((float)cX, (float)(cY-(r-medTickLen))); ----- And you will translate it down slightly so that it overlaps the center. This translation will be performed on the `GeneralPath` object directly rather than through the `Graphics` context: -[source,java] ----- -Shape translatedSecondHand = secondHand.createTransformedShape( - Transform.makeTranslation(0f, 5) -); ----- *Rotating the Second Hand:*: @@ -659,25 +382,6 @@ which, in your case will be the clock center. WARNING: The rotation pivot point is expected to be in absolute screen coordinates rather than relative coordinates of the component. You therefore need to get the absolute clock center position to perform the rotation: -[source,java] ----- -// Calculate the angle of the second hand -Calendar calendar = Calendar.getInstance(TimeZone.getDefault()); -double second = (double)(calendar.get(Calendar.SECOND)); -double secondAngle = second/60.0*2.0*Math.PI; - -// Get absolute center position of the clock -double absCX = getAbsoluteX()+cX-getX(); -double absCY = getAbsoluteY()+cY-getY(); - -g.rotate((float)secondAngle, (int)absCX, (int)absCY); -g.setColor(0xff0000); -g.drawShape( - translatedSecondHand, - new Stroke(2f, Stroke.CAP_BUTT, Stroke.JOIN_BEVEL, 1f) -); -g.resetAffine(); ----- NOTE: Remember to call `resetAffine()` after you're done with the rotation, or you will see some unexpected results on your form. @@ -694,57 +398,6 @@ parameters. For example: The hour hand angle is informed by both the hour of the The remaining drawing code is as follows: -[source,java] ----- -// Draw the minute hand -GeneralPath minuteHand = new GeneralPath(); -minuteHand.moveTo((float)cX, (float)cY); -minuteHand.lineTo((float)cX+6, (float)cY); -minuteHand.lineTo((float)cX+2, (float)(cY-(r-tickLen))); -minuteHand.lineTo((float)cX-2, (float)(cY-(r-tickLen))); -minuteHand.lineTo((float)cX-6, (float)cY); -minuteHand.closePath(); - -// Translate the minute hand slightly down so it overlaps the center -Shape translatedMinuteHand = minuteHand.createTransformedShape( - Transform.makeTranslation(0f, 5) -); - -double minute = (double)(calendar.get(Calendar.MINUTE)) + - (double)(calendar.get(Calendar.SECOND))/60.0; - -double minuteAngle = minute/60.0*2.0*Math.PI; - -// Rotate and draw the minute hand -g.rotate((float)minuteAngle, (int)absCX, (int)absCY); -g.setColor(0×000000); -g.fillShape(translatedMinuteHand); -g.resetAffine(); - - -// Draw the hour hand -GeneralPath hourHand = new GeneralPath(); -hourHand.moveTo((float)cX, (float)cY); -hourHand.lineTo((float)cX+4, (float)cY); -hourHand.lineTo((float)cX+1, (float)(cY-(r-longTickLen)*0.75)); -hourHand.lineTo((float)cX-1, (float)(cY-(r-longTickLen)*0.75)); -hourHand.lineTo((float)cX-4, (float)cY); -hourHand.closePath(); - -Shape translatedHourHand = hourHand.createTransformedShape( - Transform.makeTranslation(0f, 5) -); - -//Calendar cal = Calendar.getInstance().get -double hour = (double)(calendar.get(Calendar.HOUR_OF_DAY)%12) + - (double)(calendar.get(Calendar.MINUTE))/60.0; - -double angle = hour/12.0*2.0*Math.PI; -g.rotate((float)angle, (int)absCX, (int)absCY); -g.setColor(0×000000); -g.fillShape(translatedHourHand); -g.resetAffine(); ----- ==== The final result @@ -768,20 +421,6 @@ To animate your clock so that it updates once per second, you need to do two thi The `animate()` method in the `AnalogClock` class: -[source,java] ----- -Date currentTime = new Date(); -long lastRenderedTime = 0; - -@Override -public boolean animate() { - if ( System.currentTimeMillis()/1000 != lastRenderedTime/1000){ - currentTime.setTime(System.currentTimeMillis()); - return true; - } - return false; -} ----- This method will be invoked on each "pulse" of the EDT. It checks the last time the clock was rendered and returns `true` if the clock hasn't been rendered in the current "time second" interval. Otherwise it returns false. This @@ -793,25 +432,9 @@ Animations can be started and stopped through the `Form.registerAnimated(compone `Form.deregisterAnimated(component)` methods. You chose to encapsulate these calls in `start()` and `stop()` methods in the component as follows: -[source,java] ----- -public void start(){ - getComponentForm().registerAnimated(this); -} - -public void stop(){ - getComponentForm().deregisterAnimated(this); -} ----- The code to instantiate the clock, and start the animation would be something like: -[source,java] ----- -AnalogClock clock = new AnalogClock(); -parent.addComponent(clock); -clock.start(); ----- === Shape clipping @@ -821,45 +444,7 @@ For example: this code allows you to draw a rather complex image of duke: [source,java] ---- -Image duke = null; -try { - // duke.png is just the default Codename One icon copied into place - duke = Image.createImage("/duke.png"); -} catch(IOException err) { - Log.e(err); -} -final Image finalDuke = duke; - -Form hi = new Form("Shape Clip"); - -// We create a 50 x 100 shape, this is arbitrary since we can scale it easily -GeneralPath path = new GeneralPath(); -path.moveTo(20,0); -path.lineTo(30, 0); -path.lineTo(30, 100); -path.lineTo(20, 100); -path.lineTo(20, 15); -path.lineTo(5, 40); -path.lineTo(5, 25); -path.lineTo(20,0); - -Stroke stroke = new Stroke(0.5f, Stroke.CAP_ROUND, Stroke.JOIN_ROUND, 4); -hi.getContentPane().getUnselectedStyle().setBgPainter((Graphics g, Rectangle rect) -> { - g.setColor(0xff); - float widthRatio = ((float)rect.getWidth()) / 50f; - float heightRatio = ((float)rect.getHeight()) / 100f; - g.scale(widthRatio, heightRatio); - g.translate((int)(((float)rect.getX()) / widthRatio), (int)(((float)rect.getY()) / heightRatio)); - g.setClip(path); - g.setAntiAliased(true); - g.drawImage(finalDuke, 0, 0, 50, 100); - g.setClip(path.getBounds()); - g.drawShape(path, stroke); - g.translate(-(int)(((float)rect.getX()) / widthRatio), -(int)(((float)rect.getY()) / heightRatio)); - g.resetAffine(); -}); - -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava023Snippet.java[tag=graphics-java-023,indent=0] ---- .Shape Clipping used to clip the image of duke within the given shape @@ -882,12 +467,12 @@ call on the https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html[G [source,java] ---- -g.drawRect(10,10, 100, 100); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava024Snippet.java[tag=graphics-java-024,indent=0] ---- Where would this rectangle be drawn on the screen? -If you answered something like "10 pixels from the top, and 10 pixels from the left of the screen", +If you answered something like "10 pixels from the top, and 10 pixels from the left of the screen," you _might_ be right. It depends on whether the graphics has a translation or transform applied to it. If there is currently a translation of `(20,20)` (that's: 20 pixels to the right, and 20 pixels down), then the rectangle would be rendered at `(30, 30)`. @@ -897,21 +482,7 @@ and `Graphics.getTranslateY()` methods: [source,java] ---- -// Find out the current translation -int currX = g.getTranslateX(); -int currY = g.getTranslateY(); - -// Reset the translation to zeroes -g.translate(-currX, -currY); - -// Now we are working in absolute screen coordinates -g.drawRect(10, 10, 100, 100); - -// This rectangle should now be drawn at the exact screen -// coordinates (10,10). - -//Restore the translation -g.translate(currX, currY); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava025Snippet.java[tag=graphics-java-025,indent=0] ---- NOTE: This example glosses over issues such as clipping and transforms which may cause it to not work as you @@ -937,12 +508,7 @@ drawn. And you will keep a 5 pixel padding between the edge of the component and [source,java] ---- -class RectangleComponent extends Component { - public void paint(Graphics g){ - g.setColor(0x0000ff); - g.drawRect(getX()+5, getY()+5, getWidth()-10, getHeight()-10); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava026Snippet.java[tag=graphics-java-026,indent=0] ---- The result is as follows: @@ -972,22 +538,7 @@ your first try might look something like: [source,java] ---- - class RectangleComponent extends Component { - - @Override - protected Dimension calcPreferredSize() { - return new Dimension(250,250); - } - - public void paint(Graphics g) { - g.setColor(0x0000ff); - g.rotate((float) (Math.PI / 4.0)); - g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10); - g.rotate(-(float) (Math.PI / 4.0)); - } - } - - +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava027Snippet.java[tag=graphics-java-027,indent=0] ---- TIP: When performing rotations and transformations inside a `paint()` method, always remember to revert your @@ -996,18 +547,6 @@ transformations at the end of the method so that it doesn't pollute the renderin The behavior of this rotation will vary based on where the component is rendered on the screen. To show this, try to place five of these components on a form inside a https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html[BorderLayout] and see how it looks: -[source,java] ----- - class MyForm extends Form { - - public MyForm() { - super("Rectangle Rotations"); - for ( int i=0; i< 10; i++ ){ - this.addComponent(new RectangleComponent()); - } - } - } ----- The result is as follows: @@ -1024,38 +563,12 @@ Some possibilities would be: Top Left Corner: -[source,java] ----- - public void paint(Graphics g) { - g.setColor(0x0000ff); - g.rotate((float)(Math.PI/4.0), getAbsoluteX(), getAbsoluteY()); - g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10); - g.rotate(-(float) (Math.PI / 4.0), getAbsoluteX(), getAbsoluteY()); - } ----- .Rotating the rectangle with wrong pivot point image::img/rotation2.png[Rotating the rectangle with wrong pivot point,scaledwidth=20%] Center: -[source,java] ----- -public void paint(Graphics g) { - g.setColor(0x0000ff); - g.rotate( - (float)(Math.PI/4.0), - getAbsoluteX()+getWidth()/2, - getAbsoluteY()+getHeight()/2 - ); - g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10); - g.rotate( - -(float)(Math.PI/4.0), - getAbsoluteX()+getWidth()/2, - getAbsoluteY()+getHeight()/2 - ); -} ----- .Rotating the rectangle with the center pivot point image::img/rotation3.png[Rotating the rectangle with the center pivot point,scaledwidth=20%] @@ -1091,12 +604,6 @@ methods. For example: a `pointerPressed()` callback method can look like this: -[source,java] ----- -public void pointerPressed(int x, int y) { - addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY()); -} ----- In this case you translated these points so that they would be relative to the origin of the parent component. This is because the `drawXXX()` methods for this component take coordinates relative to the parent component. @@ -1213,12 +720,7 @@ Icon fonts can be created in 2 basic ways the first is explicitly by defining al [source,java] ---- -Form hi = new Form("Icon Font"); -Font materialFont = FontImage.getMaterialDesignFont(); -int w = Display.getInstance().getDisplayWidth(); -FontImage fntImage = FontImage.createFixed("\uE161", materialFont, 0xff0000, w, w); -hi.add(fntImage); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java[tag=graphics-java-032,indent=0] ---- .Icon font from material design icons created with the fixed size of display width @@ -1232,14 +734,7 @@ There are two versions of this method: the first one expects the `Style` object [source,java] ---- -Form hi = new Form("Icon Font"); -Font materialFont = FontImage.getMaterialDesignFont(); -int size = Display.getInstance().convertToPixels(6, true); -materialFont = materialFont.derive(size, Font.STYLE_PLAIN); -Button myButton = new Button("Save"); -myButton.setIcon(FontImage.create("\uE161", myButton.getUnselectedStyle(), materialFont)); -hi.add(myButton); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java[tag=graphics-java-033,indent=0] ---- .An image created from the Style object @@ -1259,10 +754,7 @@ To recreate the save icon from above you can do something like: [source,java] ---- -Form hi = new Form("Icon Font"); -Button myButton = new Button("Save"); -myButton.setIcon(FontImage.createMaterial(FontImage.MATERIAL_SAVE, myButton.getUnselectedStyle())); -hi.add(myButton); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java[tag=graphics-java-034,indent=0] ---- .Material save icon @@ -1274,10 +766,7 @@ You can even write the code in a more terse style using: [source,java] ---- -Form hi = new Form("Icon Font"); -Button myButton = new Button("Save"); -FontImage.setMaterialIcon(myButton, FontImage.MATERIAL_SAVE); -hi.add(myButton); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava035Snippet.java[tag=graphics-java-035,indent=0] ---- This will produce the same result for slightly shorter syntax. @@ -1297,30 +786,7 @@ The code below can convert an image to a rounded image: [source,java] ---- -Toolbar.setGlobalToolbar(true); -Form hi = new Form("Rounder", new BorderLayout()); -Label picture = new Label("", "Container"); -hi.add(BorderLayout.CENTER, picture); -hi.getUnselectedStyle().setBgColor(0xff0000); -hi.getUnselectedStyle().setBgTransparency(255); -Style s = UIManager.getInstance().getComponentStyle("TitleCommand"); -Image camera = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s); -hi.getToolbar().addCommandToRightBar("", camera, (ev) -> { - try { - int width = Display.getInstance().getDisplayWidth(); - Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1)); - Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000); - Graphics gr = roundMask.getGraphics(); - gr.setColor(0xffffff); - gr.fillArc(0, 0, width, width, 0, 360); - Object mask = roundMask.createMask(); - capturedImage = capturedImage.applyMask(mask); - picture.setIcon(capturedImage); - hi.revalidate(); - } catch(IOException err) { - Log.e(err); - } -}); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava036Snippet.java[tag=graphics-java-036,indent=0] ---- .Picture after the capture was complete and the resulted image was rounded. The background was set to red so the rounding effect will be more noticeable @@ -1343,10 +809,6 @@ Once the image was fetched the `animate()` method returns true to refresh the UI The simple use case is pretty trivial: -[source,java] ----- -Image i = URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/myurl.jpg", URLImage.RESIZE_SCALE); ----- Or you can use the similar `URLImage.createToFileSystem` method instead of the https://www.codenameone.com/javadoc/com/codename1/io/Storage.html[Storage] version. @@ -1369,10 +831,6 @@ rounded version of the downloaded image. To do this you can override: -[source,java] ----- -public EncodedImage adaptImage(EncodedImage downloadedImage, Image placeholderImage) ----- In the adapter interface and return the processed encoded image. If you do heavy processing (for example: rounded edge images) you would need to convert the processed image back to an encoded image so it can be saved. You would then also want to show that this operation should run asynchronously through the appropriate method in the class. @@ -1386,17 +844,6 @@ The standard `createToStorage` path doesn't expose the underlying other custom header / cookie / timeout) requires the `RequestDecorator` hook. Two ways to install one: -[source,java] ----- -// Global default -- applied to every URLImage download from this point on. -// The most common case is "all our images sit behind the same bearer -// token", which has its own shorthand: -URLImage.setDefaultBearerToken(Preferences.get("auth.token", null)); - -// Or the explicit form, which can attach any header / cookie / timeout: -URLImage.setDefaultRequestDecorator(req -> - req.addRequestHeader("Authorization", "Bearer " + token)); ----- For per-image overrides (for example one endpoint needs an extra API-version header on top of the global bearer token), use the @@ -1404,15 +851,6 @@ header on top of the global bearer token), use the overload. Per-instance decorators run *after* the global default so they can override or augment whatever the default set: -[source,java] ----- -URLImage profilePic = URLImage.createToStorage( - placeholder, - "profile-" + userId, - baseUrl + "/users/" + userId + "/picture", - URLImage.RESIZE_SCALE_TO_FILL, - req -> req.addRequestHeader("X-API-Version", "2")); ----- When a decorator is installed (global or per-call), `URLImage` skips the default `Util.downloadImageToStorage` path and builds a @@ -1424,16 +862,6 @@ when no decorator is set. A `URLImage` can be created with a mask adapter to apply an effect to an image. This allows you to round downloaded images or apply any sort of masking for example: you can adapt the round mask code above as such: -[source,java] ----- -Image roundMask = Image.createImage(placeholder.getWidth(), placeholder.getHeight(), 0xff000000); -Graphics gr = roundMask.getGraphics(); -gr.setColor(0xffffff); -gr.fillArc(0, 0, placeholder.getWidth(), placeholder.getHeight(), 0, 360); - -URLImage.ImageAdapter ada = URLImage.createMaskAdapter(roundMask); -Image i = URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/myurl.jpg", ada); ----- ===== URLImage in lists @@ -1443,55 +871,18 @@ https://www.codenameone.com/javadoc/java/util/List.html[List] & https://www.code `_URLImage` and give it an icon to use as the placeholder. This is easy to do in the multilist by changing the name of icon to `icon_URLImage` then using this in the data: -[source,java] ----- -map.put("icon_URLImage", urlToActualImage); ----- Make sure you also set a "real" icon to the entry in the GUI builder or in handcoded applications. This is important since the icon will be implicitly extracted and used as the placeholder value. Everything else should be handled automatically. You can use `setDefaultAdapter` & `setAdapter` on the generic list cell renderer to install adapters for the images. The default is a scale adapter although you might change that to scale fill in the future: -[source,java] ----- -Style s = UIManager.getInstance().getComponentStyle("Button"); -FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s); -EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 4), false); - -Form hi = new Form("MultiList", new BorderLayout()); - -ArrayList> data = new ArrayList<>(); - -data.add(createListEntry("A Game of Thrones", "1996", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg")); -data.add(createListEntry("A Clash Of Kings", "1998", "http://www.georgerrmartin.com/wp-content/uploads/2012/08/clashofkings.jpg")); -data.add(createListEntry("A Storm Of Swords", "2000", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/stormswordsMTI.jpg")); -data.add(createListEntry("A Feast For Crows", "2005", "http://www.georgerrmartin.com/wp-content/uploads/2012/08/feastforcrows.jpg")); -data.add(createListEntry("A Dance With Dragons", "2011", "http://georgerrmartin.com/gallery/art/dragons05.jpg")); -data.add(createListEntry("The Winds of Winter", "2016 (please, please, please)", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg")); -data.add(createListEntry("A Dream of Spring", "Ugh", "http://www.georgerrmartin.com/wp-content/uploads/2013/03/GOTMTI2.jpg")); - -DefaultListModel> model = new DefaultListModel<>(data); -MultiList ml = new MultiList(model); -ml.getUnselectedButton().setIconName("icon_URLImage"); -ml.getSelectedButton().setIconName("icon_URLImage"); -ml.getUnselectedButton().setIcon(placeholder); -ml.getSelectedButton().setIcon(placeholder); -hi.add(BorderLayout.CENTER, ml); ----- The `createListEntry` method then looks like this: [source,java] ---- -private Map createListEntry(String name, String date, String coverURL) { - Map entry = new HashMap<>(); - entry.put("Line1", name); - entry.put("Line2", date); - entry.put("icon_URLImage", coverURL); - entry.put("icon_URLImageName", name); - return entry; -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava044Snippet.java[tag=graphics-java-044,indent=0] ---- .A URL image fetched dynamically into the list model @@ -1506,26 +897,6 @@ chart view class, and wrapping it in a https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html[`ChartComponent`] it can be added to a form: -[source,java] ----- -XYSeriesRenderer seriesRenderer = new XYSeriesRenderer(); -seriesRenderer.setColor(0xff0000); - -XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); -renderer.addSeriesRenderer(seriesRenderer); - -XYSeries series = new XYSeries("Sales"); -series.add(1, 42); -series.add(2, 57); - -XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset(); -dataset.addSeries(series); - -BarChart chart = new BarChart(dataset, renderer, BarChart.Type.DEFAULT); -Form form = new Form(new BorderLayout()); -form.add(BorderLayout.CENTER, new ChartComponent(chart)); -form.show(); ----- The following classes are available for different kinds of visualisations: diff --git a/docs/developer-guide/img/badge-floating-button.png b/docs/developer-guide/img/badge-floating-button.png index 0f58843e95f..44cfe10d229 100644 Binary files a/docs/developer-guide/img/badge-floating-button.png and b/docs/developer-guide/img/badge-floating-button.png differ diff --git a/docs/developer-guide/img/border-layout-RTL.png b/docs/developer-guide/img/border-layout-RTL.png index 5239ea165c8..6f248fc1592 100644 Binary files a/docs/developer-guide/img/border-layout-RTL.png and b/docs/developer-guide/img/border-layout-RTL.png differ diff --git a/docs/developer-guide/img/border-layout-center.png b/docs/developer-guide/img/border-layout-center.png index 18cd90820bc..814b4918b74 100644 Binary files a/docs/developer-guide/img/border-layout-center.png and b/docs/developer-guide/img/border-layout-center.png differ diff --git a/docs/developer-guide/img/border-layout.png b/docs/developer-guide/img/border-layout.png index 959ee265309..7a37f022579 100644 Binary files a/docs/developer-guide/img/border-layout.png and b/docs/developer-guide/img/border-layout.png differ diff --git a/docs/developer-guide/img/box-layout-x-no-grow.png b/docs/developer-guide/img/box-layout-x-no-grow.png index 06cc4a55ccd..f91cb610c6f 100644 Binary files a/docs/developer-guide/img/box-layout-x-no-grow.png and b/docs/developer-guide/img/box-layout-x-no-grow.png differ diff --git a/docs/developer-guide/img/box-layout-x.png b/docs/developer-guide/img/box-layout-x.png index 11cdc429221..4427b719602 100644 Binary files a/docs/developer-guide/img/box-layout-x.png and b/docs/developer-guide/img/box-layout-x.png differ diff --git a/docs/developer-guide/img/box-layout-y.png b/docs/developer-guide/img/box-layout-y.png index 205311ce9e9..d3dc6e438c7 100644 Binary files a/docs/developer-guide/img/box-layout-y.png and b/docs/developer-guide/img/box-layout-y.png differ diff --git a/docs/developer-guide/img/components-accordion.png b/docs/developer-guide/img/components-accordion.png index 79791e08ae8..e5d5053f41f 100644 Binary files a/docs/developer-guide/img/components-accordion.png and b/docs/developer-guide/img/components-accordion.png differ diff --git a/docs/developer-guide/img/components-button.png b/docs/developer-guide/img/components-button.png index b82862a5b52..ece160a5887 100644 Binary files a/docs/developer-guide/img/components-button.png and b/docs/developer-guide/img/components-button.png differ diff --git a/docs/developer-guide/img/components-componentgroup.png b/docs/developer-guide/img/components-componentgroup.png index fe6cfdafd7b..6113188015d 100644 Binary files a/docs/developer-guide/img/components-componentgroup.png and b/docs/developer-guide/img/components-componentgroup.png differ diff --git a/docs/developer-guide/img/components-dialog-blur-no-tint.png b/docs/developer-guide/img/components-dialog-blur-no-tint.png index 7738acfc1fd..00b2322c364 100644 Binary files a/docs/developer-guide/img/components-dialog-blur-no-tint.png and b/docs/developer-guide/img/components-dialog-blur-no-tint.png differ diff --git a/docs/developer-guide/img/components-dialog-blur.png b/docs/developer-guide/img/components-dialog-blur.png index e4e5f986be4..9071a6b78f5 100644 Binary files a/docs/developer-guide/img/components-dialog-blur.png and b/docs/developer-guide/img/components-dialog-blur.png differ diff --git a/docs/developer-guide/img/components-dialog-green-tint.png b/docs/developer-guide/img/components-dialog-green-tint.png index a2fb1975ffd..644a8f07592 100644 Binary files a/docs/developer-guide/img/components-dialog-green-tint.png and b/docs/developer-guide/img/components-dialog-green-tint.png differ diff --git a/docs/developer-guide/img/components-dialog-modal-bottom-half.png b/docs/developer-guide/img/components-dialog-modal-bottom-half.png index 30b8b68ee1d..dd3654299fc 100644 Binary files a/docs/developer-guide/img/components-dialog-modal-bottom-half.png and b/docs/developer-guide/img/components-dialog-modal-bottom-half.png differ diff --git a/docs/developer-guide/img/components-dialog-modal-south.png b/docs/developer-guide/img/components-dialog-modal-south.png index 34c4690704a..5aa30324d8f 100644 Binary files a/docs/developer-guide/img/components-dialog-modal-south.png and b/docs/developer-guide/img/components-dialog-modal-south.png differ diff --git a/docs/developer-guide/img/components-dialog-tint.png b/docs/developer-guide/img/components-dialog-tint.png index 55d1fd3b5fd..e2bf5ba6bca 100644 Binary files a/docs/developer-guide/img/components-dialog-tint.png and b/docs/developer-guide/img/components-dialog-tint.png differ diff --git a/docs/developer-guide/img/components-floatinghint.png b/docs/developer-guide/img/components-floatinghint.png index eb32ae0b1ed..b37e16e3f93 100644 Binary files a/docs/developer-guide/img/components-floatinghint.png and b/docs/developer-guide/img/components-floatinghint.png differ diff --git a/docs/developer-guide/img/components-interaction-dialog.png b/docs/developer-guide/img/components-interaction-dialog.png index c6271291b9e..33f8f7fee63 100644 Binary files a/docs/developer-guide/img/components-interaction-dialog.png and b/docs/developer-guide/img/components-interaction-dialog.png differ diff --git a/docs/developer-guide/img/components-link-button.png b/docs/developer-guide/img/components-link-button.png index 2e240bfdd3c..56201c82aa1 100644 Binary files a/docs/developer-guide/img/components-link-button.png and b/docs/developer-guide/img/components-link-button.png differ diff --git a/docs/developer-guide/img/components-multibutton.png b/docs/developer-guide/img/components-multibutton.png index a3a152adb57..cb0b2f1219a 100644 Binary files a/docs/developer-guide/img/components-multibutton.png and b/docs/developer-guide/img/components-multibutton.png differ diff --git a/docs/developer-guide/img/components-onoffswitch.png b/docs/developer-guide/img/components-onoffswitch.png index 17dee4d99ac..6162de44fab 100644 Binary files a/docs/developer-guide/img/components-onoffswitch.png and b/docs/developer-guide/img/components-onoffswitch.png differ diff --git a/docs/developer-guide/img/components-picker.png b/docs/developer-guide/img/components-picker.png index 5b1febf2626..a62dbc1cfdd 100644 Binary files a/docs/developer-guide/img/components-picker.png and b/docs/developer-guide/img/components-picker.png differ diff --git a/docs/developer-guide/img/components-radiobutton-checkbox.png b/docs/developer-guide/img/components-radiobutton-checkbox.png index 20d869d6a42..a5f11c71da3 100644 Binary files a/docs/developer-guide/img/components-radiobutton-checkbox.png and b/docs/developer-guide/img/components-radiobutton-checkbox.png differ diff --git a/docs/developer-guide/img/components-slider.png b/docs/developer-guide/img/components-slider.png index a2eee86a432..da364a08fbe 100644 Binary files a/docs/developer-guide/img/components-slider.png and b/docs/developer-guide/img/components-slider.png differ diff --git a/docs/developer-guide/img/components-spanbutton.png b/docs/developer-guide/img/components-spanbutton.png index 360acf19803..965cf620396 100644 Binary files a/docs/developer-guide/img/components-spanbutton.png and b/docs/developer-guide/img/components-spanbutton.png differ diff --git a/docs/developer-guide/img/components-spanlabel.png b/docs/developer-guide/img/components-spanlabel.png index e515cd00490..8f88fe2ddd3 100644 Binary files a/docs/developer-guide/img/components-spanlabel.png and b/docs/developer-guide/img/components-spanlabel.png differ diff --git a/docs/developer-guide/img/components-tabs-swipe1.png b/docs/developer-guide/img/components-tabs-swipe1.png index 60235705b1a..a31a69f4e55 100644 Binary files a/docs/developer-guide/img/components-tabs-swipe1.png and b/docs/developer-guide/img/components-tabs-swipe1.png differ diff --git a/docs/developer-guide/img/components-tabs-swipe2.png b/docs/developer-guide/img/components-tabs-swipe2.png index 425fff62f11..6637f8a70bb 100644 Binary files a/docs/developer-guide/img/components-tabs-swipe2.png and b/docs/developer-guide/img/components-tabs-swipe2.png differ diff --git a/docs/developer-guide/img/components-tabs.png b/docs/developer-guide/img/components-tabs.png index 00b7ab701b4..0accfda75f3 100644 Binary files a/docs/developer-guide/img/components-tabs.png and b/docs/developer-guide/img/components-tabs.png differ diff --git a/docs/developer-guide/img/floating-action.png b/docs/developer-guide/img/floating-action.png index 4ecfd1c0148..0fe6acba762 100644 Binary files a/docs/developer-guide/img/floating-action.png and b/docs/developer-guide/img/floating-action.png differ diff --git a/docs/developer-guide/img/flow-layout-center-middle.png b/docs/developer-guide/img/flow-layout-center-middle.png index 1ee82decba2..48c2ff820a8 100644 Binary files a/docs/developer-guide/img/flow-layout-center-middle.png and b/docs/developer-guide/img/flow-layout-center-middle.png differ diff --git a/docs/developer-guide/img/flow-layout-center.png b/docs/developer-guide/img/flow-layout-center.png index c1b3237419a..d644db3134e 100644 Binary files a/docs/developer-guide/img/flow-layout-center.png and b/docs/developer-guide/img/flow-layout-center.png differ diff --git a/docs/developer-guide/img/flow-layout-right.png b/docs/developer-guide/img/flow-layout-right.png index 1e9bd7b7b67..f95eeb0b1ca 100644 Binary files a/docs/developer-guide/img/flow-layout-right.png and b/docs/developer-guide/img/flow-layout-right.png differ diff --git a/docs/developer-guide/img/flow-layout.png b/docs/developer-guide/img/flow-layout.png index a4787999c46..8645c8b1688 100644 Binary files a/docs/developer-guide/img/flow-layout.png and b/docs/developer-guide/img/flow-layout.png differ diff --git a/docs/developer-guide/img/graphics-fontimage-fixed.png b/docs/developer-guide/img/graphics-fontimage-fixed.png index 0489c4197d4..02af5fc3a82 100644 Binary files a/docs/developer-guide/img/graphics-fontimage-fixed.png and b/docs/developer-guide/img/graphics-fontimage-fixed.png differ diff --git a/docs/developer-guide/img/graphics-fontimage-material.png b/docs/developer-guide/img/graphics-fontimage-material.png index 1bf0d8ee623..96a7303e601 100644 Binary files a/docs/developer-guide/img/graphics-fontimage-material.png and b/docs/developer-guide/img/graphics-fontimage-material.png differ diff --git a/docs/developer-guide/img/graphics-fontimage-style.png b/docs/developer-guide/img/graphics-fontimage-style.png index 8138a4ad203..a9fc2e18785 100644 Binary files a/docs/developer-guide/img/graphics-fontimage-style.png and b/docs/developer-guide/img/graphics-fontimage-style.png differ diff --git a/docs/developer-guide/img/graphics-glasspane.png b/docs/developer-guide/img/graphics-glasspane.png index cc4446b326e..68204a8dc8e 100644 Binary files a/docs/developer-guide/img/graphics-glasspane.png and b/docs/developer-guide/img/graphics-glasspane.png differ diff --git a/docs/developer-guide/img/graphics-hiworld.png b/docs/developer-guide/img/graphics-hiworld.png index e6dc202656d..fbfbc8aa96d 100644 Binary files a/docs/developer-guide/img/graphics-hiworld.png and b/docs/developer-guide/img/graphics-hiworld.png differ diff --git a/docs/developer-guide/img/grid-layout-2x2.png b/docs/developer-guide/img/grid-layout-2x2.png index 7f1d2184f0e..7ee310dff92 100644 Binary files a/docs/developer-guide/img/grid-layout-2x2.png and b/docs/developer-guide/img/grid-layout-2x2.png differ diff --git a/docs/developer-guide/img/grid-layout-2x4.png b/docs/developer-guide/img/grid-layout-2x4.png index c9a4d455c46..ab81ffb2985 100644 Binary files a/docs/developer-guide/img/grid-layout-2x4.png and b/docs/developer-guide/img/grid-layout-2x4.png differ diff --git a/docs/developer-guide/img/grid-layout-autofit-landscape.png b/docs/developer-guide/img/grid-layout-autofit-landscape.png index 0108ebcbe6a..0b3254dca18 100644 Binary files a/docs/developer-guide/img/grid-layout-autofit-landscape.png and b/docs/developer-guide/img/grid-layout-autofit-landscape.png differ diff --git a/docs/developer-guide/img/grid-layout-autofit-portrait.png b/docs/developer-guide/img/grid-layout-autofit-portrait.png index 517cc895c80..328005be544 100644 Binary files a/docs/developer-guide/img/grid-layout-autofit-portrait.png and b/docs/developer-guide/img/grid-layout-autofit-portrait.png differ diff --git a/docs/developer-guide/img/layout-animation-1.png b/docs/developer-guide/img/layout-animation-1.png index f4a9ff22bdf..7ed26ae616f 100644 Binary files a/docs/developer-guide/img/layout-animation-1.png and b/docs/developer-guide/img/layout-animation-1.png differ diff --git a/docs/developer-guide/img/layout-animation-2.png b/docs/developer-guide/img/layout-animation-2.png index 0fbd882ea81..c46015833ab 100644 Binary files a/docs/developer-guide/img/layout-animation-2.png and b/docs/developer-guide/img/layout-animation-2.png differ diff --git a/docs/developer-guide/img/layout-animation-3.png b/docs/developer-guide/img/layout-animation-3.png index fec128bf667..185ac8a5534 100644 Binary files a/docs/developer-guide/img/layout-animation-3.png and b/docs/developer-guide/img/layout-animation-3.png differ diff --git a/docs/developer-guide/img/layout-animation-4.png b/docs/developer-guide/img/layout-animation-4.png index 43c4a7e2cc4..375594c499d 100644 Binary files a/docs/developer-guide/img/layout-animation-4.png and b/docs/developer-guide/img/layout-animation-4.png differ diff --git a/docs/developer-guide/img/layout-animation-5.png b/docs/developer-guide/img/layout-animation-5.png index 6d9c91c1850..d84c9706b48 100644 Binary files a/docs/developer-guide/img/layout-animation-5.png and b/docs/developer-guide/img/layout-animation-5.png differ diff --git a/docs/developer-guide/img/layout-animation-6.png b/docs/developer-guide/img/layout-animation-6.png index 0f55c520d4b..87f757dc5ca 100644 Binary files a/docs/developer-guide/img/layout-animation-6.png and b/docs/developer-guide/img/layout-animation-6.png differ diff --git a/docs/developer-guide/img/layout-animation-7.png b/docs/developer-guide/img/layout-animation-7.png index d154c9a8583..a38bef6cb24 100644 Binary files a/docs/developer-guide/img/layout-animation-7.png and b/docs/developer-guide/img/layout-animation-7.png differ diff --git a/docs/developer-guide/img/mighty-morphing-components-1.png b/docs/developer-guide/img/mighty-morphing-components-1.png index 83e23617abf..270c20eaf32 100644 Binary files a/docs/developer-guide/img/mighty-morphing-components-1.png and b/docs/developer-guide/img/mighty-morphing-components-1.png differ diff --git a/docs/developer-guide/img/raised-flat-buttons.png b/docs/developer-guide/img/raised-flat-buttons.png index 8c43c3601ad..6d54ffe85de 100644 Binary files a/docs/developer-guide/img/raised-flat-buttons.png and b/docs/developer-guide/img/raised-flat-buttons.png differ diff --git a/docs/developer-guide/img/shaped-clipping.png b/docs/developer-guide/img/shaped-clipping.png index fc8ae728e48..697c7ccece1 100644 Binary files a/docs/developer-guide/img/shaped-clipping.png and b/docs/developer-guide/img/shaped-clipping.png differ diff --git a/docs/developer-guide/img/splitpane.png b/docs/developer-guide/img/splitpane.png index 7a3b6e43e69..65c9c767efd 100644 Binary files a/docs/developer-guide/img/splitpane.png and b/docs/developer-guide/img/splitpane.png differ diff --git a/docs/developer-guide/img/transition-bubble.png b/docs/developer-guide/img/transition-bubble.png index 2278db98d08..971440d30f1 100644 Binary files a/docs/developer-guide/img/transition-bubble.png and b/docs/developer-guide/img/transition-bubble.png differ diff --git a/docs/developer-guide/img/transition-cover.jpg b/docs/developer-guide/img/transition-cover.jpg deleted file mode 100644 index 3cdf9c999d9..00000000000 Binary files a/docs/developer-guide/img/transition-cover.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-cover.png b/docs/developer-guide/img/transition-cover.png new file mode 100644 index 00000000000..7d80b3e40dc Binary files /dev/null and b/docs/developer-guide/img/transition-cover.png differ diff --git a/docs/developer-guide/img/transition-fade.jpg b/docs/developer-guide/img/transition-fade.jpg deleted file mode 100644 index 2425e4e806f..00000000000 Binary files a/docs/developer-guide/img/transition-fade.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-fade.png b/docs/developer-guide/img/transition-fade.png new file mode 100644 index 00000000000..24ea5bc2d3a Binary files /dev/null and b/docs/developer-guide/img/transition-fade.png differ diff --git a/docs/developer-guide/img/transition-flip.jpg b/docs/developer-guide/img/transition-flip.jpg deleted file mode 100644 index ed8eae1bae5..00000000000 Binary files a/docs/developer-guide/img/transition-flip.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-flip.png b/docs/developer-guide/img/transition-flip.png new file mode 100644 index 00000000000..015cbf9cdf7 Binary files /dev/null and b/docs/developer-guide/img/transition-flip.png differ diff --git a/docs/developer-guide/img/transition-slide-fade.jpg b/docs/developer-guide/img/transition-slide-fade.jpg deleted file mode 100644 index 5281ba875d5..00000000000 Binary files a/docs/developer-guide/img/transition-slide-fade.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-slide-fade.png b/docs/developer-guide/img/transition-slide-fade.png new file mode 100644 index 00000000000..c4ce53b9efe Binary files /dev/null and b/docs/developer-guide/img/transition-slide-fade.png differ diff --git a/docs/developer-guide/img/transition-slide-vertical.jpg b/docs/developer-guide/img/transition-slide-vertical.jpg deleted file mode 100644 index d0db189afe8..00000000000 Binary files a/docs/developer-guide/img/transition-slide-vertical.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-slide-vertical.png b/docs/developer-guide/img/transition-slide-vertical.png new file mode 100644 index 00000000000..582e17241a0 Binary files /dev/null and b/docs/developer-guide/img/transition-slide-vertical.png differ diff --git a/docs/developer-guide/img/transition-slide.jpg b/docs/developer-guide/img/transition-slide.jpg deleted file mode 100644 index 189e7d554ea..00000000000 Binary files a/docs/developer-guide/img/transition-slide.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-slide.png b/docs/developer-guide/img/transition-slide.png new file mode 100644 index 00000000000..3abbb21a399 Binary files /dev/null and b/docs/developer-guide/img/transition-slide.png differ diff --git a/docs/developer-guide/img/transition-uncover.jpg b/docs/developer-guide/img/transition-uncover.jpg deleted file mode 100644 index acf9a9e484b..00000000000 Binary files a/docs/developer-guide/img/transition-uncover.jpg and /dev/null differ diff --git a/docs/developer-guide/img/transition-uncover.png b/docs/developer-guide/img/transition-uncover.png new file mode 100644 index 00000000000..855c1d6f3d9 Binary files /dev/null and b/docs/developer-guide/img/transition-uncover.png differ diff --git a/docs/developer-guide/io.asciidoc b/docs/developer-guide/io.asciidoc index 492f33de1ab..8e5e7dee7da 100644 --- a/docs/developer-guide/io.asciidoc +++ b/docs/developer-guide/io.asciidoc @@ -8,28 +8,28 @@ You can place arbitrary files within the `src` directory of a Codename One proje [source,java] ---- -InputStream i = getClass().getResourceAsStream("/myFile"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava001Snippet.java[tag=io-java-001,indent=0] ---- This isn't guaranteed to work on all platforms and will probably fail on some. Instead, use something like: [source,java] ---- -InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/myFile"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava002Snippet.java[tag=io-java-002,indent=0] ---- This isn't the limitation. You can't use hierarchies, so something like this fails: [source,java] ---- -InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/res/myFile"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava003Snippet.java[tag=io-java-003,indent=0] ---- You can't use relative paths either, so this also fails (notice the missing first slash): [source,java] ---- -InputStream i = Display.getInstance().getResourceAsStream(getClass(), "myFile"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava004Snippet.java[tag=io-java-004,indent=0] ---- Those limitations exist for portability. iOS and Android handle resources differently, so supporting the full Java SE semantics is unrealistic. @@ -48,59 +48,6 @@ TIP: Objects in `Storage` are deleted when an app is uninstalled, but they're re The sample code below demonstrates listing storage content, adding and viewing entries, and deleting entries: -[source,java] ----- -Toolbar.setGlobalToolbar(true); -Form hi = new Form("Storage", new BoxLayout(BoxLayout.Y_AXIS)); -hi.getToolbar().addCommandToRightBar("+", null, e -> { - TextField tf = new TextField("", "File Name", 20, TextField.ANY); - TextArea body = new TextArea(5, 20); - body.setHint("File Body"); - Command ok = new Command("OK"); - Command cancel = new Command("Cancel"); - Command result = Dialog.show("File Name", BorderLayout.north(tf).add(BorderLayout.CENTER, body), ok, cancel); - if(ok == result) { - try(OutputStream os = Storage.getInstance().createOutputStream(tf.getText());) { - os.write(body.getText().getBytes("UTF-8")); - createFileEntry(hi, tf.getText()); - hi.getContentPane().animateLayout(250); - } catch(IOException err) { - Log.e(err); - } - } -}); -for(String file : Storage.getInstance().listEntries()) { - createFileEntry(hi, file); -} -hi.show(); - -private void createFileEntry(Form hi, String file) { - Label fileField = new Label(file); - Button delete = new Button(); - Button view = new Button(); - FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE); - FontImage.setMaterialIcon(view, FontImage.MATERIAL_OPEN_IN_NEW); - Container content = BorderLayout.center(fileField); - int size = Storage.getInstance().entrySize(file); - content.add(BorderLayout.EAST, BoxLayout.encloseX(new Label(size + "bytes"), delete, view)); - delete.addActionListener(e -> { - Storage.getInstance().deleteStorageFile(file); - content.setY(hi.getWidth()); - hi.getContentPane().animateUnlayoutAndWait(150, 255); - hi.removeComponent(content); - hi.getContentPane().animateLayout(150); - }); - view.addActionListener(e -> { - try(InputStream is = Storage.getInstance().createInputStream(file);) { - String s = Util.readToString(is, "UTF-8"); - Dialog.show(file, s, "OK", null); - } catch(IOException err) { - Log.e(err); - } - }); - hi.add(content); -} ----- .List of files within the storage image::img/storage-list.png[List of files within the storage,scaledwidth=20%] @@ -112,16 +59,12 @@ image::img/storage-content.png[Content of a file added to the storage,scaledwidt `Storage` also offers a simple API in the form of the https://www.codenameone.com/javadoc/com/codename1/io/Preferences.html[Preferences] class. The `Preferences` class lets developers store simple variables, strings, numbers, booleans, and similar values without writing storage code. This is a common use case in applications. For example, you might need to store a server token: -[source,java] ----- -Preferences.set("token", myToken); ----- You can then read the token like this: [source,java] ---- -String token = Preferences.get("token", null); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava007Snippet.java[tag=io-java-007,indent=0] ---- The backing store filename defaults to `"codenameone.properties"`, but you can override it by calling `Preferences.setPreferencesLocation(String)` before your app touches the API. This is useful when you need separate preference namespaces per user or when you want to plug in an encrypted storage layer. @@ -167,51 +110,6 @@ WARNING: A common Android hack is to write files to the SDCard storage to share A more advanced usage of the `FileSystemStorage` API can be a `FileSystemStorage` `Tree`: -[source,java] ----- -Form hi = new Form("FileSystemTree", new BorderLayout()); -TreeModel tm = new TreeModel() { - @Override - public Vector getChildren(Object parent) { - String[] files; - if(parent == null) { - files = FileSystemStorage.getInstance().getRoots(); - return new Vector(Arrays.asList(files)); - } else { - try { - files = FileSystemStorage.getInstance().listFiles((String)parent); - } catch(IOException err) { - Log.e(err); - files = new String[0]; - } - } - String p = (String)parent; - Vector result = new Vector(); - for(String s : files) { - result.add(p + s); - } - return result; - } - - @Override - public boolean isLeaf(Object node) { - return !FileSystemStorage.getInstance().isDirectory((String)node); - } -}; -Tree t = new Tree(tm) { - @Override - protected String childToDisplayLabel(Object child) { - String n = (String)child; - int pos = n.lastIndexOf("/"); - if(pos < 0) { - return n; - } - return n.substring(pos); - } -}; -hi.add(BorderLayout.CENTER, t); -hi.show(); ----- .Simple sample of a tree for the FileSystemStorage API image::img/filesystem-tree.png[Simple sample of a tree for the FileSystemStorage API,scaledwidth=20%] @@ -269,16 +167,12 @@ SQL is pretty powerful and well suited for common tabular data. The Codename One The https://www.codenameone.com/javadoc/com/codename1/db/Database.html[Database] API is a high-level abstraction that allows you to open an arbitrary database file using syntax such as: -[source,java] ----- -Database db = Display.getInstance().openOrCreate("databaseName"); ----- Some SQLite apps ship with a "ready made" database. You allow you to replace the DB file by using the code: [source,java] ---- -String path = Display.getInstance().getDatabasePath("databaseName"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava010Snippet.java[tag=io-java-010,indent=0] ---- You can then use the `FileSystemStorage` class to write the content of your DB file into the path. Notice that it must be a valid SQLite file! @@ -289,69 +183,6 @@ This is useful for applications that need to synchronize with a central server o Working with a database is pretty trivial, the application logic below can send arbitrary queries to the database and present the results in a `Table`. You can probably integrate this code into your app as a debugging tool: -[source,java] ----- -Toolbar.setGlobalToolbar(true); -Style s = UIManager.getInstance().getComponentStyle("TitleCommand"); -FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_QUERY_BUILDER, s); -Form hi = new Form("SQL Explorer", new BorderLayout()); -hi.getToolbar().addCommandToRightBar("", icon, (e) -> { - TextArea query = new TextArea(3, 80); - Command ok = new Command("Execute"); - Command cancel = new Command("Cancel"); - if(Dialog.show("Query", query, ok, cancel) == ok) { - Database db = null; - Cursor cur = null; - try { - db = Display.getInstance().openOrCreate("MyDB.db"); - if(query.getText().startsWith("select")) { - cur = db.executeQuery(query.getText()); - int columns = cur.getColumnCount(); - hi.removeAll(); - if(columns > 0) { - boolean next = cur.next(); - if(next) { - ArrayList data = new ArrayList<>(); - String[] columnNames = new String[columns]; - for(int iter = 0 ; iter < columns ; iter++) { - columnNames[iter] = cur.getColumnName(iter); - } - while(next) { - Row currentRow = cur.getRow(); - String[] currentRowArray = new String[columns]; - for(int iter = 0 ; iter < columns ; iter++) { - currentRowArray[iter] = currentRow.getString(iter); - } - data.add(currentRowArray); - next = cur.next(); - } - Object[][] arr = new Object[data.size()][]; - data.toArray(arr); - hi.add(BorderLayout.CENTER, new Table(new DefaultTableModel(columnNames, arr))); - } else { - hi.add(BorderLayout.CENTER, "Query returned no results"); - } - } else { - hi.add(BorderLayout.CENTER, "Query returned no results"); - } - } else { - db.execute(query.getText()); - hi.add(BorderLayout.CENTER, "Query completed successfully"); - } - hi.revalidate(); - } catch(IOException err) { - Log.e(err); - hi.removeAll(); - hi.add(BorderLayout.CENTER, "Error: " + err); - hi.revalidate(); - } finally { - Util.cleanup(db); - Util.cleanup(cur); - } - } -}); -hi.show(); ----- .Querying the temp demo generated by the SQLDemo application image::img/sql-table.png[Querying the temp demo generated by the SQLDemo application,scaledwidth=20%] @@ -371,38 +202,9 @@ object, which has some similarities to the networking mechanism in JavaScript bu You can send a get request to a URL using something like: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false); -request.addResponseListener((e) -> { - // process the response -}); - -// request will be handled asynchronously -NetworkManager.getInstance().addToQueue(request); ----- Notice that you can also implement the same thing and much more by avoiding the response listener code and instead overriding the methods of the `ConnectionRequest` class which offers many points to override for example: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false) { - protected void readResponse(InputStream input) { - // just read from the response input stream - } - - protected void postResponse() { - // invoked on the EDT after processing is complete to allow the networking code - // to update the UI - } - - protected void buildRequestBody(OutputStream os) { - // writes post data, by default this "just works" but if you want to write this - // manually then override this - } -}; -NetworkManager.getInstance().addToQueue(request); ----- TIP: Notice that overriding `buildRequestBody(OutputStream)` will work for `POST` requests and will replace writing the arguments @@ -410,13 +212,6 @@ IMPORTANT: You don't need to close the output/input streams passed to the reques `NetworkManager` also supports synchronous requests which work in a similar way to `Dialog` through the `invokeAndBlock` call and thus don't block the EDT footnote:[Event Dispatch Thread] illegally. For example: you can do something like this: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false); -// request will be handled synchronously -NetworkManager.getInstance().addToQueueAndWait(request); -byte[] resultOfRequest = request.getData(); ----- Notice that in this case the `addToQueueAndWait` method returned after the connection completed. Also notice that this was totally legal to do on the EDT! @@ -442,7 +237,7 @@ To update the number of threads use: [source,java] ---- -NetworkManager.getInstance().updateThreadCount(4); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava015Snippet.java[tag=io-java-015,indent=0] ---- All the callbacks in the `ConnectionRequest` occur on the network thread and *not on the EDT*! @@ -457,10 +252,7 @@ IMPORTANT: Never change the UI from a `ConnectionRequest` callback. You can eith [source,java] ---- -NetworkManager nm = NetworkManager.getInstance(); -if (nm.isVPNDetectionSupported()) { - boolean vpnActive = nm.isVPNActive(); -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava016Snippet.java[tag=io-java-016,indent=0] ---- IMPORTANT: This is a heuristic signal, not a security control. @@ -488,21 +280,11 @@ Arguments in HTTP are passed differently between `GET` and `POST` methods. That' If you continue your example from above you can do something like this: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false); -request.addArgument("MyArgName", value); ----- This will implicitly add a get argument with the content of `value`. Notice that you don't care what value is. It's implicitly HTTP encoded based on the get/post semantics. In this case it will use the get encoding since you passed `false` to the constructor. A simpler implementation could do something like this: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url + - "MyArgName=" + Util.encodeUrl(value), false); ----- This would be almost identical but doesn't provide the convenience for switching back and forth between `GET`/`POST` and it isn't as fluent. @@ -514,11 +296,6 @@ As you explained above, the `setPost()` method allows you to manipulate the get/ For example, if you wish to have finer grained control over the submission process for example: for making a `HEAD` request you can do this with code like: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false); -request.setHttpMethod("HEAD"); ----- ===== Headers @@ -526,50 +303,17 @@ When communicating with HTTP servers you often pass data within headers for auth Some headers are built-in as direct APIs for example: content type is directly exposed within the API since it's a pretty common use case. You can set the content kind of post request using: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, true); -request.setContentType("text/xml"); ----- You can also add any arbitrary header type you want, for example: a common use case is basic authorization where the authorization header includes the Base64 encoded user/password combination as such: -[source,java] ----- -String authCode = user + ":" + password; -String authHeader = "Basic " + Base64.encode(authCode.getBytes()); -request.addRequestHeader("Authorization", authHeader); ----- This can be tedious to do if you want all requests from your app to use this header. For this use case you can use: -[source,java] ----- -String authCode = user + ":" + password; -String authHeader = "Basic " + Base64.encode(authCode.getBytes()); -NetworkManager.getInstance().addDefaultHeader("Authorization", authHeader); ----- ===== Server headers Server returned headers are a bit trickier to read. You need to subclass the connection request and override the `readHeaders` method for example: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false) { - protected void readHeaders(Object connection) throws IOException { - String[] headerNames = getHeaderFieldNames(connection); - for(String headerName : headerNames) { - String headerValue = getHeader(headerName); - //.... - } - } - protected void readResponse(InputStream input) { - // just read from the response input stream - } -}; -NetworkManager.getInstance().addToQueue(request); ----- Here you can extract the headers one by one to handle complex headers such as cookies, authentication etc. @@ -586,45 +330,17 @@ Notice that the `NetworkManager` error handler takes precedence thus allowing yo For example: to block all network errors from showing anything to the user you could do something like this: -[source,java] ----- -NetworkManager.getInstance().addToQueue(request); -NetworkManager.getInstance().addErrorListener((e) -> e.consume()); ----- The error listener is invoked first with the https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html[NetworkEvent] matching the error. Consuming the event prevents it from propagating further down the chain into the `ConnectionRequest` callbacks. You can also override the error callbacks of the various types in the request for example: for a server error code you can do: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false) { - protected void handleErrorResponseCode(int code, String message) { - if(code == 444) { - // do something - } - } - protected void readResponse(InputStream input) { - // just read from the response input stream - } -}; -NetworkManager.getInstance().addToQueue(request); ----- IMPORTANT: The error callback is triggered in the network thread! + As a result it can't access the UI to show a `Dialog` or anything like that. Another approach is to use the `setFailSilently(true)` method on the `ConnectionRequest`. This will prevent the `ConnectionRequest` from displaying any errors to the user. It's a powerful strategy if you use the synchronous version of the APIs for example: -[source,java] ----- -ConnectionRequest request = new ConnectionRequest(url, false); -request.setFailSilently(true); -NetworkManager.getInstance().addToQueueAndWait(request); -if(request.getResponseCode() != 200) { - // probably an error... -} ----- TIP: This code will work with the synchronous "AndWait" version of the method since the response code will take a while to return for the non-wait version. @@ -644,11 +360,6 @@ Codename One also features a https://www.codenameone.com/javadoc/com/codename1/i By default `GZConnectionRequest` doesn't request gzipped data ( unzips it when its received) but its pretty easy to do so add the HTTP header `Accept-Encoding: gzip` for example: -[source,java] ----- -GZConnectionRequest con = new GZConnectionRequest(); -con.addRequestHeader("Accept-Encoding", "gzip"); ----- Do the rest as usual and you should have smaller responses from the servers. @@ -658,38 +369,11 @@ https://www.codenameone.com/javadoc/com/codename1/io/MultipartRequest.html[Multi You can always submit data in the `buildRequestBody` but this is flaky and has some limitations in devices/size allowed. HTTP standardized file upload capabilities through the multipart request protocol, this is implemented by countless servers and is well documented. Codename One supports this out of the box: -[source,java] ----- -MultipartRequest request = new MultipartRequest(); -request.setUrl(url); -request.addData("myFileName", fullPathToFile, "text/plain") -NetworkManager.getInstance().addToQueue(request); ----- TIP: `MultipartRequest` is a `ConnectionRequest` most stuff you expect from there should work. Even addArgument etc. Since you assume most developers reading this will be familiar with Java here is the way to implement the multipart upload in the servlet API: -[source,java] ----- -@WebServlet(name = "UploadServlet", urlPatterns = {"/upload"}) -@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 100, // 10 MB - maxFileSize = 1024 * 1024 * 150, // 50 MB - maxRequestSize = 1024 * 1024 * 200) // 100 MB -public class UploadServlet extends HttpServlet { - - @Override - public void doPost(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { - Collection parts = req.getParts(); - Part data = parts.iterator().next(); - try(InputStream is = data.getInputStream();) {} - // store or do something with the input stream - } - } -} ----- - ==== Parsing @@ -703,23 +387,6 @@ CSV is probably the easiest to use, the "Comma Separated Values" format is a lis To parse a CSV use the https://www.codenameone.com/javadoc/com/codename1/io/CSVParser.html[CSVParser] class as such: -[source,java] ----- -Form hi = new Form("CSV Parsing", new BorderLayout()); -CSVParser parser = new CSVParser(); -try(Reader r = new CharArrayReader("1997,Ford,E350,\"Super, \"\"luxurious\"\" truck\"".toCharArray());) { - String[][] data = parser.parse(r); - String[] columnNames = new String[data[0].length]; - for(int iter= 0 ; iter < columnNames.length ; iter++) { - columnNames[iter] = "Col " + (iter + 1); - } - TableModel tm = new DefaultTableModel(columnNames, data); - hi.add(BorderLayout.CENTER, new Table(tm)); -} catch(IOException err) { - Log.e(err); -} -hi.show(); ----- .CSV parsing results, notice the escaped parentheses and comma image::img/csv-parsing.png[CSV parsing results notice the escaped parentheses and comma,scaledwidth=30%] @@ -732,11 +399,6 @@ IMPORTANT: Notice that you used `CharArrayReader` from the `com.codename1.io` pa The JSON ("JavaScript Object Notation") format is popular on the web for passing values to/from webservices since it works so well with JavaScript. Parsing JSON is as easy but has two different variations. You can use the https://www.codenameone.com/javadoc/com/codename1/io/JSONParser.html[JSONParser] class to build a tree of the JSON data as such: -[source,java] ----- -JSONParser parser = new JSONParser(); -Hashtable response = parser.parse(reader); ----- The response is a `Map` containing a nested hierarchy of `Collection` (`java.util.List`), Strings and numbers to represent the content of the submitted JSON. To extract the data from a specific path iterate the `Map` keys and recurs into it. @@ -744,115 +406,14 @@ The sample below uses results from https://anapioficeandfire.com/[an API of ice [source,JavaScript] ---- -[ - { - "url": "http://www.anapioficeandfire.com/api/characters/13", - "name": "Chayle", - "culture": "", - "born": "", - "died": "In 299 AC, at Winterfell", - "titles": [ - "Septon" - ], - "aliases": [], - "father": "", - "mother": "", - "spouse": "", - "allegiances": [], - "books": [ - "http://www.anapioficeandfire.com/api/books/1", - "http://www.anapioficeandfire.com/api/books/2", - "http://www.anapioficeandfire.com/api/books/3" - ], - "povBooks": [], - "tvSeries": [], - "playedBy": [] - }, - { - "url": "http://www.anapioficeandfire.com/api/characters/14", - "name": "Gillam", - "culture": "", - "born": "", - "died": "", - "titles": [ - "Brother" - ], - "aliases": [], - "father": "", - "mother": "", - "spouse": "", - "allegiances": [], - "books": [ - "http://www.anapioficeandfire.com/api/books/5" - ], - "povBooks": [], - "tvSeries": [], - "playedBy": [] - }, - { - "url": "http://www.anapioficeandfire.com/api/characters/15", - "name": "High Septon", - "culture": "", - "born": "", - "died": "", - "titles": [ - "High Septon", - "His High Holiness", - "Father of the Faithful", - "Voice of the Seven on Earth" - ], - "aliases": [ - "The High Sparrow" - ], - "father": "", - "mother": "", - "spouse": "", - "allegiances": [], - "books": [ - "http://www.anapioficeandfire.com/api/books/5", - "http://www.anapioficeandfire.com/api/books/8" - ], - "povBooks": [], - "tvSeries": [ - "Season 5" - ], - "playedBy": [ - "Jonathan Pryce" - ] - } -] +include::../demos/javascript/src/main/snippets/developer-guide/io.js[tag=io-javascript-001,indent=0] ---- You will place that into a file named "anapioficeandfire.json" in the src directory to make the next sample simpler: [source,java] ---- -Form hi = new Form("JSON Parsing", new BoxLayout(BoxLayout.Y_AXIS)); -JSONParser json = new JSONParser(); -try(Reader r = new InputStreamReader(Display.getInstance().getResourceAsStream(getClass(), "/anapioficeandfire.json"), "UTF-8");) { - Map data = json.parseJSON(r); - java.util.List> content = (java.util.List>)data.get("root"); // <1> - for(Map obj : content) { // <2> - String url = (String)obj.get("url"); - String name = (String)obj.get("name"); - java.util.List titles = (java.util.List)obj.get("titles"); // <3> - if(name == null || name.length() == 0) { - java.util.List aliases = (java.util.List)obj.get("aliases"); - if(aliases != null && aliases.size() > 0) { - name = aliases.get(0); - } - } - MultiButton mb = new MultiButton(name); - if(titles != null && titles.size() > 0) { - mb.setTextLine2(titles.get(0)); - } - mb.addActionListener((e) -> Display.getInstance().execute(url)); - hi.add(mb); - } -} catch(IOException err) { - Log.e(err); -} -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava032Snippet.java[tag=io-java-032,indent=0] ---- <1> The `JSONParser` returns a `Map` which is great if the root object is a `Map` but sometimes its a list of elements (as is the case above). In this case a special case "root" element is created to contain the actual list of elements. @@ -880,31 +441,10 @@ For typed DTO serialization use the `@Mapped` annotation framework maps / lists where a build-time `Mapper` would be overkill, the `com.codename1.io.JSONWriter` class is the complement of `JSONParser`: -[source,java] ----- -// One-shot encoding of any Map / List / String / Number / Boolean / null tree -String json = JSONWriter.toJson(Map.of("name", "ada", "values", List.of(1, 2, 3))); - -// Streaming variants for large outputs (UTF-8) -JSONWriter.toJson(value, writer); -JSONWriter.toJson(value, outputStream); ----- For tiny request bodies the fluent builders are usually faster to read than a `Map` literal: -[source,java] ----- -String body = JSONWriter.object() - .put("email", email) - .put("password", password) - .toJson(); - -String coords = JSONWriter.array() - .add(JSONWriter.object().put("lat", 37.7749).put("lng", -122.4194)) - .add(JSONWriter.object().put("lat", 51.5074).put("lng", -0.1278)) - .toJson(); ----- Strings are double-quoted with the standard backslash escapes for `"`, `\`, `\n`, `\r`, `\t`, `\b`, `\f`, and control chars below `0x20` are @@ -918,11 +458,6 @@ The https://www.codenameone.com/javadoc/com/codename1/xml/XMLParser.html[XMLPars The simplest usage of `XMLParser` looks a bit like this: -[source,java] ----- -XMLParser parser = new XMLParser(); -Element elem = parser.parse(reader); ----- The https://www.codenameone.com/javadoc/com/codename1/xml/Element.html[Element] contains children and attributes. It represents a tag within the XML document and even the root document itself. You can iterate over the XML tree to extract the data from within the XML file. @@ -947,42 +482,11 @@ https://developers.google.com/maps/documentation/geocoding/[Google Reverse Geoco [source,xml] ---- - - - OK - - - London - London - locality - political - - - - Ontario - ON - administrative_area_level_1 - political - - - Canada - CA - country - political - - - +include::../demos/common/src/main/snippets/developer-guide/io.xml[tag=io-xml-001,indent=0] ---- You want to extract some data above into simpler string results. You can do this using: -[source,java] ----- -Result result = Result.fromContent(input, Result.XML); -String country = result.getAsString("/result/address_component[type='country']/long_name"); -String region = result.getAsString("/result/address_component[type='administrative_area_level_1']/long_name"); -String city = result.getAsString("/result/address_component[type='locality']/long_name"); ----- If you're at all familiar with processing responses from webservices, you will notice that what would require many lines of code of selecting and testing nodes in regular java can now be done in a single line using the new path expressions. @@ -998,149 +502,14 @@ To use the expression processor when calling a webservice, you could use somethi [source,java] ---- -Form hi = new Form("Location", new BoxLayout(BoxLayout.Y_AXIS)); -hi.add("Pinpointing Location"); -Display.getInstance().callSerially(() -> { - Location l = Display.getInstance().getLocationManager().getCurrentLocationSync(); - ConnectionRequest request = new ConnectionRequest("http://maps.googleapis.com/maps/api/geocode/json", false) { - private String country; - private String region; - private String city; - private String json; - - @Override - protected void readResponse(InputStream input) throws IOException { - Result result = Result.fromContent(input, Result.JSON); - country = result.getAsString("/results/address_components[types='country']/long_name"); - region = result.getAsString("/results/address_components[types='administrative_area_level_1']/long_name"); - city = result.getAsString("/results/address_components[types='locality']/long_name"); - json = result.toString(); - } - - @Override - protected void postResponse() { - hi.removeAll(); - hi.add(country); - hi.add(region); - hi.add(city); - hi.add(new SpanLabel(json)); - hi.revalidate(); - } - }; - request.setContentType("application/json"); - request.addRequestHeader("Accept", "application/json"); - request.addArgument("sensor", "true"); - request.addArgument("latlng", l.getLatitude() + "," + l.getLongitude()); - - NetworkManager.getInstance().addToQueue(request); -}); -hi.show(); -[source,java] +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava037Snippet.java[tag=io-java-037,indent=0] ---- The returned JSON looks something like this (notice it's snipped because the data is too long): [source,JavaScript] ---- -{ - "status": "OK", - "results": [ - { - "place_id": "ChIJJ5T9-iFawokRTPGaOginEO4", - "formatted_address": "280 Broadway, New York, NY 10007, USA", - "address_components": [ - { - "short_name": "280", - "types": ["street_number"], - "long_name": "280" - }, - { - "short_name": "Broadway", - "types": ["route"], - "long_name": "Broadway" - }, - { - "short_name": "Lower Manhattan", - "types": [ - "neighborhood", - "political" - ], - "long_name": "Lower Manhattan" - }, - { - "short_name": "Manhattan", - "types": [ - "sublocality_level_1", - "sublocality", - "political" - ], - "long_name": "Manhattan" - }, - { - "short_name": "New York", - "types": [ - "locality", - "political" - ], - "long_name": "New York" - }, - { - "short_name": "New York County", - "types": [ - "administrative_area_level_2", - "political" - ], - "long_name": "New York County" - }, - { - "short_name": "NY", - "types": [ - "administrative_area_level_1", - "political" - ], - "long_name": "New York" - }, - { - "short_name": "US", - "types": [ - "country", - "political" - ], - "long_name": "United States" - }, - { - "short_name": "10007", - "types": ["postal_code"], - "long_name": "10007" - }, - { - "short_name": "1868", - "types": ["postal_code_suffix"], - "long_name": "1868" - } - ], - "types": ["street_address"], - "geometry": { - "viewport": { - "northeast": { - "lng": -74.0044642197085, - "lat": 40.7156470802915 - }, - "southwest": { - "lng": -74.0071621802915, - "lat": 40.7129491197085 - } - }, - "location_type": "ROOFTOP", - "location": { - "lng": -74.00581319999999, - "lat": 40.7142981 - } - } - } - /* SNIPED the rest */ - ] -} +include::../demos/javascript/src/main/snippets/developer-guide/io.js[tag=io-javascript-002,indent=0] ---- .Running the geocode sample above in the simulator @@ -1149,15 +518,6 @@ image::img/processing-package.png[Running the geocode sample above in the simula The XML processor handles global selections by using a double slash anywhere within the expression, for example: -[source,java] ----- -// get all address_component names anywhere in the document with a type "political" -String array[] = result.getAsStringArray("//address_component[type='political']/long_name"); - -// get all types anywhere under the second result (dimension is 0-based) -String array[] = result.getAsStringArray("/result[1]//type"); ----- - NOTE: Notice that Google’s JSON webservice uses plural form for each of the node names in that API (ie. results, address_components, and types) where they don’t in the XML services (that's result, address_component etc.) @@ -1167,105 +527,38 @@ It's also possible to do some more complex expressions. You’ll use the followi [source,xml] ---- - - - Bernard - Tomic - SOUTHPORT - QLD - 1992-10-21 - - - Mathew - Ebden - CHURCHLANDS - WA - 1987-11-26 - - - Lleyton - Hewitt - EXETER - SA - 1981-02-24 - - - +include::../demos/common/src/main/snippets/developer-guide/io.xml[tag=io-xml-002,indent=0] ---- Above, if you want to select the IDs of all players that are ranked in the top 2, you can use an expression like: -[source,java] ----- -int top2[] = result.getAsIntegerArray("//player[@rank < 3]/@id"); ----- TIP: Notice above that the expression is using an attribute for selecting both rank and id. In JSON documents, if you try to select an attribute, it will look for a child node under the attribute name you ask for) If a document is ordered, you might want to select nodes by their position, for example: -[source,java] ----- -String first2[] = result.getAsStringArray("//player[position() < 3]/firstname"); - -String secondLast = result.getAsString("//player[last() - 1]/firstName"); ----- It's also possible to select parent nodes, by using the `..` expression. For example: -[source,java] ----- -int id = result.getAsInteger("//lastname[text()='Hewitt']/../@id"); ----- - Above, you globally find a lastname element with a value of ‘Hewitt’, then grab the parent node of lastname which happens to be the player node, then grab the ID attribute from the player node. Or, you could get the same result from the following simpler statement: -[source,java] ----- -int id = result.getAsInteger("//player[lastname='Hewitt']/@id"); ----- It's also possible to nest expressions, for example: -[source,java] ----- -String id=result.getAsInteger("//player[//address[country/isocode='CA']]/@id"); ----- - In the above example, if the player node had an address object, you’d be selecting all players from Canada. This is a simple example of a nested expression, but they can get much more complex, which will be required as the documents themselves get more complex. + Moving on, to select a node based on the existence of an attribute: -[source,java] ----- -int id[] = result.getAsIntegerArray("//player[@rank]/@id"); ----- Above, you selected the IDs of all ranked players. Conversely, you can select the non-ranked players like this: -[source,java] ----- -int id[] = result.getAsIntegerArray("//player[@rank=null]/@id"); ----- - NOTE: Logical not (!) operators aren't implemented) You can also select by the existence of a child node -[source,java] ----- -int id[] = result.getAsIntegerArray("//player[middlename]/@id"); ----- - Above, you selected all players that have a middle name.
Keep in mind that the Codename One path expression language isn't a full implementation of XPath 1.0, but does already handle many of the most useful features of the specification. @@ -1320,10 +613,6 @@ https://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html#down These methods complement the `Util` methods but go a bit further and feature terse syntax for example: you can download a `ConnectionRequest` to `Storage` using code like this: -[source,java] ----- -request.downloadImageToStorage(url, (img) -> theImageIsHereDoSomethingWithIt(img)); ----- This effectively maps the `ConnectionRequest` directly to a https://www.codenameone.com/javadoc/com/codename1/util/SuccessCallback.html[SuccessCallback] for further processing. @@ -1337,10 +626,6 @@ JavaScript already knows how to download and cache images from the web. `URLImag That's what the new method of `URLImage` does: -[source,java] ----- -public static Image createCachedImage(String imageName, String url, Image placeholder, int resizeRule); ----- A few important things you need to notice about this method: @@ -1355,24 +640,12 @@ If you do use this approach it would be far more efficient when running in the J The `Rest` API makes it easy to invoke a RESTful webservice without many of the nuances of `ConnectionRequest`. You can use it to define the HTTP method and start building based on that. To get a parsed JSON result from a URL you could do: -[source,java] ----- -Map jsonData = Rest.get(myUrl).getAsJsonMap().getResponseData(); ----- For a lot of REST requests this will fail because you need to add an HTTP header indicating that you accept JSON results. You have a special case support for that: -[source,java] ----- -Map jsonData = Rest.get(myUrl).acceptJson().getAsJsonMap().getResponseData(); ----- POST requests use the same builder pattern: -[source,java] ----- -Map jsonData = Rest.post(myUrl).body(bodyValueAsString).getAsJsonMap().getResponseData(); ----- Notice the usage of post and the body builder method. MANY methods exist in the builder class that cover pretty much everything you would expect and then some when it comes to the needs of rest services. @@ -1396,16 +669,6 @@ use `fetchAsJsonList` rather than `fetchAsJsonMap`. The underlying `fetchAsJsonList` unwraps that envelope for you so the callback receives the array directly: -[source,java] ----- -Rest.get("https://api.example.com/items") - .header("Authorization", "Bearer " + token) - .acceptJson() - .fetchAsJsonList(response -> { - List items = response.getResponseData(); - renderItems(items); - }); ----- There is no separate builder for the ambiguous "object or array" case: when the response shape isn't known up-front, use `fetchAsJsonMap` and @@ -1418,38 +681,9 @@ casts and inspects key by key. Once your DTOs are `@Mapped`-annotated (see `<>`), `fetchAsMapped` returns the typed object directly: -[source,java] ----- -// Model -@Mapped -public class Pet { - @JsonProperty("id") public long id; - @JsonProperty("name") public String name; - @JsonProperty("photoUrls") public List photoUrls; -} - -// Call site -Rest.get(baseUrl + "/pet/" + petId) - .header("Authorization", "Bearer " + token) - .acceptJson() - .fetchAsMapped(Pet.class, response -> { - Pet pet = response.getResponseData(); // already typed -- no Map casts - renderPet(pet); - }); ----- For endpoints that return a list of DTOs, use `fetchAsMappedList`: -[source,java] ----- -Rest.get(baseUrl + "/albums") - .header("Authorization", "Bearer " + token) - .acceptJson() - .fetchAsMappedList(Album.class, response -> { - List albums = response.getResponseData(); - renderAlbums(albums); - }); ----- Both builders fall back to the untyped `fetchAsJsonMap` / `fetchAsJsonList` path internally and then route every map element through the build-time @@ -1470,24 +704,13 @@ To get started you would need to sign up to http://twillo.com/[Twilio] and have [source,java] ---- -String accountSID = "----------------"; -String authToken = "---------------"; -String fromPhone = "your Twilio phone number here"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava055Snippet.java[tag=io-java-055,indent=0] ---- TIP: You can open a trial Twilio account and it tags all your SMS's. Notice you would need to use a You based number if you don't want to pay You can now send hello world as an SMS to the end user. Once this is in place sending an SMS through REST is a matter of using the `Rest` API: -[source,java] ----- -Response result = Rest.post("https://api.twilio.com/2010-04-01/Accounts/" + accountSID + "/Messages.json"). - queryParam("To", destinationPhone). - queryParam("From", fromPhone). - queryParam("Body", "Hello World"). - basicAuth(accountSID, authToken)). - getAsJsonMap(); ----- Notice that this is equivalent of this "curl" command: @@ -1507,48 +730,13 @@ The result is in JSON form you ignore it since it isn't that important but it mi [source,JavaScript] ---- -{ - "sid": "[sid value]", - "date_created": "Sat, 09 Sep 2017 19:47:30 +0000", - "date_updated": "Sat, 09 Sep 2017 19:47:30 +0000", - "date_sent": null, - "account_sid": "[sid value]", - "to": "[to phone number]", - "from": "[from phone number]", - "messaging_service_sid": null, - "body": "Sent from your Twilio trial account - Hello World", - "status": "queued", - "num_segments": "1", - "num_media": "0", - "direction": "outbound-api", - "api_version": "2010-04-01", - "price": null, - "price_unit": "USD", - "error_code": null, - "error_message": null, - "uri": "/2010-04-01/Accounts/[sid value]/Messages/SMe802d86b9f2246989c7c66e74b2d84ef.json", - "subresource_uris": { - "media": "/2010-04-01/Accounts/[sid value]/Messages/[message value]/Media.json" - } -} +include::../demos/javascript/src/main/snippets/developer-guide/io.js[tag=io-javascript-003,indent=0] ---- Notice the error message entry which is null meaning there was no error, if there was an error you'd have a message there or an error code that isn't in the 200-210 range. This should display an error message to the user if there was a problem sending the SMS: -[source,java] ----- -if(result.getResponseData() != null) { - String error = (String)result.getResponseData().get("error_message"); - if(error != null) { - ToastBar.showErrorMessage(error); - } -} else { - ToastBar.showErrorMessage("Error sending SMS: " + result.getResponseCode()); -} ----- - === Webservice wizard @@ -1591,17 +779,7 @@ You can now open the `GameOfThronesServiceServer.java` file in the server and it [source,java] ---- -public class GameOfThronesServiceServer { - public static String[] getBookNames() { - // your code goes here... - return null; - } - - public static String[] getBookPovCharacters(String bookName) { - // your code goes here... - return null; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava058Snippet.java[tag=io-java-058,indent=0] ---- All you need to do is fill in the code, for this example you will implement the first method for simplicity: @@ -1609,37 +787,17 @@ All you need to do is fill in the code, for this example you will implement the [source,java] ---- -public class GameOfThronesServiceServer { - public static String[] getBookNames() { - return new String[] { - "A Game of Thrones", "A Clash Of Kings", "A Storm Of Swords", "A Feast For Crows", - "A Dance With Dragons", "The Winds of Winter", "A Dream of Spring" - }; - } - - public static String[] getBookPovCharacters(String bookName) { - // your code goes here... - return null; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava059Snippet.java[tag=io-java-059,indent=0] ---- Now lets open the client side code, in the `GameOfThronesService.java` file you see this -[source,java] ----- -public class GameOfThronesService { - private static final String DESTINATION_URL = "http://localhost:8080/cn1proxy"; - -//... -} ----- The destination URL needs to point at the actual server which you will recall from the new project creation should include `HelloWebServiceWizard`. You can fix the URL to: [source,java] ---- -private static final String DESTINATION_URL = "http://localhost:8080/HelloWebServiceWizard/cn1proxy"; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava061Snippet.java[tag=io-java-061,indent=0] ---- You would need to update the host name of the server for running on a device otherwise the device would need to live within your internal network and point to your IP address. @@ -1652,44 +810,6 @@ The second kind of method uses the async JavaScript style callbacks and accepts You can pick either one of these approaches based on your personal preferences. Here you show both uses with the server API: -[source,java] ----- -Form hi = new Form("WebService Wizard", new BoxLayout(BoxLayout.Y_AXIS)); -Button getNamesSync = new Button("Get Names - Sync"); -Button getNamesASync = new Button("Get Names - ASync"); -hi.add(getNamesSync).add(getNamesASync); - -getNamesSync.addActionListener((e) -> { - try { - String[] books = GameOfThronesService.getBookNames(); - hi.add("--- SYNC"); - for(String b : books) { - hi.add(b); - } - hi.revalidate(); - } catch(IOException err) { - Log.e(err); - } -}); - -getNamesASync.addActionListener((e) -> { - GameOfThronesService.getBookNamesAsync(new Callback() { - @Override - public void onSucess(String[] value) { - hi.add("--- ASYNC"); - for(String b : value) { - hi.add(b); - } - hi.revalidate(); - } - - @Override - public void onError(Object sender, Throwable err, int errorCode, String errorMessage) { - Log.e(err); - } - }); -}); ----- .The final result of the WebService Wizard code image::img/webservice-wizard-result.png[The final result of the WebService Wizard code,scaledwidth=20%] @@ -1718,13 +838,6 @@ NOTE: Caching applies to `GET` operations, it won't work for `POST` or other met There are many methods of interest to keep an eye for: -[source,java] ----- -protected InputStream getCachedData() throws IOException; -protected void cacheUnmodified() throws IOException; -public void purgeCache(); -public static void purgeCacheDirectory() throws IOException; ----- ==== getCachedData() @@ -1761,24 +874,6 @@ For example, it might be important to update the image if it changed but you sti The `CachedDataService` will fetch data if it isn't cached locally and cache it. When you "refresh" it will send a special HTTP request that will send back the data if it has been updated since the last refresh: -[source,java] ----- -CachedDataService.register(); -CachedData d = (CachedData)Storage.getInstance().readObject("LocallyCachedData"); - -if(d == null) { - d = new CachedData(); - d.setUrl("http://...."); -} -// check if there is a new version of this on the server -CachedDataService.updateData(d, new ActionListener() { - public void actionPerformed(ActionEvent ev) { - // invoked when/if the data arrives, we now have a fresh cache - Storage.getInstance().writeObject("LocallyCachedData", d); - } -}); ----- - === Externalizable objects @@ -1797,10 +892,7 @@ Externalizing an object such as h below should work fine: [source,java] ---- -Map h = new HashMap<>(); -h.put("Hi","World"); -h.put("data", new byte[] {(byte)1}); -Storage.getInstance().writeObject("Test", h); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava065Snippet.java[tag=io-java-065,indent=0] ---- For example, notice that some things aren’t polymorphic for example: if you will externalize a `String[]` you will get back an `Object[]` @@ -1810,22 +902,11 @@ IMPORTANT: The externalization process caches objects so the app will seem to wo Implementing the `Externalizable` interface is important when you want to store a proprietary object. In this case you must register the object with the `com.codename1.io.Util` class so the externalization algorithm will be able to recognize it by name by invoking: -[source,java] ----- -Util.register("MyClass", MyClass.class); ----- WARNING: You should do this early on in the app for example: in the `init(Object)` but you shouldn't do it in a static initializer within the object as that might never be invoked! An `Externalizable` object *must* have a *default public constructor* and must implement the following 4 methods: -[source,java] ----- -public int getVersion(); -public void externalize(DataOutputStream out) throws IOException; -public void internalize(int version, DataInputStream in) throws IOException; -public String getObjectId(); ----- The `getVersion()` method returns the current version of the object allowing the stored data to change its structure in the future (the version is then passed when internalizing the object). The object ID is a `String` uniquely representing the object; it corresponds to the class name (in the example above the Unique Name should be `MyClass`). @@ -1835,105 +916,12 @@ Since devices obfuscate the class names this becomes a problem as data is stored Developers need to write the data of the object in the externalize method using the methods in the data output stream and read the data of the object in the internalize method for example: -[source,java] ----- -public void externalize(DataOutputStream out) throws IOException { - out.writeUTF(name); - if(value != null) { - out.writeBoolean(true); - out.writeUTF(value); - } else { - out.writeBoolean(false); - } - if(domain != null) { - out.writeBoolean(true); - out.writeUTF(domain); - } else { - out.writeBoolean(false); - } - out.writeLong(expires); -} - -public void internalize(int version, DataInputStream in) throws IOException { - name = in.readUTF(); - if(in.readBoolean()) { - value = in.readUTF(); - } - if(in.readBoolean()) { - domain = in.readUTF(); - } - expires = in.readLong(); -} ----- Since strings might be null sometimes you also included convenience methods to implement such externalization. This effectively writes a boolean before writing the UTF to show whether the string is null: -[source,java] ----- -public void externalize(DataOutputStream out) throws IOException { - Util.writeUTF(name, out); - Util.writeUTF(value, out); - Util.writeUTF(domain, out); - out.writeLong(expires); -} - -public void internalize(int version, DataInputStream in) throws IOException { - name = Util.readUTF(in); - value = Util.readUTF(in); - domain = Util.readUTF(in); - expires = in.readLong(); -} ----- Assuming you added a new date field to the object you can do the following. Notice that a `Date` is a `long` value in Java that can be null. For completeness the full class is presented below: -[source,java] ----- -public class MyClass implements Externalizable { - private static final int VERSION = 2; - private String name; - private String value; - private String domain; - private Date date; - private long expires; - - public MyClass() {} - - public int getVersion() { - return VERSION; - } - - public String getObjectId() { - return "MyClass"; - } - - public void externalize(DataOutputStream out) throws IOException { - Util.writeUTF(name, out); - Util.writeUTF(value, out); - Util.writeUTF(domain, out); - if(date != null) { - out.writeBoolean(true); - out.writeLong(date.getTime()); - } else { - out.writeBoolean(false); - } - out.writeLong(expires); - } - - public void internalize(int version, DataInputStream in) throws IOException { - name = Util.readUTF(in); - value = Util.readUTF(in); - domain = Util.readUTF(in); - if(version > 1) { - boolean hasDate = in.readBoolean(); - if(hasDate) { - date = new Date(in.readLong()); - } - } - expires = in.readLong(); - } -} ----- Notice that you need to check for compatibility during the reading process as the writing process always writes the latest version of the data. @@ -1941,12 +929,6 @@ Notice that you need to check for compatibility during the reading process as th Codename One provides many tools to simplify the path between networking/IO & GUI. A common task of showing a wait dialog or progress sign while fetching network data can be simplified by using the https://www.codenameone.com/javadoc/com/codename1/components/InfiniteProgress.html[InfiniteProgress] class for example: -[source,java] ----- -InfiniteProgress ip = new InfiniteProgress(); -Dialog dlg = ip.showInifiniteBlocking(); -request.setDisposeOnCompletion(dlg); ----- The process of showing a progress bar for a long IO operation such as downloading is automatically mapped to the IO stream in Codename One using the https://www.codenameone.com/javadoc/com/codename1/components/SliderBridge.html[SliderBridge] class. @@ -1956,20 +938,7 @@ The `SliderBridge` class can bind a `ConnectionRequest` to a `Slider` and effect [source,java] ---- -Form hi = new Form("Download Progress", new BorderLayout()); -Slider progress = new Slider(); -Button download = new Button("Download"); -download.addActionListener((e) -> { - ConnectionRequest cr = new ConnectionRequest("https://www.codenameone.com/img/blog/new_icon.png", false); - SliderBridge.bindProgress(cr, progress); - NetworkManager.getInstance().addToQueueAndWait(cr); - if(cr.getResponseCode() == 200) { - hi.add(BorderLayout.CENTER, new ScaleImageLabel(EncodedImage.create(cr.getResponseData()))); - hi.revalidate(); - } -}); -hi.add(BorderLayout.SOUTH, progress).add(BorderLayout.NORTH, download); -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava072Snippet.java[tag=io-java-072,indent=0] ---- .SliderBridge progress for downloading the image in the slow network mode @@ -1981,11 +950,6 @@ Codename One includes a https://www.codenameone.com/javadoc/com/codename1/io/Log As part of the premium cloud features it's possible to invoke Log.sendLog() to email a log directly to the developer account. Codename One can do that seamlessly based on changes printed into the log or based on exceptions that are uncaught or logged for example: -[source,java] ----- -Log.setReportingLevel(Log.REPORTING_DEBUG); -DefaultCrashReporter.init(true, 2); ----- This code will send a log every 2 minutes to your email if anything was changed. You can place it within the init(Object) method of your application. @@ -2006,117 +970,6 @@ connection. This simple example allows you to create a server and a client assuming the device supports both: -[source,java] ----- -public class MyApplication { - private Form current; - - public void init(Object context) { - try { - Resources theme = Resources.openLayered("/theme"); - UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0])); - } catch(IOException e){ - e.printStackTrace(); - } - } - - public void start() { - if(current != null){ - current.show(); - return; - } - final Form soc = new Form("Socket Test"); - Button btn = new Button("Create Server"); - Button connect = new Button("Connect"); - final TextField host = new TextField("127.0.0.1"); - btn.addActionListener((evt) -> { - soc.addComponent(new Label("Listening: " + Socket.getIP())); - soc.revalidate(); - Socket.listen(5557, SocketListenerCallback.class); - }); - connect.addActionListener((evt) -> { - Socket.connect(host.getText(), 5557, new SocketConnection() { - @Override - public void connectionError(int errorCode, String message) { - System.out.println("Error"); - } - - @Override - public void connectionEstablished(InputStream is, OutputStream os) { - try { - int counter = 1; - while(isConnected()) { - os.write(("Hi: " + counter).getBytes()); - counter++; - Thread.sleep(2000); - } - } catch(Exception err) { - err.printStackTrace(); - } - } - }); - }); - soc.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); - soc.addComponent(btn); - soc.addComponent(connect); - soc.addComponent(host); - soc.show(); - } - - public static class SocketListenerCallback extends SocketConnection { - private Label connectionLabel; - - @Override - public void connectionError(int errorCode, String message) { - System.out.println("Error"); - } - - private void updateLabel(final String t) { - Display.getInstance().callSerially(new Runnable() { - public void run() { - if(connectionLabel == null) { - connectionLabel = new Label(t); - Display.getInstance().getCurrent().addComponent(connectionLabel); - } else { - connectionLabel.setText(t); - } - Display.getInstance().getCurrent().revalidate(); - } - }); - } - - @Override - public void connectionEstablished(InputStream is, OutputStream os) { - try { - byte[] buffer = new byte[8192]; - while(isConnected()) { - int pending = is.available(); - if(pending > 0) { - int size = is.read(buffer, 0, 8192); - if(size == -1) { - return; - } - if(size > 0) { - updateLabel(new String(buffer, 0, size)); - } - } else { - Thread.sleep(50); - } - } - } catch(Exception err) { - err.printStackTrace(); - } - } - } - - public void stop() { - current = Display.getInstance().getCurrent(); - } - - public void destroy() { - } -} ----- === Properties @@ -2125,35 +978,7 @@ simple `Meeting` class like this: [source,java] ---- -public class Meeting { - private Date when; - private String subject; - private int attendance; - - public Date getWhen() { - return when; - } - - public String getSubject() { - return subject; - } - - public int getAttendance() { - return attendance; - } - - public void setWhen(Date when) { - this.when = when; - } - - public void setSubject(String subject) { - this.subject = subject; - } - - public void setAttendance(int attendance) { - this.attendance = attendance; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava075Snippet.java[tag=io-java-075,indent=0] ---- That's a classic POJO and it's the force that underlies JavaBeans and a few tools in Java. @@ -2186,70 +1011,29 @@ This is the same class as the one above written with properties: [source,java] ---- -public class Meeting implements PropertyBusinessObject { - public final Property when = new Property<>("when"); - public final Property subject = new Property<>("subject"); - public final Property attendance = new Property<>("attendance"); - private final PropertyIndex idx = new PropertyIndex(this, "Meeting", when, subject, attendance); - - @Override - public PropertyIndex getPropertyIndex() { - return idx; - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava076Snippet.java[tag=io-java-076,indent=0] ---- This looks a bit like a handful, so start with usage which might clarify a few things, then dig into the class itself. When you used a POJO you did this: -[source,java] ----- -Meeting meet = new Meeting(); -meet.setSubject("My Subject"); -Log.p(meet.getSubject()); ----- With properties you do this: -[source,java] ----- -Meeting meet = new Meeting(); -meet.subject.set("My Subject"); -Log.p(meet.subject.get()); ----- ===== Encapsulation At first glance it looks like you created public fields (which you did) but if you will look at the declaration you will notice the `final` keyword: -[source,java] ----- -public final Property subject = new Property<>("subject"); ----- This means that this code won't compile: -[source,java] ----- -meet.subject = otherValue; ----- All setting/getting must happen through the set/get methods and they can be replaced. For example: this is valid syntax that prevents setting the property to null and defaults it to an empty string: -[source,java] ----- -public final Property subject = new Property<>("subject", "") { - public Meeting set(String value) { - if(value == null) { - return Meeting.this; - } - return super.set(value); - } -}; ----- NOTE: You will discuss the reason for returning the `Meeting` instance below @@ -2266,10 +1050,6 @@ It allows enumerating the properties and iterating over them making them accessi Furthermore all properties are observable with the property change listener. You can write this to instantly print out any change made to the property: -[source,java] ----- -meet.subject.addChangeListener((p) -> Log.p("New property value is: " + p.get())); ----- ==== The cool stuff @@ -2277,51 +1057,15 @@ That's the simple stuff that can be done with properties, but they can do **much For starters all the common methods of `Object` can be implemented with almost no code: -[source,java] ----- -public class Meeting implements PropertyBusinessObject { - public final Property when = new Property<>("when"); - public final Property subject = new Property<>("subject"); - public final Property attendance = new Property<>("attendance"); - private final PropertyIndex idx = new PropertyIndex(this, "Meeting", when, subject, attendance); - - @Override - public PropertyIndex getPropertyIndex() { - return idx; - } - - public String toString() { - return idx.toString(); - } - - @Override - public boolean equals(Object obj) { - return obj.getClass() == getClass() && idx.equals(((TodoTask)obj).getPropertyIndex()); - } - - @Override - public int hashCode() { - return idx.hashCode(); - } -} ----- This is easy thanks to introspection... You already have some simple code that can convert an object to/from JSON Maps for example: this can fill the property values from parsed JSON: -[source,java] ----- -meet.getPropertyIndex().populateFromMap(jsonParsedData); ----- And vice versa: -[source,java] ----- -String jsonString = meet.toJSON(); ----- You also have a simple ORM solution that maps values to table columns and can create tables. It's no hibernate but sqlite isn't big iron so it might be good enough. @@ -2333,12 +1077,6 @@ fields in the constructor and you add a new field later you need to keep the old You added a new syntax: -[source,java] ----- -Meeting meet = new Meeting(). - subject.set("My Subject"). - when.set(new Date()); ----- That's why every property in the definition needed the `Meeting` generic and the set method returns the `Meeting` instance... @@ -2347,15 +1085,6 @@ You're pretty conflicted on this feature and are thinking about removing it. Without this feature the code would look like this: -[source,java] ----- -Meeting meet = new Meeting(); -meet.subject.set("My Subject"); -meet.when.set(new Date()); ----- - - - ===== Seamless serialization @@ -2363,49 +1092,16 @@ Suppose you have an object called `Contacts` which includes contact information [source,java] ---- -public class Contact implements PropertyBusinessObject { - public final IntProperty id = new IntProperty<>("id"); - public final Property name = new Property<>("name"); - public final Property email = new Property<>("email"); - public final Property phone = new Property<>("phone"); - public final Property dateOfBirth = new Property<>("dateOfBirth", Date.class); - public final Property gender = new Property<>("gender"); - public final IntProperty rank = new IntProperty<>("rank"); - public final PropertyIndex idx = new PropertyIndex(this, "Contact", id, name, email, phone, dateOfBirth, gender, rank); - - @Override - public PropertyIndex getPropertyIndex() { - return idx; - } - - public Contact() { - name.setLabel("Name"); - email.setLabel("E-Mail"); - phone.setLabel("Phone"); - dateOfBirth.setLabel("Date Of Birth"); - gender.setLabel("Gender"); - rank.setLabel("Rank"); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava088Snippet.java[tag=io-java-088,indent=0] ---- Standard Java Objects can be serialized in Codename One by implementing the Codename One `Externalizable` interface. You also need to register the `Externalizable` object so the implementation will be aware of it. Codename One business objects are seamlessly `Externalizable` and you need to register them. For example: you can do something like this in your `init(Object)` method: -[source,java] ----- -new Contact().getPropertyIndex().registerExternalizable(); ----- After you do that once you can write/read contacts from storage if you so want: -[source,java] ----- -Storage.getInstance().writeObject("MyContact", contact); - -Contact readContact = (Contact)Storage.getInstance().readObject("MyContact"); ----- This will obviously also work for things like `List` etc. @@ -2415,69 +1111,23 @@ Writing SQL code can be tedious. Which is why `SQLMap` is such an important API If you continue the example from above to show persistence to the SQL database you can do something like this: -[source,java] ----- -private Database db; -private SQLMap sm; -public void init(Object context) { - theme = UIManager.initFirstTheme("/theme"); - Toolbar.setGlobalToolbar(true); - Log.bindCrashProtection(true); - - try { - Contact c = new Contact(); - db = Display.getInstance().openOrCreate("propertiesdemo.db"); // <1> - sm = SQLMap.create(db); // <2> - sm.setPrimaryKeyAutoIncrement(c, c.id); // <3> - sm.createTable(c); // <4> - } catch(IOException err) { - Log.e(err); - } -} ----- In the above code you do the following: -<1> Create or open an SQLite database using the standard syntax -<2> Create a properties binding instance -<3> Define the primary key for contact as `id` and set it to `auto increment` which will give it a unique value from the database -<4> Call SQL's createTable if the table doesn't exist yet! TIP: Notice that now altering a created table isn't possible so if you add a new property you might need to detect that and do an `alter` call manually You can then add entries to the contact table using: -[source,java] ----- -sm.insert(myContact); ----- You can update an entry using: -[source,java] ----- -sm.update(myContact); ----- And delete an entry using: -[source,java] ----- -sm.delete(myContact); ----- Listing the entries is more interesting: -[source,java] ----- -List contacts = sm.select(c, c.name, true, 1000, 0); - -for(PropertyBusinessObject cc : contacts) { - Contact currentContact = (Contact)cc; - - // ... -} ----- The arguments for the `select` method are: @@ -2527,22 +1177,11 @@ That means you need to pay attention to the way you cache objects to avoid a cas Some objects make sense as global objects, you can use the `Preferences` API to store that data directly but then you don't have the type safety that property objects bring to the table. That's where the binding of property objects to preferences makes sense. For example: say you have a global `Settings` property object you can bind it to preferences using: -[source,java] ----- -PreferencesObject.create(settingsInstance).bind(); ----- If settings has a property called `companyName` it would bind into `Preferences` under the `Settings.companyName` entry. You can do some more elaborate bindings such as: -[source,java] ----- -PreferencesObject.create(settingsInstance). - setPrefix("MySettings-"). - setName(settingsInstance.companyName, "company"). - bind(); ----- This would customize all entry keys to start with `MySettings-` instead of `Settings.`. This would also set the company name entry to `company` so in this case instead of `Settings.companyName` you'd have `MySettings-company`. @@ -2550,11 +1189,6 @@ This would customize all entry keys to start with `MySettings-` instead of `Sett One of the bigger features of properties are their ability to bind UI to a property. For example: if you continue the sample above with the `Contact` class, say you have a text field on the form and you want the property (which you mapped to the database) to have the value of the text field. You could do something like this: -[source,java] ----- -myNameTextField.setText(myNameTextField.getText()); -myNameTextField.addActionListener(e -> myContact.name.set(myNameTextField.getText())); ----- That would work but what if you changed the property value, that wouldn't be reflected in the text field? @@ -2562,70 +1196,14 @@ Also that works for text field but what about other types for example: numbers, Binding makes this all seamless. For example: the code above can be written as: -[source,java] ----- -UiBinding uib = new UiBinding(); -uib.bind(myNameTextField, myContact.name); ----- The cool thing is that this works with many component types and property types almost magically. Binding works by using an adapter class to convert the data to/from the component. The adapter itself works with a generic converter for example: this code: -[source,java] ----- -uib.bind(myRankTextField, myContact.rank); ----- Seems like the one above but it takes a String that's returned by the text field and seamlessly converts it to the integer needed by rank. This also works in the other direction... You can build a UI that would allow you to edit the `Contact` property in memory: -[source,java] ----- -Container resp = new Container(BoxLayout.y()); -UiBinding uib = new UiBinding(); - -TextField nameTf = new TextField(); -uib.bind(c.name, nameTf); -resp.add(c.name.getLabel()). // <1> - add(nameTf); - -TextField emailTf = new TextField(); -emailTf.setConstraint(TextField.EMAILADDR); -uib.bind(c.email, emailTf); -resp.add(c.email.getLabel()). - add(emailTf); - -TextField phoneTf = new TextField(); -phoneTf.setConstraint(TextField.PHONENUMBER); -uib.bind(c.phone, phoneTf); -resp.add(c.phone.getLabel()). - add(phoneTf); - -Picker dateOfBirth = new Picker(); -dateOfBirth.setType(Display.PICKER_TYPE_DATE); // <2> -uib.bind(c.dateOfBirth, dateOfBirth); -resp.add(c.dateOfBirth.getLabel()). - add(dateOfBirth); - -ButtonGroup genderGroup = new ButtonGroup(); -RadioButton male = RadioButton.createToggle("Male", genderGroup); -RadioButton female = RadioButton.createToggle("Female", genderGroup); -RadioButton undefined = RadioButton.createToggle("Undefined", genderGroup); -uib.bindGroup(c.gender, new String[] {"M", "F", "U"}, male, female, undefined); // <3> -resp.add(c.gender.getLabel()). - add(GridLayout.encloseIn(3, male, female, undefined)); - -TextField rankTf = new TextField(); -rankTf.setConstraint(TextField.NUMERIC); -uib.bind(c.rank, rankTf); // <4> -resp.add(c.rank.getLabel()). - add(rankTf); ----- - -<1> Notice the use of the property's label, which allows better encapsulation -<2> You can bind picker seamlessly -<3> You can bind many radio buttons to a single property to allow the user to select the gender, notice that labels and values can be different for example: "Male" selection will translate to "M" as the value -<4> Numeric bindings "work" .Properties form for the contact image::img/properties-demo-binding.png[Properties form for the contact,scaledwidth=20%] @@ -2649,32 +1227,12 @@ By using the `disconnect()` method in `Binding` you can separate the UI from the Up until now this was pretty cool but if you looked at the UI construction code above you would see that it's pretty full of boilerplate code. The thing about boilerplate is that it shows where automation can be applied, that's the exact idea behind the magical "InstantUI" class. This means that the UI above can be generated using this code: -[source,java] ----- -InstantUI iui = new InstantUI(); -iui.excludeProperty(myContact.id); // <1> -iui.setMultiChoiceLabels(myContact.gender, "Male", "Female", "Undefined"); // <2> -iui.setMultiChoiceValues(myContact.gender, "M", "F", "U"); -Container cnt = iui.createEditUI(myContact, true); // <3> ----- - -<1> The id property is useful for database storage but you want to exclude it from the UI -<2> This implements the `gender` toggle button selection, you provide a hint to the UI so labels and values differ -<3> You create the UI from the screenshot above with one line and it's seamlessly bound to the properties of myContact. The second argument indicates the "auto commit" status. This still carries most of the flexibilities of the regular binding for example: you can still get a binding object using: -[source,java] ----- -UiBinding.Binding b = iui.getBindings(cnt); ----- You might not have noticed this but in the previous verbose code you had lines like: -[source,java] ----- -emailTf.setConstraint(TextField.EMAILADDR); ----- You might be surprised to know that this will still work seamlessly without doing anything, as would the picker component used to pick a date... @@ -2684,9 +1242,5 @@ However, how do you know to use an email constraint for the email property? You've some special case defaults for some common property names, so if your property is named email it will use an email constraint by default. If it's named url or password etc. It will do the "right thing" unless you explicitly state otherwise. You can customize the constraint for a specific property using something like: -[source,java] ----- -iui.setTextFieldConstraint(contact.email, TextArea.ANY); ----- This will override the defaults you've in place. The goal of this tool is to have sensible "magical" defaults that "work." diff --git a/docs/developer-guide/performance.asciidoc b/docs/developer-guide/performance.asciidoc index 0a261882c98..04691d4a89a 100644 --- a/docs/developer-guide/performance.asciidoc +++ b/docs/developer-guide/performance.asciidoc @@ -75,13 +75,6 @@ This is useful for platform abstraction classes where one implementation is guar For example, Codename One annotates: -[source,java] ----- -@Concrete(name = "com.codename1.impl.ios.IOSImplementation") -public abstract class CodenameOneImplementation { - // ... -} ----- NOTE: This hint is intended for ParparVM native translation and doesn't apply to the JavaScript back end. @@ -172,13 +165,6 @@ speedup. `Simd` is accessed as a singleton: -[source,java] ----- -Simd simd = Simd.get(); // equivalent to CN.getSimd() -if (simd.isSupported()) { - // native SIMD is wired up on this platform -} ----- The returned object is safe to cache in a field as long as the cache is refreshed across simulator restarts (the simulator may swap the backing @@ -198,12 +184,6 @@ arbitrary `new`-allocated arrays to `Simd` primitives. Every buffer used with a `Simd` primitive must so come from one of the allocation helpers on `Simd`: -[source,java] ----- -byte[] bytes = simd.allocByte(64); // heap, 16-byte aligned, registered -int[] ints = simd.allocInt(32); // heap, 16-byte aligned, registered -float[] floats = simd.allocFloat(32); // heap, 16-byte aligned, registered ----- // vale-skip: write-good.TooWordy — 'minimum size' is a precise lower bound; 'least size' would be wrong. All three methods enforce a minimum size of 16 elements and throw @@ -220,23 +200,6 @@ simulator or undefined results on device. ==== A minimal End-to-End example -[source,java] ----- -Simd simd = Simd.get(); - -byte[] a = simd.allocByte(64); -byte[] b = simd.allocByte(64); -byte[] out = simd.allocByte(64); - -// fill a and b with your data (plain array writes are fine) -for (int i = 0; i < 64; i++) { - a[i] = (byte) i; - b[i] = (byte) (64 - i); -} - -// saturating byte add, one vector instruction per 16 bytes on NEON -simd.add(a, b, out, 0, 64); ----- All `Simd` ops take explicit `offset` / `length` pairs so callers can chunk a large buffer through a small scratch array without reallocating. @@ -257,16 +220,6 @@ solution is to let the compiler place these buffers on the call stack where they cost nothing to create and are reclaimed automatically when the method returns. This is what the `alloca*` family exposes: -[source,java] ----- -byte[] scratchB = simd.allocaByte(64); -int[] scratchI = simd.allocaInt(32); -float[] scratchF = simd.allocaFloat(32); - -// Deterministic initial contents: -byte[] zeroed = simd.allocaByteZeroed(64); -int[] filled = simd.allocaIntFilled(32, -1); ----- On the simulator (and on Android) these behave like heap allocations with registration. On ParparVM the translator intercepts each `alloca*` call @@ -366,8 +319,7 @@ point validates its array arguments against the same set and throws [source,text] ---- -java.lang.IllegalArgumentException: SIMD array argument was not -allocated using Simd.alloc*(). objectId=… +include::../demos/common/src/main/snippets/developer-guide/performance.txt[tag=performance-text-001,indent=0] ---- Combined with the build-time verifier, both static analysis and dynamic @@ -486,7 +438,7 @@ This can be accomplished with a single line of code: [source,java] ---- -Log.bindCrashProtection(true); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava006Snippet.java[tag=performance-java-006,indent=0] ---- You place this in the `init(Object)` method so all future on-device errors are emailed to you. Internally this method uses the `Display.getInstance().addEdtErrorHandler()` API to bind error listeners to the EDT. When an exception is thrown there it's swallowed (using `ActionEvent.consume()`). The `Log` data is then sent using `Log.sendLog()`. @@ -516,46 +468,17 @@ This led to the discovery that you're loading the same resource file over and ov In the new Contacts demo you have a share button for each contact, the code for constructing a `ShareButton` looks like this: -[source,java] ----- -public ShareButton() { - setUIID("ShareButton"); - FontImage.setMaterialIcon(this, FontImage.MATERIAL_SHARE); - addActionListener(this); - shareServices.addElement(new SMSShare()); - shareServices.addElement(new EmailShare()); - shareServices.addElement(new FacebookShare()); -} ----- This seems reasonable until you realize that the constructors for `SMSShare`, `EmailShare` & `FacebookShare` load the icons for each of those... These icons are in a shared resource file that you load and don't cache. The initial workaround was to cache this resource but a better solution was to convert this code: -[source,java] ----- -public SMSShare() { - super("SMS", Resources.getSystemResource().getImage("sms.png")); -} ----- Into this code: [source,java] ---- -public SMSShare() { - super("SMS", null); -} - -@Override -public Image getIcon() { - Image i = super.getIcon(); - if(i == null) { - i = Resources.getSystemResource().getImage("sms.png"); - setIcon(i); - } - return i; -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava009Snippet.java[tag=performance-java-009,indent=0] ---- This way the resource uses lazy loading as needed. @@ -582,21 +505,11 @@ Here you defined: [source,java] ---- -private long lastScroll; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/PerformanceJava010Snippet.java[tag=performance-java-010,indent=0] ---- Then you did this within the background loading thread: -[source,java] ----- -// don't do anything while we are scrolling or animating -long idle = System.currentTimeMillis() - lastScroll; -while(idle < 1500 || contactsDemo.getAnimationManager().isAnimating() || scrollY != contactsDemo.getScrollY()) { - scrollY = contactsDemo.getScrollY(); - Util.sleep(Math.min(1500, Math.max(100, 2000 - ((int)idle)))); - idle = System.currentTimeMillis() - lastScroll; -} ----- This effectively sleeps when the user interacts with the UI and loads the images if the user hasn't touched the UI in a while. @@ -604,27 +517,8 @@ Notice that you also check if the scroll changes, this allows you to notice case All you need to do now is update the `lastScroll` variable whenever user interaction is in place. This works for user touches: -[source,java] ----- -parentForm.addPointerDraggedListener(e -> lastScroll = System.currentTimeMillis()); ----- This works for general scrolling: -[source,java] ----- -contactsDemo.addScrollListener(new ScrollListener() { - int initial = -1; - @Override - public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) { - // scrolling is sensitive on devices... - if(initial < 0) { - initial = scrollY; - } - lastScroll = System.currentTimeMillis(); - ... - } -}); ----- NOTE: Due to technical constraints you can't use a lambda in this specific case... diff --git a/docs/developer-guide/security.asciidoc b/docs/developer-guide/security.asciidoc index 7652cbf8fb9..ea68f37980c 100644 --- a/docs/developer-guide/security.asciidoc +++ b/docs/developer-guide/security.asciidoc @@ -33,25 +33,19 @@ NOTE: This isn't encoding or encryption — it's a simple obfuscation of the dat There are two simple methods in the `Util` class: -[source,java] ----- -public static String xorDecode(String s); -public static String xorEncode(String s); ----- They use a simple xor based obfuscation to make a String less readable. For example, if you've code like this: [source,java] ---- -private static final String SECRET = "Don't let anyone see this...."; +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava002Snippet.java[tag=security-java-002,indent=0] ---- You might be concerned about the secret, then this would make it slightly harder to find out: [source,java] ---- -// Don't let anyone see this.... -private static final String SECRET = Util.xorDecode("RW1tI3Ema219KmpidGFhdTFhdnE1Yn9xajQ1MjM="); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava003Snippet.java[tag=security-java-003,indent=0] ---- Notice that this **isn't secure**, if you have a crucial value that must not be found you need to store it in the server. No alternative exists because everything that's sent to the client can be compromised by a determined hacker. @@ -62,20 +56,7 @@ Codename One's built-in user-specific constants are obfuscated with this method. [source,java] ---- -Form hi = new Form("Encoder", BoxLayout.y()); -TextField bla = new TextField("", "Type Text Here", 20, TextArea.ANY); -TextArea encoded = new TextArea(); -SpanLabel decoded = new SpanLabel(); -hi.addAll(bla, encoded, decoded); -bla.addDataChangedListener((a, b) -> { - String s = bla.getText(); - String e = Util.xorEncode(s); - encoded.setText(e); - decoded.setText(Util.xorDecode(e)); - hi.getContentPane().animateLayout(100); -}); - -hi.show(); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava004Snippet.java[tag=security-java-004,indent=0] ---- This allows you to type in the first text field and the second text area shows the encoded result. A text area is used so copy/paste is easy. @@ -91,10 +72,6 @@ securing/encrypting your app more painful than it should. Codename One supports full encryption of the `Storage` (notice the distinction, `Storage` isn't `FileSystemStorage`). This is available by installing the bouncy castle cn1lib from the extensions menu then using one line of code -[source,java] ----- -EncryptedStorage.install("your-pass-encryption-key"); ----- TIP: You would want that code within your `init(Object)` method @@ -105,23 +82,9 @@ a new password. This works through a new mechanism in storage where you can replace the storage instance with another instance using: -[source,java] ----- -Storage.setStorageInstance(new MyCustomStorageSubclass()); ----- You can leverage that knowledge to change the encryption password on the encryption storage using pseudo-code like this: -[source,java] ----- -EncryptedStorage.install(oldKey); -InputStream is = Storage.getInstance().createInputStream(storageFileName); -byte[] data = Util.readInputStream(is); -EncryptedStorage.install(newKey); -OutputStream o = Storage.getInstance().createOutputStream("TestEncryption"); -o.write(data); -o.close(); ----- NOTE: It isn't a good idea to replace storage objects when an app is running so this is purely for this special case... @@ -137,13 +100,6 @@ Some values are too sensitive for `Storage` or `Preferences`, which are plaintex `com.codename1.security.SecureStorage` keeps small string values in the operating system's secure keychain (iOS Keychain Services and the Android Keystore) so they're protected by the platform's secure enclave instead of being written to app storage. The quiet, no-prompt API is three calls: -[source,java] ----- -SecureStorage ss = SecureStorage.getInstance(); -ss.set("auth.token", token); -String token = ss.get("auth.token"); // null if absent -ss.remove("auth.token"); ----- On a platform without a keychain the base implementation returns `false` / `null`, so treat a `null` read as "not available" and fall back accordingly. @@ -151,11 +107,6 @@ On a platform without a keychain the base implementation returns `false` / `null Baking an API key (for example a Google Maps key) into your app is unsafe: anyone can extract it from the binary, and rotating it means shipping a new build. `com.codename1.security.Secrets` solves this. You define the secret once in the Codename One Cloud vault, and the app fetches it at runtime and caches it in `SecureStorage`: -[source,java] ----- -// off the EDT -- the first call may hit the network: -String mapsKey = Secrets.get("googlemaps.key"); ----- `get` returns the keychain-cached value when present, otherwise it fetches from the vault over TLS, caches it, and returns it. `refresh(name)` forces a fresh fetch (after you rotate the value server-side) and `clear(name)` drops the cached copy. @@ -188,15 +139,11 @@ To block copy & paste globally use: [source,java] ---- -Display.getInstance().setProperty("blockCopyPaste", "true"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava010Snippet.java[tag=security-java-010,indent=0] ---- To block copy & paste on a specific field do: -[source,java] ----- -textCmp.putClientProperty("blockCopyPaste", Boolean.TRUE); ----- NOTE: Notice that the inverse of using `false` might not work as expected @@ -210,11 +157,7 @@ For obvious reasons it's hard to accurately detect a jailbroken or rooted device [source,java] ---- -if(Display.getInstance().isJailbrokenDevice()) { - // probably jailbroken or rooted -} else { - // probably not -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava012Snippet.java[tag=security-java-012,indent=0] ---- Notice that this isn't right, you can't be 100% sure as there are no official ways to detect jailbreak. That's why it's crucial to encrypt everything and assume the device was compromised to begin with when dealing with sensitive data. Still it's worthwhile to use these APIs to make the life of an attacker a little bit harder. @@ -237,21 +180,12 @@ When you'd rather respond in-app than be hard-killed -- for example to block onl [source,java] ---- -import com.codename1.security.DeviceIntegrity; - -if (DeviceIntegrity.isDeviceCompromised()) { - // root / jailbreak / Frida / emulator detected -- do not hard-exit, - // instead degrade: disable the transfer button, require step-up auth, etc. - for (String reason : DeviceIntegrity.getCompromiseReasons()) { - // reason is a machine-readable code: "root", "frida", "emulator", "jailbreak" - Log.p("Device compromise signal: " + reason); - } -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava013Snippet.java[tag=security-java-013,indent=0] ---- `isDeviceCompromised()` aggregates the same root/jailbreak/Frida signals as the launch gates plus emulator heuristics, but never terminates the app. On the simulator and unsupported ports it simply returns `false`. -NOTE: If your security policy mandates a specific commercial Mobile Threat Defense (MTD) / RASP vendor (Zimperium, Promon SHIELD, etc.), you integrate that vendor's SDK through the standard `android.gradleDep` / `ios.pods` build hints -- Codename One's built-in checks complement, but do not claim to replace, a dedicated MTD product. +NOTE: If your security policy mandates a specific commercial Mobile Threat Defense (MTD) / RASP vendor (Zimperium, Promon SHIELD, etc.), you integrate that vendor's SDK through the standard `android.gradleDep` / `ios.pods` build hints -- Codename One's built-in checks complement, but don't claim to replace, a dedicated MTD product. ==== Platform attestation: Google Play Integrity & Apple App Attest @@ -264,23 +198,6 @@ Enable it with a build hint, which bundles the SDK (Android) or links the entitl The runtime API is identical on both platforms -- request a token bound to a fresh server nonce, then POST it to your backend for verification: -[source,java] ----- -import com.codename1.security.DeviceIntegrity; - -// serverNonce is generated by, and round-tripped from, your backend so it can -// detect replay. The returned token is opaque and MUST be verified server-side. -DeviceIntegrity.requestIntegrityToken(serverNonce).onResult((token, err) -> { - if (err != null) { - // Attestation unavailable -- treat the device as untrusted / - // require an additional verification step before allowing the transfer. - return; - } - // POST `token` to your backend. The backend decrypts/validates the verdict - // with Google / Apple and decides whether to authorize the transfer. - authorizeTransferAfterServerVerification(token); -}); ----- `DeviceIntegrity.isAttestationSupported()` reports whether attestation is available and was bundled into this build. On the simulator, on unsupported ports, or when the build hint is off, `requestIntegrityToken()` completes with an error rather than throwing. @@ -299,28 +216,17 @@ At runtime you get the same information for granular control: [source,java] ---- -import com.codename1.security.DeviceIntegrity; - -// Enumerate the enabled accessibility services (component ids "pkg/.Service"). -String[] services = DeviceIntegrity.getEnabledAccessibilityServices(); - -// True if any enabled service is outside your trusted allow-list. -if (DeviceIntegrity.hasUntrustedAccessibilityService("com.google.android.marvin.talkback")) { - // Likely accessibility-abusing malware -- block the sensitive action. -} +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava015Snippet.java[tag=security-java-015,indent=0] ---- For the screens that actually matter (PIN entry, transfer confirmation), mark them secure so the OS blocks screenshots, screen recording, and accessibility screen scraping while they're displayed: [source,java] ---- -// Entering a sensitive screen (Android FLAG_SECURE; no-op on iOS): -DeviceIntegrity.setSecureScreen(true); -// ... leaving it: -DeviceIntegrity.setSecureScreen(false); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava016Snippet.java[tag=security-java-016,indent=0] ---- -NOTE: Accessibility-service enumeration and `setSecureScreen()` are Android concepts; on iOS they are no-ops (iOS has no equivalent third-party accessibility-service threat model). Use `ios.disableScreenshots=true` to block screen capture on iOS as described in <>. +NOTE: Accessibility-service enumeration and `setSecureScreen()` are Android concepts; on iOS they're no-ops (iOS has no equivalent third-party accessibility-service threat model). Use `ios.disableScreenshots=true` to block screen capture on iOS as described in <>. === Strong Android certificates @@ -374,21 +280,6 @@ This is the attack certificate pinning (or SSL pinning) aims to prevent. You cod To do this you have a https://github.com/codenameone/SSLCertificateFingerprint/[cn1lib]. That fetches the certificate fingerprint from the server, you can check this fingerprint against a list of "authorized" keys to decide whether it's valid. You can install the `SSLCertificateFingerprint` from the extensions section in #Codename One Settings# and use something like this to verify your server: -[source,java] ----- -if(CheckCert.isCertCheckingSupported()) { - String f = CheckCert.getFingerprint(myHttpsURL); - if(validKeysList.contains(f)) { - // OK it's a good certificate proceed - } else { - if(Dialog.show("Security Warning", "WARNING: it is possible your commmunications are being tampered! We suggest quitting the app at once!", "Quit", "Continue")) { - Display.getInstance().exitApplication(); - } - } -} else { - // certificate fingerprint checking isn't supported on this platform... It's your decision whether to proceed or not -} ----- Notice that once connection is established you don't need to verify again for the current application run. @@ -410,21 +301,6 @@ NOTE: Cryptographic operations only throw `com.codename1.security.CryptoExceptio `com.codename1.security.Hash` implements MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512. SHA-256 is the recommended default for new code; MD5 and SHA-1 are exposed only for compatibility with older protocols since both are broken for collision resistance: -[source,java] ----- -import com.codename1.security.Hash; - -// One-shot -byte[] digest = Hash.sha256("hello".getBytes("UTF-8")); -String hex = Hash.toHex(digest); // 64-character lowercase hex -byte[] back = Hash.fromHex(hex); // round-trip helper - -// Streaming -Hash h = Hash.create(Hash.SHA256); -h.update(part1); -h.update(part2); -byte[] full = h.digest(); // hash is reset after digest() ----- The algorithm identifier strings (`Hash.MD5`, `Hash.SHA256`, etc.) are also accepted with or without dashes and in any case (`"sha256"` and `"SHA-256"` are equivalent). @@ -432,18 +308,6 @@ The algorithm identifier strings (`Hash.MD5`, `Hash.SHA256`, etc.) are also acce `com.codename1.security.Hmac` implements HMAC (RFC 2104) on top of any of the hash algorithms above. Use HMAC whenever a message authentication code is needed -- API request signing, session cookie tamper detection, JWT signing with the `HS` family, or TOTP token generation: -[source,java] ----- -import com.codename1.security.Hmac; - -byte[] tag = Hmac.sha256(secret, message); - -// Streaming -Hmac h = Hmac.create(Hash.SHA256, secret); -h.update(part1); -h.update(part2); -byte[] tag2 = h.doFinal(); ----- When comparing authentication tags, use `Hmac.constantTimeEquals(a, b)` instead of `Arrays.equals(a, b)` -- the latter short-circuits on the first mismatch and is vulnerable to timing attacks. @@ -453,31 +317,13 @@ When comparing authentication tags, use `Hmac.constantTimeEquals(a, b)` instead [source,java] ---- -import com.codename1.security.SecureRandom; - -byte[] iv = SecureRandom.bytes(12); // 12-byte AES-GCM nonce -int pin = SecureRandom.intBelow(1_000_000); // bias-free 6-digit code -long id = SecureRandom.longBelow(1L << 53); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava020Snippet.java[tag=security-java-020,indent=0] ---- ==== Symmetric encryption (AES) `com.codename1.security.Cipher` exposes AES through the platform's native AES implementation. AES-GCM is the recommended default because it's authenticated -- a single tag-mismatch failure detects any tampering of the ciphertext or the associated data: -[source,java] ----- -import com.codename1.security.Cipher; -import com.codename1.security.KeyGenerator; -import com.codename1.security.SecretKey; -import com.codename1.security.SecureRandom; - -SecretKey key = KeyGenerator.aes(256); -byte[] nonce = SecureRandom.bytes(12); -byte[] aad = "v1".getBytes("UTF-8"); - -byte[] ciphertext = Cipher.aesEncrypt(Cipher.AES_GCM, key, nonce, aad, plaintext); -byte[] decrypted = Cipher.aesDecrypt(Cipher.AES_GCM, key, nonce, aad, ciphertext); ----- The `Cipher` class exposes the standard JCE-style transformation strings as constants: `Cipher.AES_GCM`, `Cipher.AES_CBC_PKCS5`, `Cipher.AES_CBC`, and `Cipher.AES_ECB_PKCS5`. For GCM, the 16-byte authentication tag is appended to the ciphertext (matching the JCE convention). ECB is exposed only for interop with legacy systems -- it leaks structure and should never be used for new designs. @@ -485,33 +331,9 @@ The `Cipher` class exposes the standard JCE-style transformation strings as cons `com.codename1.security.Cipher` also covers RSA encryption, and `com.codename1.security.Signature` covers digital signatures (RSA and ECDSA). Keys are represented by `com.codename1.security.PublicKey` and `com.codename1.security.PrivateKey`, which wrap X.509 SubjectPublicKeyInfo and PKCS#8 DER blobs respectively -- the same encodings used by OpenSSL: -[source,java] ----- -import com.codename1.security.Cipher; -import com.codename1.security.KeyGenerator; -import com.codename1.security.KeyPair; -import com.codename1.security.Signature; - -// Generate a fresh RSA-2048 key pair (run on a background thread; key -// generation can take a noticeable amount of time) -KeyPair kp = KeyGenerator.rsa(2048); - -// Encrypt a small payload (e.g. wrap an AES key) -byte[] sealed = Cipher.rsaEncrypt(Cipher.RSA_OAEP_SHA256, kp.getPublicKey(), payload); -byte[] opened = Cipher.rsaDecrypt(Cipher.RSA_OAEP_SHA256, kp.getPrivateKey(), sealed); - -// Sign / verify -byte[] sig = Signature.sign(Signature.SHA256_WITH_RSA, kp.getPrivateKey(), data); -boolean ok = Signature.verify(Signature.SHA256_WITH_RSA, kp.getPublicKey(), data, sig); ----- To use a key that was generated outside the app, feed the DER bytes to the static factory methods: -[source,java] ----- -PublicKey pub = PublicKey.rsa(x509SpkiBytes); // openssl rsa -pubout -outform DER -PrivateKey priv = PrivateKey.rsa(pkcs8DerBytes); // openssl pkcs8 -topk8 -nocrypt -outform DER ----- The `Signature` class exposes the JCE algorithm strings as constants: `SHA256_WITH_RSA`, `SHA384_WITH_RSA`, `SHA512_WITH_RSA`, `SHA256_WITH_ECDSA`, `SHA384_WITH_ECDSA`, and `SHA512_WITH_ECDSA`. @@ -521,36 +343,13 @@ The `Signature` class exposes the JCE algorithm strings as constants: `SHA256_WI [source,java] ---- -import com.codename1.security.Jwt; -import java.util.LinkedHashMap; -import java.util.Map; - -Map claims = new LinkedHashMap(); -claims.put("sub", "user-123"); -claims.put("exp", System.currentTimeMillis() / 1000 + 3600); - -// Sign with a shared secret (HS256) -String token = Jwt.signHs256(claims, "secret".getBytes("UTF-8")); - -// Parse + verify -Jwt parsed = Jwt.parse(token); -if (!parsed.verifyHs256("secret".getBytes("UTF-8"))) { - throw new SecurityException("bad signature"); -} -String subject = (String) parsed.getClaim("sub"); +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava024Snippet.java[tag=security-java-024,indent=0] ---- For RSA-signed tokens (RS256/384/512) and ECDSA-signed tokens (ES256/384/512), pass the key to the dynamic overloads: -[source,java] ----- -KeyPair kp = KeyGenerator.rsa(2048); -String rs = Jwt.sign(claims, kp.getPrivateKey(), Jwt.RS256); -Jwt read = Jwt.parse(rs); -boolean ok = read.verify(kp.getPublicKey()); ----- -WARNING: `Jwt.parse(token)` does not verify the signature -- you must call one of the `verify*` methods afterwards. Tokens with an `alg` header of `none` are rejected by `verify` unless you explicitly call `setVerifyAllowNoneAlgorithm(true)` on the parsed JWT. Accepting unsigned tokens is a critical security bug in almost every deployment, so leave it off unless you have decided that the transport is trusted. +WARNING: `Jwt.parse(token)` doesn't verify the signature -- you must call one of the `verify*` methods afterwards. Tokens with an `alg` header of `none` are rejected by `verify` unless you explicitly call `setVerifyAllowNoneAlgorithm(true)` on the parsed JWT. Accepting unsigned tokens is a critical security bug in almost every deployment, so leave it off unless you have decided that the transport is trusted. ==== HOTP / TOTP one-time passwords @@ -558,27 +357,9 @@ WARNING: `Jwt.parse(token)` does not verify the signature -- you must call one o Shared secrets are commonly distributed as Base32 strings (the format embedded in QR codes by authenticator apps). `com.codename1.security.Base32` handles the encoding: -[source,java] ----- -import com.codename1.security.Base32; -import com.codename1.security.Otp; - -byte[] secret = Base32.decode("JBSWY3DPEHPK3PXP"); - -// Generate the current 6-digit TOTP code (30-second step, SHA-1) -String code = Otp.totp(secret); - -// Verify the code the user typed in, allowing one step of clock skew on -// either side (so the previous, current, and next codes are all accepted) -boolean ok = Otp.verifyTotp(secret, userInput, 1); ----- For HOTP, supply the counter explicitly and increment it after every successful authentication: -[source,java] ----- -String code = Otp.hotp(secret, counter++, 6); ----- ==== Walkthrough: Adding two-factor authentication to a sign-in flow @@ -593,20 +374,7 @@ The TOTP secret should be generated and stored on your *server*, not on the Code [source,java] ---- -import com.codename1.security.Otp; -import com.codename1.security.SecureRandom; - -// 1. Generate (or fetch from your server) a 20-byte secret -- 160 bits is the -// de-facto standard for authenticator compatibility. -byte[] secret = SecureRandom.bytes(20); - -// 2. Build the otpauth URI. The authenticator app stores the secret against -// the "Acme Bank: alice@example.com" label and tags it with the issuer -// so it can show "Acme Bank" next to the rotating code. -String uri = Otp.otpauthUri("Acme Bank", "alice@example.com", secret); - -// 3. Render `uri` as a QR code and show it on screen for the user to scan. -// See "Rendering the QR code" below. +include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/SecurityJava028Snippet.java[tag=security-java-028,indent=0] ---- [NOTE] @@ -624,56 +392,13 @@ The maximum-compatibility settings -- 6 digits, 30 second step, SHA-1 -- are the *Phase 2 -- Verification.* On every sign-in after enrolment, ask the user for the current six-digit code and verify it against the stored secret. Use [com.codename1.components.OtpField] for the input: -[source,java] ----- -import com.codename1.components.OtpField; -import com.codename1.security.Otp; -import com.codename1.ui.events.ActionEvent; -import com.codename1.ui.events.ActionListener; - -OtpField field = new OtpField(6); -field.addCompleteListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - String userInput = field.getText(); - // tolerance=1 allows the previous, current, and next 30-second - // windows to compensate for clock skew between the device and the - // server. tolerance=0 is strict but unforgiving; tolerance=2 or 3 - // is more permissive but increases the brute-force attack surface. - if (Otp.verifyTotp(secret, userInput, 1)) { - grantAccess(); - } else { - field.clear(); - Dialog.show("Invalid code", "Try again", "OK", null); - } - } -}); -form.add(field); ----- -*Server-side verification.* When your backend does the verification, the corresponding Java looks the same -- `Otp.verifyTotp(storedSecret, codeFromClient, 1)`. Make sure you rate-limit verification attempts (e.g. lock the account after 5 wrong codes within a minute) and log failures, since the 6-digit search space is small enough to be brute-forced without that. +*Server-side verification.* When your backend does the verification, the corresponding Java looks the same -- `Otp.verifyTotp(storedSecret, codeFromClient, 1)`. Make sure you rate-limit verification attempts (for example, lock the account after 5 wrong codes within a minute) and log failures, since the 6-digit search space is small enough to be brute-forced without that. ==== OTP input widget `com.codename1.components.OtpField` is a segmented input -- one box per character, that advances to the next box as the user types and steps back on backspace. This is the standard pattern for SMS confirmation and authenticator-app entry screens: -[source,java] ----- -import com.codename1.components.OtpField; -import com.codename1.ui.events.ActionEvent; -import com.codename1.ui.events.ActionListener; - -OtpField otp = new OtpField(6); // 6 digits, numeric -otp.addCompleteListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - if (Otp.verifyTotp(secret, otp.getText(), 1)) { - // accepted - } else { - otp.clear(); - } - } -}); -form.add(otp); ----- Style the boxes through the UIIDs `OtpField` (the container) and `OtpDigit` (each box). The component supports alphanumeric input via the two-argument constructor (`new OtpField(8, false)`), and pasting a full code into any one box distributes the characters across the remaining boxes automatically. diff --git a/scripts/developer-guide/compare-guide-screenshots.sh b/scripts/developer-guide/compare-guide-screenshots.sh new file mode 100755 index 00000000000..d42d704d19a --- /dev/null +++ b/scripts/developer-guide/compare-guide-screenshots.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +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}" +MANIFEST_FILE="${GUIDE_SCREENSHOT_MANIFEST:-${PROJECT_ROOT}/scripts/developer-guide/guide-screenshots.txt}" +ARTIFACT_DIR="${1:-${PROJECT_ROOT}/docs/demos/guide-screenshot-artifacts}" +GENERATED_DIR="${ARTIFACT_DIR}/generated" + +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 +mkdir -p "${GENERATED_DIR}" + +if [[ ! -d "${STORAGE_DIR}" ]]; then + echo "Storage directory ${STORAGE_DIR} does not exist; no guide screenshots were captured." >&2 + exit 1 +fi + +if [[ ! -f "${MANIFEST_FILE}" ]]; then + echo "Guide screenshot manifest not found: ${MANIFEST_FILE}" >&2 + exit 2 +fi + +EXPECTED_SCREENSHOTS=() +while IFS= read -r expected_screenshot; do + EXPECTED_SCREENSHOTS+=("${expected_screenshot}") +done < <(sed -e 's/#.*//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' "${MANIFEST_FILE}" | awk 'length($0) > 0') + +if [[ ${#EXPECTED_SCREENSHOTS[@]} -eq 0 ]]; then + echo "Guide screenshot manifest is empty: ${MANIFEST_FILE}" >&2 + exit 1 +fi + +mismatch_count=0 + +for filename in "${EXPECTED_SCREENSHOTS[@]}"; do + screenshot="${STORAGE_DIR}/${filename}" + base="${filename%.png}" + + if [[ ! -f "${screenshot}" ]]; then + echo "Expected guide screenshot was not captured: ${filename}" >&2 + mismatch_count=$((mismatch_count + 1)) + continue + fi + + cp "${screenshot}" "${GENERATED_DIR}/${filename}" + + 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 documented baseline found for ${filename}; treating as mismatch." >&2 + mismatch_count=$((mismatch_count + 1)) + cp "${screenshot}" "${ARTIFACT_DIR}/${filename}" + 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 "${diff_image}" "${metric_file}" + echo "${filename} matches baseline." + 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" + echo "${filename} differs from baseline; artifacts stored in ${ARTIFACT_DIR}." >&2 +done + +if [[ ${mismatch_count} -eq 0 ]]; then + echo "All guide screenshots match their baselines." + exit 0 +fi + +echo "${mismatch_count} guide screenshot(s) differ from documented baselines." >&2 +exit 1 diff --git a/scripts/developer-guide/compile-android-demo-sources.sh b/scripts/developer-guide/compile-android-demo-sources.sh new file mode 100755 index 00000000000..da146404cb8 --- /dev/null +++ b/scripts/developer-guide/compile-android-demo-sources.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" +ANDROID_HOME_DIR="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" + +if [ -z "$ANDROID_HOME_DIR" ]; then + if [ -d "$HOME/Library/Android/sdk" ]; then + ANDROID_HOME_DIR="$HOME/Library/Android/sdk" + elif [ -d "$HOME/Android/Sdk" ]; then + ANDROID_HOME_DIR="$HOME/Android/Sdk" + fi +fi + +if [ -z "$ANDROID_HOME_DIR" ] || [ ! -d "$ANDROID_HOME_DIR/platforms" ]; then + echo "Android SDK not found. Set ANDROID_HOME or ANDROID_SDK_ROOT." >&2 + exit 1 +fi + +ANDROID_JAR="$(find "$ANDROID_HOME_DIR/platforms" -maxdepth 2 -name android.jar | sort -V | tail -n 1)" +if [ -z "$ANDROID_JAR" ]; then + echo "No android.jar found under $ANDROID_HOME_DIR/platforms." >&2 + exit 1 +fi + +CORE_JAR="$HOME/.m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT.jar" +if [ ! -f "$CORE_JAR" ] && [ -f "$ROOT_DIR/maven/core/target/codenameone-core-8.0-SNAPSHOT.jar" ]; then + CORE_JAR="$ROOT_DIR/maven/core/target/codenameone-core-8.0-SNAPSHOT.jar" +fi + +ANDROID_CN1_JAR="$ROOT_DIR/maven/android/target/codenameone-android-8.0-SNAPSHOT.jar" +if [ ! -f "$ANDROID_CN1_JAR" ]; then + ANDROID_CN1_JAR="$HOME/.m2/repository/com/codenameone/codenameone-android/8.0-SNAPSHOT/codenameone-android-8.0-SNAPSHOT.jar" +fi +ANDROID_CN1_CLASSES="$ROOT_DIR/maven/android/target/classes" +COMMON_CLASSES="$ROOT_DIR/docs/demos/common/target/classes" + +for required in "$CORE_JAR" "$ANDROID_CN1_JAR"; do + if [ ! -f "$required" ]; then + echo "Required Codename One jar not found: $required" >&2 + echo "Run the local Codename One Maven artifact install before compiling Android demo sources." >&2 + exit 1 + fi +done + +if [ ! -d "$COMMON_CLASSES" ]; then + echo "Common demo classes not found: $COMMON_CLASSES" >&2 + echo "Run docs/demos JavaSE test/compile before compiling Android demo sources." >&2 + exit 1 +fi + +WORK_DIR="${TMPDIR:-/tmp}/cn1-docs-android-compile" +CLASSES_DIR="$WORK_DIR/classes" +SOURCES_FILE="$WORK_DIR/android-sources.txt" +STUB_SOURCES_DIR="$WORK_DIR/stubs" + +rm -rf "$WORK_DIR" +mkdir -p "$CLASSES_DIR" +find "$ROOT_DIR/docs/demos/android/src/main/java" -name '*.java' -print | sort > "$SOURCES_FILE" + +COMPILE_CLASSPATH="$ANDROID_JAR:$COMMON_CLASSES:$CORE_JAR:$ANDROID_CN1_JAR" +if [ -d "$ANDROID_CN1_CLASSES" ]; then + COMPILE_CLASSPATH="$COMPILE_CLASSPATH:$ANDROID_CN1_CLASSES" +fi +ANDROID_BINARIES_DIR="$ROOT_DIR/maven/target/cn1-binaries/android" +if [ -d "$ANDROID_BINARIES_DIR" ]; then + while IFS= read -r jar; do + COMPILE_CLASSPATH="$COMPILE_CLASSPATH:$jar" + done < <(find "$ANDROID_BINARIES_DIR" -maxdepth 1 -name '*.jar' -print | sort) +fi + +if [ "${CN1_FORCE_ANDROID_COMPILE_STUBS:-0}" = "1" ] \ + || ! javap -classpath "$COMPILE_CLASSPATH" com.codename1.impl.android.AndroidNativeUtil >/dev/null 2>&1 \ + || ! javap -classpath "$COMPILE_CLASSPATH" com.codename1.impl.android.AndroidImplementation >/dev/null 2>&1 \ + || ! javap -classpath "$COMPILE_CLASSPATH" com.codename1.impl.android.PermissionPromptCallback >/dev/null 2>&1; then + mkdir -p "$STUB_SOURCES_DIR/com/codename1/impl/android" + cat > "$STUB_SOURCES_DIR/com/codename1/impl/android/AndroidNativeUtil.java" <<'STUB' +package com.codename1.impl.android; + +import android.app.Activity; + +public final class AndroidNativeUtil { + private AndroidNativeUtil() { + } + + public static boolean checkForPermission(String permission, String rationale) { + return true; + } + + public static Activity getActivity() { + return null; + } + + public static void setPermissionPromptCallback(PermissionPromptCallback callback) { + } +} +STUB + cat > "$STUB_SOURCES_DIR/com/codename1/impl/android/AndroidImplementation.java" <<'STUB' +package com.codename1.impl.android; + +public final class AndroidImplementation { + private AndroidImplementation() { + } + + public static void runOnUiThreadAndBlock(Runnable runnable) { + if (runnable != null) { + runnable.run(); + } + } +} +STUB + cat > "$STUB_SOURCES_DIR/com/codename1/impl/android/PermissionPromptCallback.java" <<'STUB' +package com.codename1.impl.android; + +public interface PermissionPromptCallback { + boolean showPermissionPrompt(String permission, String title, String body, String positiveButtonText, String negativeButtonText); + + void showPermissionMessage(String permission, String title, String body, String okButtonText); +} +STUB + find "$STUB_SOURCES_DIR" -name '*.java' -print | sort >> "$SOURCES_FILE" +fi + +javac \ + --release 17 \ + -cp "$COMPILE_CLASSPATH" \ + -d "$CLASSES_DIR" \ + @"$SOURCES_FILE" diff --git a/scripts/developer-guide/guide-screenshots.txt b/scripts/developer-guide/guide-screenshots.txt new file mode 100644 index 00000000000..b086e849015 --- /dev/null +++ b/scripts/developer-guide/guide-screenshots.txt @@ -0,0 +1,62 @@ +layout-animation-1.png +layout-animation-2.png +layout-animation-3.png +layout-animation-4.png +layout-animation-5.png +layout-animation-6.png +layout-animation-7.png +transition-slide.png +transition-slide-vertical.png +transition-slide-fade.png +transition-cover.png +transition-uncover.png +transition-fade.png +transition-flip.png +transition-bubble.png +mighty-morphing-components-1.png +flow-layout.png +flow-layout-center.png +flow-layout-right.png +flow-layout-center-middle.png +box-layout-y.png +box-layout-x.png +box-layout-x-no-grow.png +border-layout.png +border-layout-center.png +border-layout-RTL.png +grid-layout-2x2.png +grid-layout-2x4.png +grid-layout-autofit-portrait.png +grid-layout-autofit-landscape.png +components-button.png +components-link-button.png +raised-flat-buttons.png +components-radiobutton-checkbox.png +components-componentgroup.png +components-multibutton.png +components-spanbutton.png +components-spanlabel.png +components-onoffswitch.png +components-tabs.png +components-tabs-swipe1.png +components-tabs-swipe2.png +components-picker.png +components-floatinghint.png +components-accordion.png +floating-action.png +badge-floating-button.png +splitpane.png +components-slider.png +graphics-hiworld.png +graphics-glasspane.png +shaped-clipping.png +graphics-fontimage-fixed.png +graphics-fontimage-style.png +graphics-fontimage-material.png +components-dialog-modal-south.png +components-dialog-modal-bottom-half.png +components-dialog-tint.png +components-dialog-green-tint.png +components-dialog-blur.png +components-dialog-blur-no-tint.png +components-interaction-dialog.png diff --git a/scripts/developer-guide/migrate-inline-guide-snippets.py b/scripts/developer-guide/migrate-inline-guide-snippets.py new file mode 100644 index 00000000000..af22df51fbb --- /dev/null +++ b/scripts/developer-guide/migrate-inline-guide-snippets.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +import argparse +import re +import shutil +from collections import defaultdict +from pathlib import Path + + +SOURCE_RE = re.compile(r"^\[source,([A-Za-z0-9_#+. -]+)(?:,.*)?\]\s*$") +INCLUDE_RE = re.compile(r"^include::([^\[]+)\[(.*)\]\s*$") +LISTING_DELIMITER_RE = re.compile(r"^-{4,}\s*$") + +EXTENSIONS = { + "bash": "sh", + "sh": "sh", + "shell": "sh", + "java": "java", + "kotlin": "kt", + "xml": "xml", + "css": "css", + "javascript": "js", + "json": "json", + "properties": "properties", + "html": "html", + "objective-c": "m", + "c": "c", + "csharp": "cs", + "sql": "sql", + "text": "txt", + "listing": "txt", + "strings": "strings", + "bytecode": "txt", + "rpf": "txt", +} + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Move inline developer-guide source blocks into docs/demos tagged snippet fixtures." + ) + parser.add_argument("--docs-dir", default="docs/developer-guide") + parser.add_argument( + "--snippets-dir", + default="docs/demos/common/src/main/snippets/developer-guide", + ) + parser.add_argument( + "--clean", + action="store_true", + help="Remove generated snippet fixture files before migrating.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Report the planned migration without writing files.", + ) + return parser.parse_args() + + +def guide_files(docs_dir): + return sorted( + p for p in Path(docs_dir).iterdir() + if p.is_file() and p.suffix in (".adoc", ".asciidoc") + ) + + +def normalize_language(language): + return " ".join(language.strip().split()).lower() + + +def snippet_extension(language): + normalized = normalize_language(language) + return EXTENSIONS.get(normalized, re.sub(r"[^a-z0-9]+", "-", normalized).strip("-") or "txt") + + +def tag_prefix(source): + return re.sub(r"[^A-Za-z0-9]+", "-", source.stem).strip("-").lower() + + +def java_snippet_file(source): + words = { + "3d": "ThreeD", + "ai": "Ai", + "ios": "Ios", + "nfc": "Nfc", + "junit": "Junit", + "svg": "Svg", + "css": "Css", + "io": "Io", + "tvplatforms": "TvPlatforms", + } + parts = re.split(r"[^A-Za-z0-9]+", source.stem) + name = "".join(words.get(part.lower(), part[:1].upper() + part[1:]) for part in parts if part) + return f"{name}Snippets.java" + + +def is_include_body(lines): + nonempty = [line.strip() for line in lines if line.strip()] + return len(nonempty) == 1 and INCLUDE_RE.match(nonempty[0]) is not None + + +def migrate_file(source, snippets_dir, dry_run): + original_lines = source.read_text(encoding="utf-8", errors="replace").splitlines() + output_lines = [] + generated = defaultdict(list) + migrated = 0 + counters = defaultdict(int) + prefix = tag_prefix(source) + + i = 0 + while i < len(original_lines): + source_match = SOURCE_RE.match(original_lines[i]) + if not source_match: + output_lines.append(original_lines[i]) + i += 1 + continue + + language = source_match.group(1) + j = i + 1 + blank_lines = [] + while j < len(original_lines) and not original_lines[j].strip(): + blank_lines.append(original_lines[j]) + j += 1 + + if j >= len(original_lines): + output_lines.append(original_lines[i]) + output_lines.extend(blank_lines) + i = j + continue + + delimiter = original_lines[j].strip() + if not LISTING_DELIMITER_RE.match(delimiter): + output_lines.append(original_lines[i]) + output_lines.extend(blank_lines) + output_lines.append(original_lines[j]) + i = j + 1 + continue + + k = j + 1 + while k < len(original_lines) and original_lines[k].strip() != delimiter: + k += 1 + if k >= len(original_lines): + output_lines.extend(original_lines[i:]) + break + + code_lines = original_lines[j + 1:k] + if is_include_body(code_lines): + output_lines.extend(original_lines[i:k + 1]) + i = k + 1 + continue + + normalized_language = normalize_language(language) + counters[normalized_language] += 1 + tag = f"{prefix}-{normalized_language.replace(' ', '-')}-{counters[normalized_language]:03d}" + extension = snippet_extension(language) + snippet_file = java_snippet_file(source) if extension == "java" else f"{prefix}.{extension}" + include_path = f"../demos/common/src/main/snippets/developer-guide/{snippet_file}" + generated[snippet_file].append((tag, code_lines)) + + output_lines.append(original_lines[i]) + output_lines.extend(blank_lines) + output_lines.append(original_lines[j]) + output_lines.append(f"include::{include_path}[tag={tag},indent=0]") + output_lines.append(original_lines[k]) + migrated += 1 + i = k + 1 + + if not dry_run and migrated: + source.write_text("\n".join(output_lines) + "\n", encoding="utf-8") + + if not dry_run: + snippets_dir.mkdir(parents=True, exist_ok=True) + for snippet_file, entries in generated.items(): + target = snippets_dir / snippet_file + chunks = [ + "// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline.", + "", + ] + for tag, code_lines in entries: + chunks.append(f"// tag::{tag}[]") + chunks.extend(code_lines) + chunks.append(f"// end::{tag}[]") + chunks.append("") + target.write_text("\n".join(chunks), encoding="utf-8") + + return migrated + + +def main(): + args = parse_args() + docs_dir = Path(args.docs_dir) + snippets_dir = Path(args.snippets_dir) + + if args.clean and snippets_dir.exists() and not args.dry_run: + shutil.rmtree(snippets_dir) + + total = 0 + by_file = {} + for source in guide_files(docs_dir): + count = migrate_file(source, snippets_dir, args.dry_run) + if count: + by_file[str(source)] = count + total += count + + for source, count in sorted(by_file.items()): + print(f"{source}: migrated {count}") + print(f"Total migrated inline source blocks: {total}") + + +if __name__ == "__main__": + main() diff --git a/scripts/developer-guide/validate-guide-snippets.py b/scripts/developer-guide/validate-guide-snippets.py new file mode 100755 index 00000000000..a7a3061d123 --- /dev/null +++ b/scripts/developer-guide/validate-guide-snippets.py @@ -0,0 +1,552 @@ +#!/usr/bin/env python3 +import argparse +import os +import re +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + + +SOURCE_RE = re.compile(r"^\[source,([A-Za-z0-9_#+. -]+)(?:,.*)?\]\s*$") +INCLUDE_RE = re.compile(r"^include::([^\[]+)\[(.*)\]\s*$") +TAG_RE = re.compile(r"(?:^|,)tag=([^,\]]+)") +LISTING_DELIMITER_RE = re.compile(r"^-{4,}\s*$") +TAG_BLOCK_RE = re.compile(r"// tag::([^\[]+)\[\]\n(.*?)\n// end::\1\[\]", re.S) +JAVA_IDENTIFIER_RE = re.compile(r"^[A-Za-z_$][A-Za-z0-9_$]*\.java$") + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Validate developer-guide source blocks against demo-backed includes." + ) + parser.add_argument( + "--docs-dir", + default="docs/developer-guide", + help="Directory containing guide .adoc/.asciidoc files.", + ) + return parser.parse_args() + + +def guide_files(docs_dir): + return sorted( + p for p in Path(docs_dir).iterdir() + if p.is_file() and p.suffix in (".adoc", ".asciidoc") + ) + + +def extract_source_blocks(docs_dir): + docs_dir = Path(docs_dir) + inline = [] + includes = [] + errors = [] + + for source in guide_files(docs_dir): + rel_source = str(source) + lines = source.read_text(encoding="utf-8", errors="replace").splitlines() + i = 0 + while i < len(lines): + source_match = SOURCE_RE.match(lines[i]) + if not source_match: + i += 1 + continue + + language = source_match.group(1) + start_line = i + 1 + j = i + 1 + while j < len(lines) and not lines[j].strip(): + j += 1 + + if j >= len(lines): + errors.append(f"{rel_source}:{start_line}: source block has no body") + i += 1 + continue + + include_match = INCLUDE_RE.match(lines[j].strip()) + if include_match: + target = include_match.group(1) + attrs = include_match.group(2) + includes.append( + { + "sourceFile": rel_source, + "line": j + 1, + "language": language, + "target": target, + "attrs": attrs, + } + ) + i = j + 1 + continue + + delimiter = lines[j].strip() + if not LISTING_DELIMITER_RE.match(delimiter): + errors.append( + f"{rel_source}:{start_line}: source block is neither include-backed nor fenced with ----" + ) + i = j + 1 + continue + + body_start = j + 1 + k = body_start + while k < len(lines) and lines[k].strip() != delimiter: + k += 1 + if k >= len(lines): + errors.append(f"{rel_source}:{start_line}: source block is missing closing ----") + i = j + 1 + continue + + code_lines = lines[body_start:k] + nonempty_code_lines = [line.strip() for line in code_lines if line.strip()] + if len(nonempty_code_lines) == 1: + include_match = INCLUDE_RE.match(nonempty_code_lines[0]) + if include_match: + target = include_match.group(1) + attrs = include_match.group(2) + includes.append( + { + "sourceFile": rel_source, + "line": body_start + 1, + "language": language, + "target": target, + "attrs": attrs, + } + ) + i = k + 1 + continue + + code = "\n".join(code_lines) + inline.append( + { + "sourceFile": rel_source, + "line": start_line, + "language": language, + "preview": " ".join(code.strip().split())[:120], + } + ) + i = k + 1 + + return inline, includes, errors + + +def validate_includes(includes): + errors = [] + cache = {} + java_snippets = [] + for item in includes: + if not item["target"].startswith("../demos/"): + errors.append( + f"{item['sourceFile']}:{item['line']}: source include must target ../demos/: {item['target']}" + ) + continue + + source_path = Path(item["sourceFile"]) + target_path = (source_path.parent / item["target"]).resolve() + if not target_path.exists(): + errors.append( + f"{item['sourceFile']}:{item['line']}: include target not found: {item['target']}" + ) + continue + + if item["language"].strip().lower() == "java": + if item["target"].endswith(".java.txt"): + errors.append( + f"{item['sourceFile']}:{item['line']}: Java snippets must not be hidden in .java.txt files: " + f"{item['target']}" + ) + if target_path.suffix == ".java" and not JAVA_IDENTIFIER_RE.match(target_path.name): + errors.append( + f"{item['sourceFile']}:{item['line']}: Java snippet fixture must use a valid Java filename: " + f"{item['target']}" + ) + if item["language"].strip().lower() in ("java", "kotlin") and ( + "/demos/common/src/main/snippets/" in item["target"] + or item["target"].startswith("../demos/common/src/main/snippets/") + ): + errors.append( + f"{item['sourceFile']}:{item['line']}: Java/Kotlin guide snippets in the common demo " + f"must live under a compiled source root, not src/main/snippets: {item['target']}" + ) + if item["language"].strip().lower() == "css" and ( + "/demos/common/src/main/snippets/" in item["target"] + or item["target"].startswith("../demos/common/src/main/snippets/") + ): + errors.append( + f"{item['sourceFile']}:{item['line']}: CSS guide snippets in the common demo " + f"must live under src/main/css so the CSS compiler sees them: {item['target']}" + ) + if item["language"].strip().lower() in ("javascript", "html") and ( + "/demos/common/" in item["target"] or item["target"].startswith("../demos/common/") + ): + errors.append( + f"{item['sourceFile']}:{item['line']}: JavaScript/HTML snippets must live under " + f"the JavaScript demo module, not common: {item['target']}" + ) + + tag_match = TAG_RE.search(item["attrs"]) + if not tag_match: + continue + + tag = tag_match.group(1) + text = cache.get(target_path) + if text is None: + text = target_path.read_text(encoding="utf-8", errors="replace") + cache[target_path] = text + if f"tag::{tag}[]" not in text: + errors.append( + f"{item['sourceFile']}:{item['line']}: include tag not found in {item['target']}: {tag}" + ) + if f"end::{tag}[]" not in text: + errors.append( + f"{item['sourceFile']}:{item['line']}: include end tag not found in {item['target']}: {tag}" + ) + if item["language"].strip().lower() == "java" and "/src/main/java/" not in str(target_path): + java_snippets.append( + { + "sourceFile": item["sourceFile"], + "line": item["line"], + "target": item["target"], + "targetPath": target_path, + "tag": tag, + "text": text, + } + ) + if os.environ.get("CN1_VALIDATE_JAVA_SNIPPET_COMPILE") == "1": + errors.extend(validate_java_snippets(java_snippets)) + return errors + + +def extract_tag_body(text, tag): + match = re.search( + r"// tag::" + re.escape(tag) + r"\[\]\n(.*?)\n// end::" + re.escape(tag) + r"\[\]", + text, + re.S, + ) + if not match: + return None + return match.group(1) + + +def split_java_snippet(body): + imports = [] + code = [] + for line in body.splitlines(): + stripped = line.strip() + if stripped.startswith("package "): + continue + if stripped.startswith("import "): + imports.append(stripped) + continue + code.append(line) + return imports, "\n".join(code).strip() + + +def normalize_java_snippet_for_compile(code): + normalized = [] + for line in code.splitlines(): + line = re.sub(r"\s*<\d+>\s*$", "", line) + stripped = line.strip() + if stripped in ("...", "...."): + normalized.append(line[: len(line) - len(line.lstrip())] + "/* omitted */") + else: + normalized.append(line.replace("....", "/* omitted */").replace("...", "null")) + return "\n".join(normalized) + + +def looks_like_member_snippet(code): + in_block_comment = False + first = "" + for line in code.splitlines(): + stripped_line = line.strip() + if in_block_comment: + if "*/" in stripped_line: + in_block_comment = False + continue + if not stripped_line or stripped_line.startswith("//"): + continue + if stripped_line.startswith("/*"): + if "*/" not in stripped_line: + in_block_comment = True + continue + if stripped_line.startswith("*"): + continue + first = stripped_line + break + return ( + first.startswith("@") + or first.startswith("public ") + or first.startswith("private ") + or first.startswith("protected ") + or first.startswith("abstract ") + or first.startswith("interface ") + or first.startswith("enum ") + ) + + +def java_compile_classpath(): + candidates = [ + Path("docs/demos/common/target/classes"), + Path("maven/core/target/classes"), + Path.home() / ".m2/repository/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT.jar", + Path.home() / ".m2/repository/com/codenameone/java-runtime/8.0-SNAPSHOT/java-runtime-8.0-SNAPSHOT.jar", + ] + return os.pathsep.join(str(p) for p in candidates if p.exists()) + + +def java_harness_source(class_name, imports, code, as_member): + import_lines = "\n".join(imports) + fixture_imports = """ +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; +""" + fields = """ + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; +""" + helper_members, statements = split_top_level_java_members(code) + if helper_members and statements: + indented = "\n".join(" " + line for line in statements.splitlines()) + return ( + f"{fixture_imports}\n{import_lines}\nclass {class_name} {{\n{fields}\n" + f"{helper_members}\n" + f" void snippet() throws Exception {{\n{indented}\n }}\n}}\n" + ) + if helper_members and not statements: + return f"{fixture_imports}\n{import_lines}\nclass {class_name} {{\n{fields}\n{helper_members}\n}}\n" + constructor_match = re.match(r"\s*public\s+([A-Z][A-Za-z0-9_$]*)\s*\(", code) + if as_member and constructor_match: + nested_class = constructor_match.group(1) + return f"{fixture_imports}\n{import_lines}\nclass {class_name} {{\n{fields}\nclass {nested_class} {{\n{code}\n}}\n}}\n" + if as_member: + return f"{fixture_imports}\n{import_lines}\nclass {class_name} {{\n{fields}\n{code}\n}}\n" + indented = "\n".join(" " + line for line in code.splitlines()) + return f"{fixture_imports}\n{import_lines}\nclass {class_name} {{\n{fields}\n void snippet() throws Exception {{\n{indented}\n }}\n}}\n" + + +def split_top_level_java_members(code): + lines = code.splitlines() + has_method_member = any(is_top_level_method_start(line) for line in lines) + members = [] + statements = [] + i = 0 + found_member = False + while i < len(lines): + line = lines[i] + stripped = line.strip() + if not stripped: + (members if found_member else statements).append(line) + i += 1 + continue + if is_top_level_field(stripped, has_method_member): + members.append(line) + found_member = True + i += 1 + continue + if is_top_level_method_start(line): + block = [line] + depth = line.count("{") - line.count("}") + i += 1 + while i < len(lines) and depth > 0: + block.append(lines[i]) + depth += lines[i].count("{") - lines[i].count("}") + i += 1 + members.extend(block) + found_member = True + continue + statements.append(line) + i += 1 + return "\n".join(members).strip(), "\n".join(statements).strip() + + +def is_top_level_field(stripped, has_method_member): + if ( + stripped.startswith("public static final ") + or stripped.startswith("private static final ") + or stripped.startswith("static final ") + ) and stripped.endswith(";"): + return True + if not has_method_member: + return False + return re.match( + r"^(?:[A-Za-z_$][A-Za-z0-9_$.<>?, \[\]]+)\s+[A-Za-z_$][A-Za-z0-9_$]*\s*=.+;$", + stripped, + ) is not None + + +def is_top_level_method_start(line): + if line.startswith((" ", "\t")): + return False + stripped = line.strip() + if "{" not in stripped: + return False + return re.match( + r"^(public|private|protected)?\s*([A-Za-z_$][A-Za-z0-9_$<>, ?\[\]]+\s+)+[A-Za-z_$][A-Za-z0-9_$]*\s*\([^;]*\)\s*(throws\s+[A-Za-z0-9_., ]+\s*)?\{", + stripped, + ) is not None + + +def write_junit_stubs(src_dir): + junit_dir = src_dir / "org/junit/jupiter/api" + junit_dir.mkdir(parents=True, exist_ok=True) + (junit_dir / "Test.java").write_text( + "package org.junit.jupiter.api;\npublic @interface Test {}\n", + encoding="utf-8", + ) + (junit_dir / "Assertions.java").write_text( + "package org.junit.jupiter.api;\n" + "public final class Assertions {\n" + " public static void assertEquals(Object expected, Object actual) {}\n" + " public static void assertTrue(boolean value) {}\n" + " public static void assertFalse(boolean value) {}\n" + "}\n", + encoding="utf-8", + ) + + +def validate_java_snippets(java_snippets): + if not java_snippets: + return [] + javac = shutil.which("javac") + if javac is None: + return ["javac not found; cannot compile developer-guide Java snippets."] + + errors = [] + with tempfile.TemporaryDirectory(prefix="cn1-guide-java-snippets-") as tmp: + tmp_dir = Path(tmp) + src_dir = tmp_dir / "src" + classes_dir = tmp_dir / "classes" + src_dir.mkdir() + classes_dir.mkdir() + write_junit_stubs(src_dir) + snippet_locations = {} + + for index, item in enumerate(java_snippets, start=1): + body = extract_tag_body(item["text"], item["tag"]) + if body is None: + continue + imports, code = split_java_snippet(body) + code = normalize_java_snippet_for_compile(code) + if not code: + continue + + class_name = f"GuideSnippet{index}" + source = src_dir / f"{class_name}.java" + source.write_text( + java_harness_source(class_name, imports, code, looks_like_member_snippet(code)), + encoding="utf-8", + ) + snippet_locations[source.name] = item + + sources = sorted(str(p) for p in src_dir.rglob("*.java")) + cmd = [ + javac, + "--release", + "17", + "-proc:none", + "-Xmaxerrs", + "200", + "-cp", + java_compile_classpath(), + "-d", + str(classes_dir), + *sources, + ] + result = subprocess.run(cmd, text=True, capture_output=True) + if result.returncode != 0: + reported = set() + for line in result.stderr.splitlines(): + match = re.search(r"([^/\s]+\.java):\d+:", line) + if not match: + continue + item = snippet_locations.get(match.group(1)) + if item is None: + continue + key = (item["sourceFile"], item["line"], item["target"], item["tag"]) + if key in reported: + continue + reported.add(key) + errors.append( + f"{item['sourceFile']}:{item['line']}: Java snippet does not compile: " + f"{item['target']} tag={item['tag']}\n{line}" + ) + if len(reported) >= 50: + break + if not errors: + first_error = "\n".join(result.stderr.splitlines()[:20]) + errors.append(f"Developer guide Java snippet compilation failed.\n{first_error}") + return errors + + +def main(): + args = parse_args() + inline, includes, errors = extract_source_blocks(args.docs_dir) + errors.extend(validate_includes(includes)) + + if inline: + errors.append( + f"{len(inline)} inline source block(s) found. " + "Move all guide snippets into docs/demos and include them by tag." + ) + for record in inline[:50]: + errors.append( + f"{record['sourceFile']}:{record['line']}: [{record['language']}] {record['preview']}" + ) + + print(f"Validated {len(includes)} include-backed source block(s).") + + if errors: + for error in errors: + print(error, file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/developer-guide/verify-guide-screenshot-manifest.sh b/scripts/developer-guide/verify-guide-screenshot-manifest.sh new file mode 100755 index 00000000000..cdf9882c665 --- /dev/null +++ b/scripts/developer-guide/verify-guide-screenshot-manifest.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +MANIFEST_FILE="${GUIDE_SCREENSHOT_MANIFEST:-${PROJECT_ROOT}/scripts/developer-guide/guide-screenshots.txt}" +GUIDE_DIR="${PROJECT_ROOT}/docs/developer-guide" + +if [[ ! -f "${MANIFEST_FILE}" ]]; then + echo "Guide screenshot manifest not found: ${MANIFEST_FILE}" >&2 + exit 1 +fi + +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT + +manifest_sorted="${tmp_dir}/manifest.txt" +referenced_sorted="${tmp_dir}/referenced.txt" +generated_refs_sorted="${tmp_dir}/generated-refs.txt" + +grep -v '^[[:space:]]*$' "${MANIFEST_FILE}" | sort -u > "${manifest_sorted}" + +find "${GUIDE_DIR}" -type f \( -name '*.adoc' -o -name '*.asciidoc' \) -print0 \ + | xargs -0 grep -Eoh 'img/[A-Za-z0-9._/-]+\.(png|jpg|jpeg)' \ + | sed -E 's#.*img/##' \ + | sort -u > "${referenced_sorted}" + +grep -E '^(layout-animation-[0-9]+|transition-[A-Za-z0-9-]+|mighty-morphing-components-1)\.png$' \ + "${referenced_sorted}" > "${generated_refs_sorted}" || true + +missing_from_manifest="$(comm -23 "${generated_refs_sorted}" "${manifest_sorted}")" +if [[ -n "${missing_from_manifest}" ]]; then + echo "Generated guide screenshot reference(s) missing from manifest:" >&2 + echo "${missing_from_manifest}" >&2 + exit 1 +fi + +missing_from_guide="$(comm -23 "${manifest_sorted}" "${referenced_sorted}")" +if [[ -n "${missing_from_guide}" ]]; then + echo "Guide screenshot manifest entry/entries not referenced by developer guide:" >&2 + echo "${missing_from_guide}" >&2 + exit 1 +fi + +echo "Guide screenshot manifest covers $(wc -l < "${manifest_sorted}" | tr -d '[:space:]') generated screenshot reference(s)." diff --git a/scripts/developer-guide/verify-guide-screenshots-captured.sh b/scripts/developer-guide/verify-guide-screenshots-captured.sh new file mode 100755 index 00000000000..02c15b548d2 --- /dev/null +++ b/scripts/developer-guide/verify-guide-screenshots-captured.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +STORAGE_DIR="${CN1_STORAGE_DIR:-${HOME}/.cn1}" +MANIFEST_FILE="${GUIDE_SCREENSHOT_MANIFEST:-${PROJECT_ROOT}/scripts/developer-guide/guide-screenshots.txt}" + +if [[ ! -f "${MANIFEST_FILE}" ]]; then + echo "Guide screenshot manifest not found: ${MANIFEST_FILE}" >&2 + exit 2 +fi + +if [[ ! -d "${STORAGE_DIR}" ]]; then + echo "Storage directory ${STORAGE_DIR} does not exist; CN1 screenshot tests did not capture images." >&2 + exit 1 +fi + +missing_count=0 +captured_count=0 + +while IFS= read -r expected_screenshot; do + screenshot="${STORAGE_DIR}/${expected_screenshot}" + if [[ -f "${screenshot}" ]]; then + size="$(wc -c < "${screenshot}" | tr -d '[:space:]')" + if [[ "${size}" -eq 0 ]]; then + echo "Captured guide screenshot is empty: ${screenshot}" >&2 + missing_count=$((missing_count + 1)) + continue + fi + echo "Captured guide screenshot: ${screenshot} (${size} bytes)" + captured_count=$((captured_count + 1)) + else + echo "Expected guide screenshot was not captured: ${expected_screenshot}" >&2 + missing_count=$((missing_count + 1)) + fi +done < <(sed -e 's/#.*//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' "${MANIFEST_FILE}" | awk 'length($0) > 0') + +if [[ ${captured_count} -eq 0 ]]; then + echo "No guide screenshots were captured in ${STORAGE_DIR}." >&2 + exit 1 +fi + +if [[ ${missing_count} -ne 0 ]]; then + echo "${missing_count} expected guide screenshot(s) were not captured." >&2 + exit 1 +fi + +echo "Verified ${captured_count} guide screenshot(s) in ${STORAGE_DIR}." diff --git a/scripts/website/update_developer_guide_redirect.sh b/scripts/website/update_developer_guide_redirect.sh index 8ab1dae8e84..8bf82c897e4 100755 --- a/scripts/website/update_developer_guide_redirect.sh +++ b/scripts/website/update_developer_guide_redirect.sh @@ -58,15 +58,19 @@ for asset in assets: if asset.get("name") == "developer-guide.pdf": asset_url = asset.get("browser_download_url", "") break -if not asset_url: - raise SystemExit("developer-guide.pdf asset not found in latest release.") print(f"{tag} {asset_url}") ' "${release_json}" )" if [ -z "${asset_url}" ]; then - echo "Resolved developer-guide.pdf URL is empty." >&2 - exit 1 + if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ]; then + asset_url="https://github.com/codenameone/CodenameOne/releases/download/${release_tag}/developer-guide.pdf" + echo "developer-guide.pdf asset not found in latest release; using PR fallback ${asset_url}" >&2 + else + echo "developer-guide.pdf asset not found in latest release." >&2 + echo "Resolved developer-guide.pdf URL is empty." >&2 + exit 1 + fi fi awk -v begin="${BEGIN_MARKER}" -v end="${END_MARKER}" '