diff --git a/.github/workflows/developer-guide-docs.yml b/.github/workflows/developer-guide-docs.yml index 79f8164049..008bd9dd11 100644 --- a/.github/workflows/developer-guide-docs.yml +++ b/.github/workflows/developer-guide-docs.yml @@ -299,7 +299,7 @@ jobs: set -euo pipefail pip install --user language-tool-python==2.9.4 - - name: Run LanguageTool grammar check (advisory) + - name: Run LanguageTool grammar check run: | set -euo pipefail REPORT_DIR="build/developer-guide/reports" @@ -314,7 +314,8 @@ jobs: LT_COUNT="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d.get("total",0))' "$REPORT_FILE" 2>/dev/null || echo 0)" echo "LANGUAGETOOL_REPORT=$REPORT_FILE" >> "$GITHUB_ENV" echo "LANGUAGETOOL_COUNT=$LT_COUNT" >> "$GITHUB_ENV" - echo "LanguageTool flagged ${LT_COUNT} match(es). This is advisory and does not fail the build." >&2 + echo "LANGUAGETOOL_STATUS=$STATUS" >> "$GITHUB_ENV" + echo "LanguageTool flagged ${LT_COUNT} match(es). The final quality-gate step will fail the build if any matches remain." >&2 - name: Check for unused developer guide images run: | @@ -463,11 +464,16 @@ jobs: echo "Rewrite the flagged paragraph so its first prose word starts with a capital letter." >&2 FAIL=1 fi + if [ "${LANGUAGETOOL_STATUS:-0}" != "0" ] || [ "${LANGUAGETOOL_COUNT:-0}" != "0" ]; then + echo "LanguageTool reported ${LANGUAGETOOL_COUNT:-?} match(es) (exit status ${LANGUAGETOOL_STATUS:-?}). See the developer-guide-languagetool artifact." >&2 + echo "Either fix the prose, add the term to docs/developer-guide/languagetool-accept.txt, or document a new exception by disabling the rule in scripts/developer-guide/run_languagetool.py." >&2 + FAIL=1 + fi if [ "$FAIL" -ne 0 ]; then - echo "Developer guide quality gates failed. Warnings from vale and asciidoctor are treated as build-breaking errors." >&2 + echo "Developer guide quality gates failed. Warnings from vale, asciidoctor, and LanguageTool are treated as build-breaking errors." >&2 exit 1 fi - echo "Developer guide quality gates passed (vale issues=${VALE_ISSUE_COUNT:-0}, asciidoctor status=${ASCII_DOC_LINT_STATUS:-0}, unused-image status=${UNUSED_IMAGES_STATUS:-0})." + echo "Developer guide quality gates passed (vale issues=${VALE_ISSUE_COUNT:-0}, asciidoctor status=${ASCII_DOC_LINT_STATUS:-0}, unused-image status=${UNUSED_IMAGES_STATUS:-0}, languagetool count=${LANGUAGETOOL_COUNT:-0})." - name: Comment with artifact download links if: ${{ always() && github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} @@ -538,7 +544,7 @@ jobs: links.push(`- [Paragraph capitalization report](${artifactLinks.get('developer-guide-paragraph-capitalization')})`); } if (artifactLinks.has('developer-guide-languagetool')) { - links.push(`- [LanguageTool report (advisory)](${artifactLinks.get('developer-guide-languagetool')})`); + links.push(`- [LanguageTool report](${artifactLinks.get('developer-guide-languagetool')})`); } if (!links.length) { @@ -568,7 +574,7 @@ jobs: qualityLines.push(`- Paragraph capitalization: ${paragraphCapSummary}${paragraphCapLink ? ` ([report](${paragraphCapLink}))` : ''}`); } if (languagetoolSummary) { - qualityLines.push(`- LanguageTool (advisory): ${languagetoolSummary}${languagetoolLink ? ` ([report](${languagetoolLink}))` : ''}`); + qualityLines.push(`- LanguageTool: ${languagetoolSummary}${languagetoolLink ? ` ([report](${languagetoolLink}))` : ''}`); } if (unusedSummary) { qualityLines.push(`- Image references: ${unusedSummary}${unusedLink ? ` ([report](${unusedLink}))` : ''}`); diff --git a/docs/developer-guide/Advanced-Theming.asciidoc b/docs/developer-guide/Advanced-Theming.asciidoc index 6a2815b6bc..3baa7048f4 100644 --- a/docs/developer-guide/Advanced-Theming.asciidoc +++ b/docs/developer-guide/Advanced-Theming.asciidoc @@ -115,7 +115,7 @@ Internally, Codename One has many built-in constants, and the list keeps growing |`true` to activate the material design ripple effect on the buttons. This effect draws a growing circle from the point of touch onwards. This is `false` by default except for Android where it defaults to `true`. This is equivalent to `setButtonRippleEffectDefault(bool)` in the `Button` class |capsButtonTextBool -|`true` to activate the caps text mode in the buttons. When activated `setText` on `Button` and all the constructors will invoke `upcase()` on all the strings effectively making the application buttons use uppercase exclusively. This is `false` by default except for Android where it defaults to `true`. it's equivalent to `Button.setCapsTextDefault(boolean)` and can be tuned to an individual `Component` via `Component.setRippleEffect(boolean)` +|`true` to activate the caps text mode in the buttons. When activated `setText` on `Button` and all the constructors will invoke `upcase()` on all the strings effectively making the application buttons use uppercase exclusively. This is `false` by default except for Android where it defaults to `true`. It's equivalent to `Button.setCapsTextDefault(boolean)` and can be tuned to an individual `Component` via `Component.setRippleEffect(boolean)` |capsButtonUiids |A list of the UIID's that should be capitalized by default (in supported platforms) other than the `Button` and `RaisedButton` which are already capitalized. This list can be separated by spaces or commas for example, `capsButtonUiids=UpcaseButton,OtherCustomButton` @@ -161,7 +161,7 @@ Internally, Codename One has many built-in constants, and the list keeps growing SoftKey, Touch, Bar, Title, Right, Native |ComponentGroupBool -|Enables component group, which allows components to be logically grouped together, so the UIID's of components would be modified based on their group placement. This allows for some unique styling effects where the first/last elements have different styles from the rest of the elements. it's disabled by default, thus leaving its usage up to the designer +|Enables component group, which allows components to be logically grouped together, so the UIID's of components would be modified based on their group placement. This allows for some unique styling effects where the first/last elements have different styles from the rest of the elements. It's disabled by default, thus leaving its usage up to the designer |dialogTransitionIn |Default transition for dialog @@ -565,7 +565,7 @@ SoftKey, Touch, Bar, Title, Right, Native |Snap to grid toggle |statusBarScrollsUpBool -|Indicates that a tap on the status bar should scroll up the UI, only relevant in OS's where paintsTitleBarBool is true +|Indicates that a tap on the status bar should scroll up the UI, only relevant in OSes where paintsTitleBarBool is true |switchButtonPadInt |Indicates the padding in the on/off switch, defaults to 16 @@ -610,7 +610,7 @@ SoftKey, Touch, Bar, Title, Right, Native |A hex RGB color which defaults to null in which case this has no effect. When defined this will change the color of the border and label to the given color to match the material design styling. This implements the red border underline in cases of error and the label text color change |textComponentOnTopBool -|Toggles the on top mode which makes things look like they do on Android. This defaults to true on Android and false on other OS's. This can also be manipulated via the `onTopMode(boolean)` method in `InputComponent`, but the layout will only use the theme constant +|Toggles the on top mode which makes things look like they do on Android. This defaults to true on Android and false on other OSes. This can also be manipulated via the `onTopMode(boolean)` method in `InputComponent`, but the layout will only use the theme constant |textComponentAnimBool |toggles the animation mode which again can be manipulated by a method in `InputComponent`. If you want to keep the UI static without the floating hint effect set this to false. Notice this defaults to true only on Android @@ -640,10 +640,10 @@ SoftKey, Touch, Bar, Title, Right, Native |The size of the side menu when expanded and attached to the top in a phone in landscape mode |touchCommandFillBool -|Indicates how the touch menu should layout the commands within +|Indicates how the touch menu should lay out the commands within |touchCommandFlowBool -|Indicates how the touch menu should layout the commands within +|Indicates how the touch menu should lay out the commands within |transitionSpeedInt |Indicates the default speed for transitions @@ -723,27 +723,27 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/advancedth Will set the foreground color of the https://www.codenameone.com/javadoc/com/codename1/ui/Button.html[Button] UIID to white. -When a Codename One https://www.codenameone.com/javadoc/com/codename1/ui/Component.html[Component] is instantiated it requests a https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html[Style] object from the https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html[UIManager] class. The `Style` object is based on the settings within the theme and can be modified thru code or by using the theme. +When a Codename One https://www.codenameone.com/javadoc/com/codename1/ui/Component.html[Component] is instantiated it requests a https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html[Style] object from the https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html[UIManager] class. The `Style` object is based on the settings within the theme and can be modified through code or by using the theme. You can replace the theme dynamically in runtime and refresh the styles assigned to the various components using the https://www.codenameone.com/javadoc/com/codename1/ui/Component.html#refreshTheme--[refreshTheme()] method. -NOTE: it's a common mistake to invoke `refreshTheme()` without actually changing the theme. You see developers doing it when all they need is a `repaint()` or `revalidate()`. Since `refreshTheme()` is **** expensive recommend that you don't use it unless you need to... +NOTE: It's a common mistake to invoke `refreshTheme()` without actually changing the theme. You see developers doing it when all they need is a `repaint()` or `revalidate()`. Since `refreshTheme()` is **** expensive recommend that you don't use it unless you need to... -A theme `Hashtable` key is comprised of: +A theme `Hashtable` key comprises: `[UIID.][type#]attribute` -The _UIID_, corresponds to the component’s UIID for example: `Button`, https://www.codenameone.com/javadoc/com/codename1/ui/CheckBox.html[CheckBox] etc. it's optional and may be omitted to address the global default style. +The _UIID_, corresponds to the component’s UIID for example: `Button`, https://www.codenameone.com/javadoc/com/codename1/ui/CheckBox.html[CheckBox] etc. It's optional and may be omitted to address the global default style. The type is omitted for the default unselected type, and may be one of _sel_ (selected type), _dis_ (disabled type) or _press_ (pressed type). The attribute should be one of: * `derive` - the value for this attribute should be a string representing the base component. * `bgColor` - represents the background color for the component, if applicable, in a web hex string format RRGGBB for example: ff0000 for red. * `fgColor` - represents the foreground color, if applicable. -* border - an instance of the border class, used to display the border for the component. +* `border` - an instance of the border class, used to display the border for the component. * `bgImage` - an https://www.codenameone.com/javadoc/com/codename1/ui/Image.html[Image] object used in the background of a component. -* transparency - a `String` containing a number between 0-255 representing the alpha value for the background. This applies to the bgColor. -* `margin` - the margin of the component as a `String` containing 4 comma separated numbers for top,bottom,left,right. +* `transparency` - a `String` containing a number between 0-255 representing the alpha value for the background. This applies to the bgColor. +* `margin` - the margin of the component as a `String` containing 4 comma separated numbers for top, bottom, left, right. * `padding` - the padding of the component, it has an identical format to the margin attribute. * `font` - A https://www.codenameone.com/javadoc/com/codename1/ui/Font.html[Font] object instance. * `alignment` - an `Integer` object containing the LEFT/RIGHT/CENTER constant values defined in Component. @@ -788,7 +788,7 @@ From the programming perspective this is seamless, a developer accesses one imag You can use two basic methods to add a multi-image: quick add and standard add. -Both methods rely on understanding the source resolution of the image, for example: if you've an icon that you expect to be +Both methods rely on understanding the source resolution of the image, for example: if you have an icon that you expect to be 128×128 pixels on iPhone 4, 102×102 on nexus one and 64×64 on iPhone 3gs. You can provide the source image as the 128 pixel image and perform a quick add option while picking the # High# density option. @@ -862,7 +862,7 @@ image::img/styled-sidemenu-result.png[Side Menu final result,scaledwidth=50%] A side menu is a crucial piece of an elegant application. You will explain how one creates a simple side menu that's elegant, portable and easy to build. This is a good "starting point" side menu from which you can build more elaborate designs. -To get this result you will start from a native theme and a bare bones application to keep things simple. +To get this result you will start from a native theme and a bare-bones application to keep things simple. The code for the side menu is this: @@ -896,7 +896,7 @@ You will set padding to 3 millimeters which provides everything a good feel and .Padding is 3mm so it will feel spacious and touch friendly image::img/styled-sidemenu-4.png[Padding is 3mm so it will feel spacious and touch friendly,scaledwidth=40%] -You will set margin to 0 except for the bottom one pixel which will leave a nice white line by showing off the background. This means the commands will have a space between them and the white style you gave to the #SideNavigationPanel# will appear thru that space. +You will set margin to 0 except for the bottom one pixel which will leave a nice white line by showing off the background. This means the commands will have a space between them and the white style you gave to the #SideNavigationPanel# will appear through that space. .Margin is 0 except for a thin line below each command image::img/styled-sidemenu-5.png[Margin is 0 except for a thin line below each command,scaledwidth=40%] @@ -936,11 +936,11 @@ The last change for the theme is for the #StatusBarSideMenu# UIID which is a spa .StatusBarSideMenu padding for the top of the side menu image::img/styled-sidemenu-11.png[StatusBarSideMenu padding for the top of the side menu,scaledwidth=40%] -Finally, you will add the icon image (or a logo if you've it) into the theme as a multi image so you can use it within the side menu as a good looking logo. A large icon image works as a 2HD multi-image but you can use many strategies to get a fitting image for this spot. +Finally, you will add the icon image (or a logo if you have it) into the theme as a multi image so you can use it within the side menu as a good looking logo. A large icon image works as a 2HD multi-image but you can use many strategies to get a fitting image for this spot. TIP: Rounded images work well here, you can round images dynamically using masking -These steps produce the UI above as a side menu, they might seem like a long set of steps but each step is pretty simple as you walk thru each one. This does show off the versatility and power of Codename One as a change to one step can create a radically different UI design. +These steps produce the UI above as a side menu, they might seem like a long set of steps but each step is pretty simple as you walk through each one. This does show off the versatility and power of Codename One as a change to one step can create a radically different UI design. === Converting a PSD to a theme @@ -996,14 +996,14 @@ Scroll up the hierarchy a bit and uncheck/recheck the eye icon on the left until .Selecting a layer from the region you're interested in image::img/psd2app-image5.png[Find the right element layer you're interested in,scaledwidth=30%] -Right click the layer and select #Convert To Smart Object#. +Right-click the layer and select #Convert To Smart Object#. IMPORTANT: The right click menu will present different options when you click different areas of the layer, clicking on the left area of the layer works .In the right click menu option select "Convert To Smart Object" image::img/psd2app-image6.png[In the right click menu option select "Convert To Smart Object",scaledwidth=40%] -Once the layer hierarchy is a smart object you can double click it which will open the sub hierarchy in a new tab and you now have the pieces of the image you care about. +Once the layer hierarchy is a smart object you can double-click it which will open the sub hierarchy in a new tab and you now have the pieces of the image you care about. .Double clicking the smart object allows you to edit the form you need image::img/psd2app-image7.png[Double clicking the smart object allows you to edit the form you need,scaledwidth=40%] @@ -1021,7 +1021,7 @@ To discover that, click them to select the layer then switch to the text tool: .The text tool allows you to inspect the font used image::img/psd2app-image8.png[The text tool allows you to inspect the font used,scaledwidth=5%] -Then double click the text area layer to find out the font in the top of the UI like this: +Then double-click the text area layer to find out the font in the top of the UI like this: .The Done toolbar entry uses SourceSansPro Regular image::img/psd2app-image9.png[The Done toolbar entry uses SourceSansPro Regular] @@ -1039,13 +1039,13 @@ You can now hide both text layers so they won't pose a problem later. The camera button includes an icon and the button background itself. You can use that as a single image and be done with it, but for this tutorial you will take the harder route of separating this into a button background and a foreground image. -When you click on the camera icon you will notice that the camera icon is comprised of two separate layers: the camera and the "x" symbol above it. You can select both layers using #ctrl-click# (command click on the Mac) and convert both to a smart object together using the same method as before: +When you click on the camera icon you will notice that the camera icon comprises two separate layers: the camera and the "x" symbol above it. You can select both layers using #ctrl-click# (command click on the Mac) and convert both to a smart object together using the same method as before: .The camera smart object image::img/psd2app-image11.png[The camera smart object] Since the image is used as an icon you want it to be square which isn't the situation here! + -This is important as a non-square image can trigger misalignment when dealing with icons and the background. you need to use the #Image# -> #Canvas Size# menu and set the values to be the same (the higher value of the two). +This is important as a non-square image can trigger misalignment when dealing with icons and the background. You need to use the #Image# -> #Canvas Size# menu and set the values to be the same (the higher value of the two). .The canvas size dialog for the camera.png file image::img/psd-image-size.png[The canvas size dialog for the camera.png file,scaledwidth=40%] @@ -1097,10 +1097,10 @@ While that was verbose it was simple. You will create a simple barebones manual NOTE: The reason for this is to avoid "noise," if you use a more elaborate theme it would have some existing settings. This can make the tutorial harder to follow -.Simple bare bones app settings -image::img/psd2app-image14.png[Simple bare bones app settings] +.Simple bare-bones app settings +image::img/psd2app-image14.png[Simple bare-bones app settings] -Once the project is created double click the `theme.res` file and within the designer select #Images# -> #Quick Add Multi Images#. Select the 3 images you created above: `background.jpg`, `camera.png` & `camera-button.png`. Leave the default setting on # High# and press #OK#. +Once the project is created double-click the `theme.res` file and within the designer select #Images# -> #Quick Add Multi Images#. Select the 3 images you created above: `background.jpg`, `camera.png` & `camera-button.png`. Leave the default setting on # High# and press #OK#. Then save the resource file so you can use these images from code. @@ -1132,10 +1132,10 @@ Open the designer and select the theme. Press the #Add# button and type in #Titl Define the padding as: -- Left - 3 millimeter -- Right - 3 millimeter -- Top - 8 millimeter -- Bottom - 2 millimeter +- Left - 3 millimeters +- Right - 3 millimeters +- Top - 8 millimeters +- Bottom - 2 millimeters This will allow enough space for the title. Define margin as 0 on all sides. Then press #OK#. @@ -1155,7 +1155,7 @@ Add the "TitleArea" UIID. In the #Color# tab define transparency as `0` (fully t In the #Border# tab press the #...# button and select #[Empty]#. Press #OK# to save the changes. -Add the "TextField" UIID. In the #Color# tab define transparency as `255` (fully opaque) and the background as `ffffff` (white). Define padding as 2 millimeter on all sides and margin as 0 on all sides. + +Add the "TextField" UIID. In the #Color# tab define transparency as `255` (fully opaque) and the background as `ffffff` (white). Define padding as 2 millimeters on all sides and margin as 0 on all sides. + In the #Border# tab press the #...# button and select #[Empty]#. In the #Font# tab set the #True Type# to #native:MainLight# and set the size to 2. Press #OK# to save the changes. diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index 2bf0a074b2..10ce4eb53f 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -32,7 +32,7 @@ Here is the current list of supported arguments. Build hints change over time, s |defaults to an empty string. Allows developers of native Android code to add text within the application block to define things such as widgets, services etc. |android.permission.PERMISSION_NAME -|true/false Whether to include a particular permission. Use of these build hints is preferred to `android.xpermissions` since they avoid possible conflicts with libraries. See https://developer.android.com/reference/android/Manifest.permission.html[Android's Manifest.permission docs] for a full list of permissions. +|true/false Whether to include a particular permission. Use of these build hints is preferred to `android.xpermissions` since they avoid possible conflicts with libraries. See https://developer.android.com/reference/android/Manifest.permission.html[Android's `Manifest.permission` docs] for a full list of permissions. |android.permission.PERMISSION_NAME.maxSdkVersion |Will be translated to the `maxSdkVersion` attribute of the `` tag for the corresponding `android.permission.PERMISSION_NAME` build hint. (Optional) @@ -82,7 +82,7 @@ Here is the current list of supported arguments. Build hints change over time, s |Device key used to mark a specific Android device as a test device for Google Play ads defaults to C6783E2486F0931D9D09FABC65094FDF |android.includeGPlayServices -|*Deprecated, please android.playService.+++*+++!* Indicates whether Goolge Play Services should be included into the build, defaults to false but that might change based on the functionality of the application and other build hints. Adding Google Play Services support allows you to use a more refined location implementation and invoke some Google specific functionality from native code. +|*Deprecated, please android.playService.+++*+++!* Indicates whether Google Play Services should be included into the build, defaults to false but that might change based on the functionality of the application and other build hints. Adding Google Play Services support allows you to use a more refined location implementation and invoke some Google specific functionality from native code. |android.playService.plus, android.playService.auth, android.playService.base, android.playService.identity, android.playService.indexing, android.playService.appInvite, android.playService.analytics, android.playService.cast, android.playService.gcm, android.playService.drive, android.playService.fitness, android.playService.location, android.playService.maps, android.playService.ads, android.playService.vision, android.playService.nearby, android.playService.panorama, android.playService.games, android.playService.safetynet, android.playService.wallet, android.playService.wearable |Allows including only a specific play services library portion. Notice that this setting conflicts with the deprecated `android.includeGPlayServices` and only works with the Gradle-based Android build pipeline. + @@ -93,10 +93,10 @@ If none of the services are defined to true then plus, auth, base, analytics, gc | The version number of play services to build against. Experimental. **Use with caution** as building against versions other than the server default may introduce incompatibilities with some Codename One APIs. |xxx.minPlayServicesVersion -|This is a special case build hint. You can use any prefix to the build hint and the convention is to use your cn1lib name. it's identical to `android.minPlayServicesVersion` with the exception that the "highest version wins." That way if your cn1lib requires play services 9+ and uses: `myLib.minPlayServicesVersion=9.0.0` and another library has `otherLib.minPlayServicesVersion=10.0.0` then play services will be 10.0.0 +|This is a special case build hint. You can use any prefix to the build hint and the convention is to use your cn1lib name. It's identical to `android.minPlayServicesVersion` with the exception that the "highest version wins." That way if your cn1lib requires play services 9+ and uses: `myLib.minPlayServicesVersion=9.0.0` and another library has `otherLib.minPlayServicesVersion=10.0.0` then play services will be 10.0.0 |android.multidex -|Boolean true/false defaults to false. Multidex allows Android binaries to reference more than 65536 methods. This slows builds a bit so you've it off by default but if you get a build error mentioning this limit you should turn this on. +|Boolean true/false defaults to false. Multidex allows Android binaries to reference more than 65536 methods. This slows builds a bit so you have it off by default but if you get a build error mentioning this limit you should turn this on. |android.headphoneCallback |Boolean true/false defaults to false. When set to true it assumes the main class has two methods: `headphonesConnected` & `headphonesDisconnected` which it invokes appropriately as needed @@ -126,13 +126,13 @@ If none of the services are defined to true then plus, auth, base, analytics, gc |Optional path (relative to the root of the native Android project) to an image file to use as the adaptive icon background when `android.enableAdaptiveIcons=true`. If this property is set, it overrides `android.adaptiveIconBackground`. |android.cusom_layout1 -|Applies to any number of layouts as long as they're in sequence (for example, android.cusom_layout2, android.cusom_layout3 etc.). Will write the content of the argument as a layout xml file and give it the name `cusom_layout1.xml` onwards. This can be used by native code to work with XML files +|Applies to any number of layouts as long as they're in sequence (for example, android.cusom_layout2, android.cusom_layout3 etc.). Will write the content of the argument as a layout XML file and give it the name `cusom_layout1.xml` onwards. This can be used by native code to work with XML files |android.keyboardOpen |Boolean true/false defaults to true. Toggles the new async keyboard mode that leaves the keyboard open while you move between text components |android.versionCode -|Allows overriding the auto generated version number with a custom internal version number specifically used for the xml attribute `android:versionCode` +|Allows overriding the auto generated version number with a custom internal version number specifically used for the XML attribute `android:versionCode` |android.captureRecord |Indicates whether the `RECORD_AUDIO` permission should be requested. Can be `enabled` or any other value to disable this option @@ -227,12 +227,12 @@ If none of the services are defined to true then plus, auth, base, analytics, gc | desktop.mac.plist.PLISTKEY | Set the key `PLISTKEY` in the Info.plist file for desktop mac build. For example, `desktop.mac.plist.LSApplicationCategoryType=public.app-category.business`. See https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html[Apple Documentation of Info.plist keys and values for a full list of supported keys]. + -Only supported for App Store builds. See https://www.codenameone.com/developer-guide.html#_mac_os_desktop_build_options[Mac OS Desktop Build Options] for more information. +Only supported for App Store builds. See https://www.codenameone.com/developer-guide.html#_mac_os_desktop_build_options[macOS Desktop Build Options] for more information. | desktop.mac.plistInject | Injects raw XML into the Info.plist file for desktop builds. For example, `desktop.mac.plistInject=LSApplicationCategoryTypepublic.app-category.business` + -Only supported for App Store builds. See https://www.codenameone.com/developer-guide.html#_mac_os_desktop_build_options[Mac OS Desktop Build Options] for more information. +Only supported for App Store builds. See https://www.codenameone.com/developer-guide.html#_mac_os_desktop_build_options[macOS Desktop Build Options] for more information. |ios.associatedDomains |Comma-delimited list of domains associated with this app. Since 6.0. Note that each domain should be prefixed by a supported prefix. For example, "applinks:" or "webcredentials:." See https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains?language=objc[Apple's documentation on Associated domains] for more information. @@ -353,7 +353,7 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g |true/false (defaults to `false`). Setting this flag to `true` will cause available java source files to be included in the resulting .zip and .war files. These may be used by Chrome during debugging. |javascript.stopOnErrors -|true/false (defaults to `true`). Cause JavaScript build to fail if there are warnings during the build. Sometimes build warnings won't affect the running of the app. For example, if the Javascript port is missing a method that the app depends on, but it isn't used in most of the app. Or if there is multithreaded code detected in static initializers, but that code-path isn't used by the app. Setting this to `false` may allow you to get past some build errors, but it might just result in runtime errors later on, which are much more difficult to debug. *This build hint is only available in Codename One 3.4 and later. +|true/false (defaults to `true`). Cause JavaScript build to fail if there are warnings during the build. Sometimes build warnings won't affect the running of the app. For example, if the JavaScript port is missing a method that the app depends on, but it isn't used in most of the app. Or if there is multithreaded code detected in static initializers, but that code-path isn't used by the app. Setting this to `false` may allow you to get past some build errors, but it might just result in runtime errors later on, which are much more difficult to debug. *This build hint is only available in Codename One 3.4 and later. |javascript.teavm.version | (Optional) The version of TeaVM to use for the build. *Use caution*, only use this property if you know what you're doing! @@ -393,7 +393,7 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g |Objective-C code that can be injected into the iOS callback method (message) `viewDidLoad` |ios.googleAdUnitIdPadding -|Indicates the amount of padding to pass to the Google ads placed at the bottom of the screen with `google.adUnitId` +|Indicates the amount of padding to pass to the Google Ads placed at the bottom of the screen with `google.adUnitId` |ios.enableBadgeClear |Boolean true/false defaults to true. Clears the badge value with every load of the app, this is useful if the app doesn't manually keep track of number values for the badge @@ -449,7 +449,7 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g |Height in pixels for the form in desktop builds, will be doubled for retina grade displays. Defaults to 600. |desktop.adaptToRetina -|Boolean true/false defaults to true. When set to true some values will ve implicitly doubled to deal with retina displays and icons etc. will use higher DPI's +|Boolean true/false defaults to true. When set to true some values will ve implicitly doubled to deal with retina displays and icons etc. Will use higher DPI's |desktop.resizable |Boolean true/false defaults to true. Indicates whether the UI in the desktop build is resizable @@ -461,7 +461,7 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g |Name of the theme res file (without the ".res" extension) to use as the "native" theme. By default this is native indicating iOS theme on Mac and Windows Metro on Windows. If its something else then the app will try to load the file /themeName.res (placed in native/Java SE directory). |desktop.themeMac -|Same as `desktop.theme` but specific to Mac OS +|Same as `desktop.theme` but specific to macOS |desktop.themeWin |Same as `desktop.theme` but specific to Windows @@ -482,13 +482,13 @@ Only supported for App Store builds. See https://www.codenameone.com/developer-g |The JVM that should be bundled in the Windows desktop build. Windows desktop builds only. Supported values: zulu8, zuluFx8, zulu8-32bit, zuluFx8-32bit, zulu11, zuluFx11, zulu11-32bit, zuluFx11-32bit |windows.extensions -|Historical build hint for the discontinued UWP target. it's retained here only for legacy reference and isn't used by current supported build targets. +|Historical build hint for the discontinued UWP target. It's retained here only for legacy reference and isn't used by current supported build targets. |win.vm32bit |true/false (defaults to false). Forces windows desktop builds to use the Win32 JVM instead of the 64 bit VM making them compatible with older Windows Machines. This is off by default at the moment because of a bug in JDK 8 update 112 that might cause this to fail for some cases |win.installDirName -|Windows desktop builds only. Overrides the default installation folder name suggested by the installer (under `Program Files`). Defaults to the application's main class name for backward compatibility. Use this build hint to set a user-friendly installation folder name (for example, `win.installDirName=My Application`). The application id used by Windows for upgrade detection is unaffected, so existing installs continue to upgrade. +|Windows desktop builds only. Overrides the default installation folder name suggested by the installer (under `Program Files`). Defaults to the application's main class name for backward compatibility. Use this build hint to set a user-friendly installation folder name (for example, `win.installDirName=My Application`). The application ID used by Windows for upgrade detection is unaffected, so existing installations continue to upgrade. |win.shortcutName |Windows desktop builds only. Overrides the name used for the Start Menu shortcut, the Desktop shortcut and (when `win.launchOnStart=true`) the autostart shortcut. Defaults to the application's main class name for backward compatibility. Use this build hint to set a user-friendly shortcut label (for example, `win.shortcutName=My Application`). @@ -551,7 +551,7 @@ If you explicitly lower the target SDK (for example: `android.targetSDKVersion=2 .Install UI when using the old permissions system image::img/marshmallow-permissions-level21.png[Install UI when using the old permissions system,scaledwidth=20%] -When you keep the default target (API 33+) the installer defers to the runtime permission flow and the install UI looks like this instead: +When you keep the default target (API 33+) the installer defers to the runtime permission flow and the installation UI looks like this instead: .Install UI when using the new permissions system image::img/marshmallow-permissions-level23.png[Install UI when using the new permissions system,scaledwidth=20%] @@ -578,7 +578,7 @@ Notice that denying this second request won't trigger another Codename One promp ===== Code changes -No explicit code changes needed for this functionality to "work." The respective API's will work like they always worked and will prompt the user seamlessly for permissions. +No explicit code changes needed for this functionality to "work." The respective APIs will work like they always worked and will prompt the user seamlessly for permissions. TIP: Some behaviors that never occurred on Android but were legal in the past might start occurring with the switch to the new API. For example: the location manager might be null and your app must always be ready to deal with such a situation @@ -682,7 +682,7 @@ Here are steps that should work for everyone: NOTE: You might need to copy more Gradle file meta-data such as multi-dexing etc. -You might not need to repeat the whole thing with every build. For example: it might be practical to copy the `userSources.jar` from the libs directory to get the latest version of your code. You can copy the `src/main` directory to get the latest up to date Android port. +You might not need to repeat the whole thing with every build. For example: it might be practical to copy the `userSources.jar` from the libs directory to get the latest version of your code. You can copy the `src/main` directory to get the latest up-to-date Android port. === Native interfaces @@ -691,7 +691,7 @@ Sometimes you may wish to use an API that's unsupported by Codename One or integ ==== Introduction -Notice that when you say "native" you don't mean C/C++ always but rather the platforms "native" environment. for Android, Java or Kotlin code can be invoked with full access to the Android API. In case of iOS, Objective-C or Swift code can be invoked and so forth. +Notice that when you say "native" you don't mean C/C++ always but rather the platforms "native" environment. For Android, Java or Kotlin code can be invoked with full access to the Android API. In case of iOS, Objective-C or Swift code can be invoked and so forth. TIP: You can still access C code under Android either by using JNI from the Android native code or by using a library @@ -701,7 +701,7 @@ NOTE: The reason for the limits is the disparity between the platforms. Mapping Furthermore, native methods should avoid features such as overloading, varargs (or any Java 5+ feature for that matter) to allow portability for languages that don't support such features. -IMPORTANT: don't rely on pass by reference/value behavior since they vary between platforms +IMPORTANT: Don't rely on pass by reference/value behavior since they vary between platforms Implementing a native layer effectively means: @@ -716,7 +716,7 @@ For example: to create a simple hello world interface do something like: include::../demos/common/src/main/java/com/mycompany/myapp/MyNative.java[tag=myNativeInterface,indent=0] ---- -You now need to right click the class in the IDE and select the #Generate Native Access# menu item: +You now need to right-click the class in the IDE and select the #Generate Native Access# menu item: .Generating the native code image::img/native-interfaces-generate-menu.png[Generating the native code,scaledwidth=20%] @@ -766,12 +766,12 @@ include::../demos/android/src/main/java/com/mycompany/myapp/MyNativeImpl.java[ta <2> The impl class doesn't physically implement the `MyNative` interface! + This is intentional and due to the `PeerComponent` functionality mentioned below. You don't need to add an implements clause. -<3> Notice that there is no constructor and the class is public. it's crucial that the system will be able to assign the class without obstruction. You can use a constructor but it can't have any arguments and you shouldn't rely on semantics of construction. +<3> Notice that there is no constructor and the class is public. It's crucial that the system will be able to assign the class without obstruction. You can use a constructor but it can't have any arguments and you shouldn't rely on semantics of construction. <4> You implemented the native method and that you set `isSupported` to true. IMPORTANT: The IDE won't provide completion suggestions and will claim that there are errors in the code! + -Codename One doesn't include the native platforms in its bundle for example: the full Android SDK or the full Xcode Objective-C runtime. For example, since the native code is compiled on the servers (where these runteims are present) this shouldn't be a problem +Codename One doesn't include the native platforms in its bundle for example: the full Android SDK or the full Xcode Objective-C runtime. For example, since the native code is compiled on the servers (where these runtimes are present) this shouldn't be a problem TIP: When implementing a non-trivial native interface, send a server build with the "Include Source" option checked. Implement the native interface in the native IDE then copy and paste the native code back into Codename One @@ -799,9 +799,9 @@ For Android native interfaces you can implement the generated `...Impl` class in ===== Use the Android main thread (native EDT) -iOS, Android & pretty much any modern OS has an EDT like thread that handles events etc. The problem is that they differ in their nuanced behavior. For example: Android will respect calls off of the EDT and iOS will often crash. Some OS's enforce EDT access and will throw an exception when you violate that... +iOS, Android & pretty much any modern OS has an EDT like thread that handles events etc. The problem is that they differ in their nuanced behavior. For example: Android will respect calls off of the EDT and iOS will often crash. Some OSes enforce EDT access and will throw an exception when you violate that... -You don't need to know about these things, hidden functionality within your implementation bridges between your EDT and the native EDT to provide consistent cross platform behavior. However, when you write native code you need awareness. +You don't need to know about these things, hidden functionality within your implementation bridges between your EDT and the native EDT to provide consistent cross-platform behavior. However, when you write native code you need awareness. .Why not Implicitly call Native Interfaces on the Native EDT? **** @@ -817,7 +817,7 @@ Within your native code in Android do something like: include::../demos/android/src/main/java/com/codenameone/developerguide/advancedtopics/AndroidThreadSnippets.java[tag=androidRunOnUiThread,indent=0] ---- -This will execute the block within `run()` asynchronously on the native Android UI thread. If you need synchronous execution you've a special method for Codename One: +This will execute the block within `run()` asynchronously on the native Android UI thread. If you need synchronous execution you have a special method for Codename One: [source,java] ---- @@ -851,7 +851,7 @@ android.gradleDep=compile 'io.intercom.android:intercom-sdk:3.+' This would "work" but there is a catch... -You might need to define the specific version of the Android SDK used and specific version of Google play services version used. Intercom is pretty sensitive about those and demanded that you also add: +You might need to define the specific version of the Android SDK used and specific version of Google Play Services version used. Intercom is pretty sensitive about those and demanded that you also add: ---- android.playServices=9.8.0 @@ -911,19 +911,19 @@ You can use this for synchronous invocation, notice the lack of the `a` in the d include::../demos/ios/src/main/objectivec/DispatchExamples.m[tag=dispatchSync,indent=0] ---- -The problem with the synchronous call is that it will block the caller thread, if the caller thread is the EDT this can cause performance issues and even a deadlock. it's important to be cautious with this call! +The problem with the synchronous call is that it will block the caller thread, if the caller thread is the EDT this can cause performance issues and even a deadlock. It's important to be cautious with this call! ===== Use Cocoapods for dependencies Cocoapods are the iOS equivalent of Gradle dependencies. -CocoaPods allow you to add a native library dependency to iOS far more than Gradle. By default you target iOS 7.0 or newer which is supported by Intercom for older versions of the library. Annoyingly CocoaPods might seem to work but some specific API's won't work since it fell back to an older version... To solve this you've to explicitly define the build hint `ios.pods.platform=8.0` to force iOS 8 or newer. You might need to force it to even newer versions as some libraries force an iOS 9 least etc. +CocoaPods allow you to add a native library dependency to iOS far more than Gradle. By default you target iOS 7.0 or newer which is supported by Intercom for older versions of the library. Annoyingly CocoaPods might seem to work but some specific APIs won't work since it fell back to an older version... To solve this you've to explicitly define the build hint `ios.pods.platform=8.0` to force iOS 8 or newer. You might need to force it to even newer versions as some libraries force an iOS 9 least etc. Including intercom itself required a single build hint: `ios.pods=Intercom` which you can obviously extend by using commas to include many libraries. You can search the https://cocoapods.org/[CocoaPods website] for supported 3rd party libraries which includes everything you would expect. One important advantage when working with CocoaPods is the faster build time as the upload to the Codename One website is smaller and the bandwidth you've to CocoaPods is faster. Another advantage is the ability to keep up with the latest developments from the library providers. -==== Javascript +==== JavaScript -Native interfaces in Javascript look a little different than the other platforms since Javascript doesn't natively support threads or classes. The native implementation should be placed in a file with name matching the name of the package and the class name combined where the "." elements are replaced by underscores. +Native interfaces in JavaScript look a little different than the other platforms since JavaScript doesn't natively support threads or classes. The native implementation should be placed in a file with name matching the name of the package and the class name combined where the "." elements are replaced by underscores. The default generated stubs for the JavaScript build look like this `com_mycompany_myapp_MyNative`: @@ -947,7 +947,7 @@ exports.com_mycompany_myapp_MyNative= o; })(cn1_get_native_interfaces()); ---- -A simple implementation looks like this. +A simple implementation looks like this: [source,JavaScript] ---- @@ -968,7 +968,7 @@ exports.com_my_code_MyNative = o; })(cn1_get_native_interfaces()); ---- -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. +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. WARNING: Make sure you call either `callback.complete()` or `callback.error()` in your method at some point, or you will cause a deadlock in your app (code calling your native method will sit and "wait" forever for your method to return a value). @@ -979,7 +979,7 @@ The naming conventions for the methods themselves are modeled after the naming c Where `` is the name of the method in Java, and the ``s are a string representing the parameter type. The general rule for these strings are: -1. Primitive types are mapped to their type name. (For example: `int` to "int," `double` to "double," etc...). +1. Primitive types are mapped to their type name. (For example: `int` to "int," `double` to "double," etc.). 2. Reference types are mapped to their fully qualified class name with '.' replaced with underscores. For example: `java.lang.String` would be `java_lang_String`. 3. Array parameters are marked by their scalar type name followed by an underscore and `1ARRAY`. For example: `int[]` would be "int_1ARRAY" and `String[]` would be `java_lang_String_1ARRAY`. @@ -1062,7 +1062,7 @@ include::../demos/ios/src/main/objectivec/PeerComponentExamples.m[tag=createPeer TIP: Not all platforms support native peers. Specifically Java SE doesn't support them due to the way the Java SE native interfaces are mapped to their implementation. + Note that this won't limit the code from running on an unsupported platform. Only that specific method won't work. -Javascript would expect a DOM Element (for example: a `
` tag to be returned.). For example: +JavaScript would expect a DOM Element (for example: a `
` tag to be returned.). For example: [source,JavaScript] ---- @@ -1172,7 +1172,7 @@ Permissions in Codename One are seamless. Codename One traverses the bytecode an For example, when accessing native functionality this won’t work since native code might require specialized permissions and you don’t/can’t run any serious analysis on it (it can be about anything). If you require more permissions in your Android native code you need to define them in the build arguments using -`android.permission.=true` for each permission you want to include. A full list of permissions are listed in Android's https://developer.android.com/reference/android/Manifest.permission.html[Manifest.permission documentation]. +`android.permission.=true` for each permission you want to include. A full list of permissions are listed in Android's https://developer.android.com/reference/android/Manifest.permission.html[`Manifest.permission` documentation]. For example: @@ -1207,7 +1207,7 @@ NOTE: You need to include the full XML snippet. You can unify many lines into a ==== Native AndroidNativeUtil -If you do any native interfaces programming in Android you should be familiar with the `AndroidNativeUtil` class which allows you to access native device functionality more from the native code. For example: many Android API's need access to the `Activity` which you can get by calling `AndroidNativeUtil.getActivity()`. +If you do any native interfaces programming in Android you should be familiar with the `AndroidNativeUtil` class which allows you to access native device functionality more from the native code. For example: many Android APIs need access to the `Activity` which you can get by calling `AndroidNativeUtil.getActivity()`. The native util class includes a few other features such as: @@ -1218,7 +1218,7 @@ The native util class includes a few other features such as: `onCreate` etc. which can be pretty useful for some cases. * `registerViewRenderer` - https://www.codenameone.com/javadoc/com/codename1/ui/PeerComponent.html[PeerComponent]'s are shown on top of the UI since they're rendered within - their own thread outside of the EDT cycle. when you need to show a https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html[Dialog] on top of the peer you grab a + their own thread outside of the EDT cycle. When you need to show a https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html[Dialog] on top of the peer you grab a screenshot of the peer, hide it and then show the dialog with the image as the background (the same applies for transitions). Some components (specifically the MapView) might not render and require custom code to implement the transferal to a native Bitmap, this API allows you to do that. @@ -1234,7 +1234,7 @@ include::../demos/android/src/main/java/com/codenameone/developerguide/advancedt A common way to implement features in Android is the `BroadcastReceiver` API. This allows intercepting operating system events for common use cases. -A good example is intercepting incoming SMS which is specific to Android so you'd need a broardcast receiver to implement that. This is often confusing to developers who sometimes derive the impl class from broadcast receiver. that's a mistake... +A good example is intercepting incoming SMS which is specific to Android so you'd need a broadcast receiver to implement that. This is often confusing to developers who sometimes derive the impl class from broadcast receiver. That's a mistake... The solution is to place any native Android class into the `native/android` directory. It will get compiled with the rest of the native code and "works." You can therefore place this class under `native/android/com/codename1/sms/intercept`: @@ -1324,7 +1324,7 @@ include::../demos/common/src/main/java/com/codename1/sms/intercept/NativeSMSInte That's easy! -Notice that `isSupported()` returns false for all other OS's so you won't need to ask whether this is "Android" you can use `isSupported()`. +Notice that `isSupported()` returns false for all other OSes so you won't need to ask whether this is "Android" you can use `isSupported()`. The implementation is pretty easy too: @@ -1335,7 +1335,7 @@ include::../demos/android/src/main/java/com/codename1/sms/intercept/NativeSMSInt <1> This will trigger the permission prompt on Android 6 and newer. Even though the permission is declared in XML this isn't enough for 6+. Notice that even when you run on Android 6 you still need to declare permissions in XML! -<2> Here you actually bind the listener, this allows you to grab one SMS and not listen in on every SMS coming thru +<2> Here you actually bind the listener, this allows you to grab one SMS and not listen in on every SMS coming through ==== Native code callbacks @@ -1345,8 +1345,8 @@ Native interfaces standardize the invocation of native code from Codename One, b A common "trick" for calling back is to define a static method and then trigger it from native code. This works for Android & Java SE since those platforms use Java for their "native code." Mapping this to iOS requires some basic understanding of how the iOS VM works. -For this explanation lets pretend you've a class called NativeCallback in the src hierarchy under -the package `com.mycompany` that has the method: `public static void callback()`. +For this explanation lets pretend you have a class called NativeCallback in the src hierarchy under +the package `com.mycompany` that has the method: `public static void callback()`: [source,java] ---- @@ -1449,11 +1449,11 @@ include::../demos/ios/src/main/objectivec/NativeCallbackSnippets.m[tag=nativeCal The upper case R allows you to differentiate between void `callback(int,int)` and `int callback(int)`. -TIP: Covariant return types are a little known Java 5 feature. For example: the method `Object getX()` can be overriden by `MyObject getX()`. For example, in the VM level they can both exist side by side. +TIP: Covariant return types are a little known Java 5 feature. For example: the method `Object getX()` can be overridden by `MyObject getX()`. For example, in the VM level they can both exist side by side. ===== Accessing callbacks from JavaScript -The mechanism for invoking static callback methods from Javascript (for the Javascript port ) is like Objective-C's. The `this` object in your native interface method contains a property named `$GLOBAL$` that provides access to static Java methods. This object will contain Javascript mirror objects for each Java class (though the property name is mangled by replacing "." with underscores). Each mirror object contains a wrapper method for its underlying class's static methods where the method name follows the same naming convention as is used for the Javascript native methods themselves (and like the naming conventions used in Objective-C). +The mechanism for invoking static callback methods from JavaScript (for the JavaScript port ) is like Objective-C's. The `this` object in your native interface method contains a property named `$GLOBAL$` that provides access to static Java methods. This object will contain JavaScript mirror objects for each Java class (though the property name is mangled by replacing "." with underscores). Each mirror object contains a wrapper method for its underlying class's static methods where the method name follows the same naming convention as is used for the JavaScript native methods themselves (and like the naming conventions used in Objective-C). For example, the Google Maps project includes the static callback method: @@ -1464,7 +1464,7 @@ include::../demos/common/src/main/java/com/codename1/googlemaps/MapContainerCall It's defined in the `com.codename1.googlemaps.MapContainer` class. -This method is called from Javascript inside a native interface using the following code: +This method is called from JavaScript inside a native interface using the following code: [source,JavaScript] ---- @@ -1476,7 +1476,7 @@ google.maps.event.addListener(this.map, 'bounds_changed', function() { 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. -WARNING: Your code *MUST* contain the full string path `this.$GLOBAL$.your_class_name.your_method_name` or the build server won't be able to recognize that your code requires this method. The `$GLOBAL$` object is populated by the build server with those classes and methods that are used inside your native methods. If the build server doesn't recognize that the methods are being used (through this pattern) it won't generate the necessary wrappers for your Javascript code to access the Java methods. +WARNING: Your code *MUST* contain the full string path `this.$GLOBAL$.your_class_name.your_method_name` or the build server won't be able to recognize that your code requires this method. The `$GLOBAL$` object is populated by the build server with those classes and methods that are used inside your native methods. If the build server doesn't recognize that the methods are being used (through this pattern) it won't generate the necessary wrappers for your JavaScript code to access the Java methods. ===== Callbacks of the SMS receiver @@ -1495,7 +1495,7 @@ include::../demos/common/src/main/java/com/codename1/sms/intercept/SMSCallback.j ===== Asynchronous callbacks & threading -One of the problematic aspects of calling back into Java from Javascript is that Javascript has no notion of multi-threading. If the method you're calling uses Java's threads at all (for example: It includes a `wait()`, `notify()`, `sleep()`, `callSerially()`, etc...) you need to call it asynchronously from Javascript. You can call a method asynchronously by appending `$async` to the method name. For example: With the Google Maps example above, you would change : +One of the problematic aspects of calling back into Java from JavaScript is that JavaScript has no notion of multi-threading. If the method you're calling uses Java's threads at all (for example: It includes a `wait()`, `notify()`, `sleep()`, `callSerially()`, etc.) you need to call it asynchronously from JavaScript. You can call a method asynchronously by appending `$async` to the method name. For example: With the Google Maps example above, you would change : [source,JavaScript] ---- @@ -1518,12 +1518,12 @@ TIP: Usually you should use the *async* version of a method when calling it from Support for JAR files in Codename One has been a source of confusion so its probably a good idea to revisit this subject again and clarify all the details. -The first source of confusion is changing the classpath. You should NEVER change the classpath or add an external JAR through the IDE classpath UI. The reasoning here is simple, these IDE's don't package the JAR's into the final executable and even if they did these JAR's would probably use features unavailable or inappropriate for the device (for example: `java.io.File` etc.). +The first source of confusion is changing the classpath. You should NEVER change the classpath or add an external JAR through the IDE classpath UI. The reasoning here is simple, these IDEs don't package the JARs into the final executable and even if they did these JARs would probably use features unavailable or inappropriate for the device (for example: `java.io.File` etc.). -.don't change the classpath, this is how it should look for a typical Java 8 Codename One application -image::img/cn1libs-dont-change-classpath.png[don't change the classpath, this is how it should look for a typical Java 8 Codename One application,scaledwidth=40%] +.Don't change the classpath, this is how it should look for a typical Java 8 Codename One application +image::img/cn1libs-dont-change-classpath.png[Don't change the classpath, this is how it should look for a typical Java 8 Codename One application,scaledwidth=40%] -Cn1libs are Codename One's file format for 3rd party extensions. it's physicially a zip file containing other zip files and some meta-data. +Cn1libs are Codename One's file format for 3rd party extensions. It's physically a zip file containing other zip files and some meta-data. ==== Why not use JAR @@ -1533,7 +1533,7 @@ Jars don't include support for writing native code; you could use JNI in jars bu Jars don't support "proper" code completion, a common developer trick is to stick source code into the jar but that prevents usage with proprietary code. Cn1libs provide full IDE code completion (with JavaDoc hints) without exposing the sources. -There are two use cases for wanting JAR's and they both have different solutions: +There are two use cases for wanting JARs and they both have different solutions: . Modularity . Working with an existing JARs @@ -1755,13 +1755,13 @@ While its pretty easy to use native interfaces to write Android native code some obvious. For example: if you want to integrate a 3rd party library, specifically one that includes native C JNI code this process isn't as straightforward. -If you need to integrate such a library into your native calls you've the following options: +If you need to integrate such a library into your native calls you have the following options: . The first option (and the easiest one) is to place a Jar file in the native/android directory. This will link your binary with the jar file. Just place the jar under the native/android and the build server will pick it up and will add it to the classpath. + Notice that Android release apps are obfuscated by default which might cause issues with such libraries if they -reference API's that are unavailable on Android. You can workaround this by adding a build hint to the proguard +reference APIs that are unavailable on Android. You can work around this by adding a build hint to the proguard obfuscation code that blocs the obfuscation of the problematic classes using the build hint: + `android.proguardKeep=-keep class com.mypackage.ProblemClass { *; }`` @@ -1778,13 +1778,13 @@ Rename the extension from `.zip` to `.andlib` and place the andlib file under th === Drag & drop -Unlike other platforms that tried to create overly generic catch all API's Codename One tried to make things as simple as possible. +Unlike other platforms that tried to create overly generic catch all APIs Codename One tried to make things as simple as possible. In Codename One components can be dragged and drop targets are always components. The logic of actually performing the operation indicated by the drop is the responsibility of the person implementing the drop. NOTE: Some platforms for example: AWT allow dragging abstract concepts such as mime type elements. This allows dragging things like a text file into the app, but that use case isn't realistic in mobile -The code below allows you to rearrange the items based on a sensible order. Notice it relies on the default `Container` drop behavior. +The code below allows you to rearrange the items based on a sensible order. Notice it relies on the default `Container` drop behavior: [source,java] ---- @@ -1849,7 +1849,7 @@ of your project. It should look like this: ---- -=== Intercepting URL's on iOS & Android +=== Intercepting URLs on iOS & Android A common trick in mobile application development, is communication between two unrelated applications. @@ -1942,11 +1942,11 @@ Use this checklist when the integration can activate payment credentials or acco _Where_: native Android code (bridge activity/native interface), because this requires Android `PackageManager` APIs. . **Check payload freshness** (nonce/timestamp/challenge) before approving. + _Where_: back end/API service as the source of truth. -. **Bind response to request** (request id / correlation id) and reject mismatched or replayed responses. + +. **Bind response to request** (request ID / correlation ID) and reject mismatched or replayed responses. + _Where_: back end first, then enforce in CN1/native completion flow. . **Time-box bridge activity state** and fail closed on timeout, process death, or missing bridge instance. + _Where_: native Android bridge + CN1 timeout handling. -. **Audit log** caller package, request id, decision, and failure reason (without storing full PAN/token data). + +. **Audit log** caller package, request ID, decision, and failure reason (without storing full PAN/token data). + _Where_: back end and security telemetry pipeline. **** @@ -2091,7 +2091,7 @@ The equivalent on the iOS side would be: ios.plistInject=CFBundleURLTypes CFBundleURLName com.yourcompany.myapp CFBundleURLSchemes myapp ---- -For example, that can conflict with the Facebook integration if you use `FacebookConnect` which needs access to the schemes. To workaround it you can use the build hint `ios.urlScheme` for example: +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] ---- @@ -2118,10 +2118,10 @@ of Codename One's code is in Java hence its portable and pretty easy to maintain ==== Why does Codename One need native widgets at all -You need the native device to do input, html rendering etc. these are too big and too complex tasks for Codename One to +You need the native device to do input, html rendering etc. These are too big and too complex tasks for Codename One to do from scratch. -They're sometimes impossible to perform without the native platform. For example: the virtual keyboard input on the devices is tied directly to the native text input. it's impractical to implement everything from scratch for all languages, dictionaries etc. The result would be sub-par. +They're sometimes impossible to perform without the native platform. For example: the virtual keyboard input on the devices is tied directly to the native text input. It's impractical to implement everything from scratch for all languages, dictionaries etc. The result would be sub-par. A web browser can't be implemented in this age without a JavaScript JIT and including a JIT within an iOS app is prohibited by Apple. @@ -2147,13 +2147,13 @@ Codename One grabs a screenshot of the peer, hide it and then you can show the s ==== Why can't you combine peer component scrolling and Codename One scrolling -Since the form title/footer etc. are drawn by Codename One the peer component might paint itself on top of them. +Since the form title/footer etc. Are drawn by Codename One the peer component might paint itself on top of them. Clipping a peer component is often pretty difficult. Furthermore, if the user drags his finger within the peer component he might trigger the native scroll within the might collide with your scrolling? ==== Native components in the first Form -Another problem might be counter intuitive. iOS has screenshot images representing the first form. If your first page is an HTML or a native map (or other peer widget) the screenshot process on the build server will show fallback code instead of the real thing thus providing sub-par behavior. +Another problem might be counterintuitive. iOS has screenshot images representing the first form. If your first page is an HTML or a native map (or other peer widget) the screenshot process on the build server will show fallback code instead of the real thing thus providing sub-par behavior. Its impractical to support something like HTML for the screenshot process since it would also look different from the web component running on the device. @@ -2214,7 +2214,7 @@ Strings, as are return values. are written in the native language of the platform (for example: Java for Android, and Objective-C for iOS). 4. *Native dependencies*. Any 3rd party libraries required for the native code to work, need to be included for each platform. On android, this may mean bundling.jar files,.aar files, or.andlib files. On iOS, this may mean -bundling.h files,.a files,.framework, and.bundle files. +bundling `.h` files, `.a` files, `.framework`, and `.bundle` files. 5. *Build hints*. Some libraries will require you to add some extra build hints to your project. For example: On Android you may need to add permissions to the manifest, or define services in the `` section of the manifest. On iOS, this may mean specifying more core frameworks for inclusion, or adding build flags for compilation. @@ -2275,7 +2275,7 @@ In any case, you don't have to come up with the specifics right now, as you're s ===== Callbacks -It's often the case that native code needs to call back into Codename One code when an event occurs. This may be connected directly to an API method call (for example: as the result of an asynchronous method invocation), or due to something initiated by the operating system or the native SDK on its own (for example: a push notification, a location event, etc..). +It's often the case that native code needs to call back into Codename One code when an event occurs. This may be connected directly to an API method call (for example: as the result of an asynchronous method invocation), or due to something initiated by the operating system or the native SDK on its own (for example: a push notification, a location event, etc.). Native code will have access to both the Codename One API and any native APIs in your app, but on some platforms, accessing the Codename One API may be a little tricky. For example: on iOS you'll be calling from Objective-C back into Java which requires knowledge of Codename One's java-to-goal C conversion process. In general, the easiest way to ease callbacks is to provide abstractions that involve static Java methods (in Codename One space) that accept and return primitive types. @@ -2301,7 +2301,7 @@ You've two issues that will need to be solved here: For the first issue, you will use strategy #2 that you mentioned earlier: (Store the parameter on the Codename One side and pass some ID or token that can be used on the native side to retrieve the value). -For the second issue, you will create a static method that can take the token generated to solve the first issue, and call the stored `callback` object's `onResult()` method. You abstract both sides of this process using the https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/freshdesk/MobihelpNativeCallback.java[`MobihelpNativeCallback` class]. +For the second issue, you will create a static method that can take the token generated to solve the first issue, and call the stored `callback` object's `onResult()` method. You abstract both sides of this process using the https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/freshdesk/MobihelpNativeCallback.java[`MobihelpNativeCallback` class]: [source,java] ---- @@ -2353,7 +2353,7 @@ include::../demos/common/src/main/java/com/codenameone/support/mobihelp/Mobihelp ===== The native interface -The final native interface is identical to your public API, except in cases where the public API included non-primitive parameters. +The final native interface is identical to your public API, except in cases where the public API included non-primitive parameters: [source,java] ---- @@ -2364,7 +2364,7 @@ Notice also, that the native interface includes a set of methods with names pref ===== Connecting the public API to the native interface -You've a public API, and you've a native interface. The idea is that the public API should be a thin wrapper around the native interface to smooth out rough edges that are likely to exist due to the strict set of rules involved in native interfaces. You will, so, use delegation inside the `Mobihelp` class to provide it a reference to an instance of `MobihelpNative`: +You've a public API, and you have a native interface. The idea is that the public API should be a thin wrapper around the native interface to smooth out rough edges that are likely to exist due to the strict set of rules involved in native interfaces. You will, so, use delegation inside the `Mobihelp` class to provide it a reference to an instance of `MobihelpNative`: [source,java] ---- @@ -2394,7 +2394,8 @@ include::../demos/common/src/main/java/com/codenameone/support/mobihelp/Mobihelp For some other methods, the public API needs to break apart the parameters into a form that the native interface can accept. For example: the `init()` method, shown above, takes a `MobihelpConfig` object as a parameter, but it passed the properties of the `config` object individually into the native interface. -Another example, is the `showSupport(ArrayList tags)` method. The corresponding native interface method that's wraps is `showSupport(String tags, `String` separator)` - i.e it needs to merge all tags into a single delimited string, and pass then to the native interface along with the delimiter used. The implementation is: +// vale-skip: Microsoft.Foreign: "i.e.," is the precise abbreviation here; "that is" would read awkwardly inside the parenthetical clause. LanguageTool requires the comma after "i.e.". +Another example, is the `showSupport(ArrayList tags)` method. The corresponding native interface method that's wraps is `showSupport(String tags, `String` separator)` - i.e., it needs to merge all tags into a single delimited string, and pass then to the native interface along with the delimiter used. The implementation is: [source,java] ---- @@ -2492,7 +2493,7 @@ appcompat-v7-19.1.0.aar.sha1 appcompat-v7-19.1.0.pom.sha1 There are two files of interest here: -. appcompat-v7-19.1.0.aar - This is the actual library that you need to include in your project to meet the Mobisdk dependency. +. appcompat-v7-19.1.0.aar - This is the actual library that you need to include in your project to meet the MobiSDK dependency. . appcompat-v7-19.1.0.pom - This is the Maven XML file for the library. It will show you any dependencies that the appcompat library has. You will also need to include these dependencies: + ---- @@ -2652,7 +2653,7 @@ You will need to add the `` tags and all the contents of the `< For the release build, you will also need to inject some proguard configuration so that important classes don't get stripped out at build time. The FreshDesk SDK instructions state: -> If you use Proguard, please make sure you've the following included in your project's proguard-project.txt +> If you use Proguard, please make sure you have the following included in your project's proguard-project.txt > > ---- > -keep class android.support.v4.** { *; } @@ -2742,12 +2743,12 @@ include::../demos/ios/src/main/objectivec/com_codename1_freshdesk_MobihelpNative ==== Bundling native iOS SDK -Now that you've implemented your iOS native interface, you need to bundle the Mobihelp iOS SDK into your project. a few different scenarios you may face when looking to include a native SDK: +Now that you've implemented your iOS native interface, you need to bundle the Mobihelp iOS SDK into your project. A few different scenarios you may face when looking to include a native SDK: . The SDK includes `.bundle` resource files. In this case, copy the `.bundle` file(s) into your `native/ios` directory. . The SDK includes `.h` header files. In this case, copy the `.h` file(s) into your `native/ios` directory. . The SDK includes `.a` files. In this case, copy the `.a` file(s) into your `native/ios` directory. -. The SDK includes `.framework` files. In this case, you'll need to zip up the framework, and copy it into your `native/ios` directory. For example: If the framework is named, MyFramework.framework, then the zip file should be named MyFramework.framework.zip, and should be located at `native/ios/MyFramework.framework.zip`. +. The SDK includes `.framework` files. In this case, you'll need to zip up the framework, and copy it into your `native/ios` directory. For example: If the framework is named, `MyFramework.framework`, then the zip file should be named MyFramework.framework.zip, and should be located at `native/ios/MyFramework.framework.zip`. The FreshDesk SDK doesn't include any `.framework` files, so you don't need to worry about that last scenario. You https://s3.amazonaws.com/assets.mobihelp.freshpo.com/sdk/mobihelp_sdk_ios.zip[download the iOS SDK], copy the `libFDMobihelpSDK.a`, `Mobihelp.h`. `MHModel.bundle`, `MHResources.bundle`, and `MHLocalization/en.proj/MHLocalizable.strings` into `native/ios`. @@ -2839,7 +2840,7 @@ The difficulty of porting a particular language to Codename One will vary depend The more similar a language, and its build outputs are to Java, the easier it will be to port (probably). Most JVM languages have two parts: -1. A compiler, which compiles source files to JVM byte-code (usually as.class files). +1. A compiler, which compiles source files to JVM byte-code (usually as `.class` files). 2. A runtime library. One language (other than Java) doesn't require a runtime library: http://www.mirah.org/[Mirah]. @@ -3064,13 +3065,13 @@ Constant pool: #7 = Utf8 Hello Kotlin #8 = String #7 // Hello Kotlin #9 = Utf8 com/codename1/ui/layouts/BoxLayout - ... etc... + ... etc. ---- The constant pool will consist of class names, and strings. You'll want to peruse this list to see if the compiler has added any classes that aren't in the source code. In the example above, it looks like Kotlin is pretty faithful to the original source's dependencies. It didn't inject any classes that aren't in the original source. -Even if the compiler does inject other dependencies into the bytecode, it might not be a problem. it's a problem if those classes aren't supported by Codename One. Watch for anything in the `java.lang.reflect` package or unsolicited use of `java.net`, `java.nio`, or any other package that aren't part of the Codename One standard library. If you're not sure if a class or package is available in the Codename One standard library, check https://www.codenameone.com/javadoc/[the javadocs]. +Even if the compiler does inject other dependencies into the bytecode, it might not be a problem. It's a problem if those classes aren't supported by Codename One. Watch for anything in the `java.lang.reflect` package or unsolicited use of `java.net`, `java.nio`, or any other package that aren't part of the Codename One standard library. If you're not sure if a class or package is available in the Codename One standard library, check https://www.codenameone.com/javadoc/[the javadocs]. **The ByteCode Instructions**: @@ -3091,7 +3092,7 @@ public com.codename1.hellokotlin2.KotlinForm(); 12: new #21 // class com/codename1/ui/Label 15: dup 16: ldc #8 // String Hello Kotlin - etc... + etc. ---- 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. @@ -3108,7 +3109,7 @@ If you find that the compiler does use invokedynamic or add references to classe ====== Assessing the runtime library -The process for assessing the runtime library is pretty like the process for the bytecodes. You'll want to get your hands on the language's runtime library, and use `javap` to inspect the.class files. You're looking for the same things as you were looking for in the compiler's output: "invokedynamic" and classes that aren't supported in Codename One. +The process for assessing the runtime library is pretty like the process for the bytecodes. You'll want to get your hands on the language's runtime library, and use `javap` to inspect the `.class` files. You're looking for the same things as you were looking for in the compiler's output: "invokedynamic" and classes that aren't supported in Codename One. ===== Step 2: Convert the runtime library into a CN1Lib @@ -3116,13 +3117,13 @@ Once you've assessed the language and are optimistic that it's a good candidate If you can't find the sources of the runtime library or they don't seem to be "buildable," then the next best thing is to get the binary distribution's jar file and convert it to a cn1lib. This is what you did for the https://github.com/shannah/codenameone-kotlin[Kotlin runtime library]. -This procedure exploits the fact that a cn1lib file is a zip file with a specific file structure inside it. The cross-platform Java.class files are all contained inside a file named `main.zip`, inside the zip file. This is the *mandatory* file that must be inside a cn1lib. +This procedure exploits the fact that a cn1lib file is a zip file with a specific file structure inside it. The cross-platform Java `.class` files are all contained inside a file named `main.zip`, inside the zip file. This is the *mandatory* file that must be inside a cn1lib. -To make the library easier to use the cn1lib file can also contain a file named "stubs.zip" which includes stubs of the Java sources. When you build a cn1lib using a Maven cn1lib project, it will automatically generate stubs of the source so that the IDE will have access to nice things like Javadoc when using the library. The kotlin distribution includes a separate jar file with the runtime sources, named `kotlin-runtime-sources.jar`, so you used this as the "stubs." It contains full sources, which isn't necessary, but it also doesn't hurt. +To make the library easier to use the cn1lib file can also contain a file named "stubs.zip" which includes stubs of the Java sources. When you build a cn1lib using a Maven cn1lib project, it will automatically generate stubs of the source so that the IDE will have access to nice things like Javadoc when using the library. The Kotlin distribution includes a separate jar file with the runtime sources, named `kotlin-runtime-sources.jar`, so you used this as the "stubs." It contains full sources, which isn't necessary, but it also doesn't hurt. Now that you had your two jar files: kotlin-runtime.jar and kotlin-runtime-sources.jar, create a new empty directory and copy them inside. Rename the jars "main.zip" and "stubs.zip" respectively. Then zip up the directory and rename the zip file `kotlin-runtime.cn1lib`. -IMPORTANT: Building cn1libs manually in this way is a ** bad habit, as it bypasses the API verification step that occurs when building a library project. it's possible, even likely, that the jar files that you convert depend on classes that aren't in the Codename One library, so your library will fail at runtime in unexpected ways. The reason you could do this with kotlin's runtime (with some confidence) is because the bytecodes were already analyzed to ensure that they didn't include anything problematic. +IMPORTANT: Building cn1libs manually in this way is a ** bad habit, as it bypasses the API verification step that occurs when building a library project. It's possible, even likely, that the jar files that you convert depend on classes that aren't in the Codename One library, so your library will fail at runtime in unexpected ways. The reason you could do this with kotlin's runtime (with some confidence) is because the bytecodes were already analyzed to ensure that they didn't include anything problematic. ===== Step 3: Hello world @@ -3161,7 +3162,7 @@ If you run this in the Simulator, it should print "Hello from Kotlin" in the out ===== Step 4: A more complex hello world -For Kotlin, the hello world example app would actually run without the runtime library because it was so simple. it was necessary to add a more complex example to prove the need for the runtime library. It doesn't matter what you do with your more complex example, as long as it doesn't require classes that aren't in Codename One. +For Kotlin, the hello world example app would actually run without the runtime library because it was so simple. It was necessary to add a more complex example to prove the need for the runtime library. It doesn't matter what you do with your more complex example, as long as it doesn't require classes that aren't in Codename One. If you want to use the Codename One inside your project, you should add the CodenameOne.jar (found inside any Codename One project) to your classpath so that it will compile. @@ -3170,14 +3171,14 @@ If you want to use the Codename One inside your project, you should add the Code At this point you already have a manual process for incorporating files built with your alternate language into a Codename One project. The process looks like: 1. Use standard tools for your JVM language to write your code. -2. Use the JVM language's standard build tools (for example: command-line compiler, etc..) to compile your code so that you've.class files (and optionally a.jar file). -3. Wrap your.class files in a cn1lib by dropping them into a Maven cn1lib project generated from the `cn1lib-archetype` (see <>). +2. Use the JVM language's standard build tools (for example: command-line compiler, etc.) to compile your code so that you've`.class` files (and optionally a `.jar` file). +3. Wrap your `.class` files in a cn1lib by dropping them into a Maven cn1lib project generated from the `cn1lib-archetype` (see <>). 4. Install the cn1lib locally with `mvn install` and add it as a `pom`-typed dependency to your Codename One application's `common/pom.xml`. 5. Use your library from the Codename One project. -When Steve first developed Mirah support he automated this process using an https://github.com/shannah/CN1MirahNBM/blob/master/src/ca/weblite/codename1/mirah/build.xml[ANT script]. He also automatically generated some bootstrap code so that he could develop the whole app in Mirah and he woudn't have to write any Java. For example, this level of integration has limitations. +When Steve first developed Mirah support he automated this process using an https://github.com/shannah/CN1MirahNBM/blob/master/src/ca/weblite/codename1/mirah/build.xml[ANT script]. He also automatically generated some bootstrap code so that he could develop the whole app in Mirah and he wouldn't have to write any Java. For example, this level of integration has limitations. -For example, with this approach alone, you couldn't have two-way dependencies between Java source and Mirah source. Yes, Mirah code could use Java libraries (and it did depend on CodenameOne.jar), and your Java code could use your Mirah code. For example, Mirah *source* code couldn't depend on the Java *source* code in your project. This has to do with the order in which code is compiled. it's a bit of a chicken and egg issue. If you're building a project that has Java source code and Mirah source code, you're using two different compilers: mirahc to compile the Mirah files, and javac to compile the Java files. If you're starting from a clean build, and you run mirahc first, then the.Java files haven't yet been compiled to.class files - and thus mirahc can't *reference* them - and any mirah code that depends on those uncompiled Java classes will fail. If you compile the.Java files first, then you've the opposite problem. +For example, with this approach alone, you couldn't have two-way dependencies between Java source and Mirah source. Yes, Mirah code could use Java libraries (and it did depend on CodenameOne.jar), and your Java code could use your Mirah code. For example, Mirah *source* code couldn't depend on the Java *source* code in your project. This has to do with the order in which code is compiled. It's a bit of a chicken-and-egg issue. If you're building a project that has Java source code and Mirah source code, you're using two different compilers: mirahc to compile the Mirah files, and javac to compile the Java files. If you're starting from a clean build, and you run mirahc first, then the `.java` files haven't yet been compiled to `.class` files - and thus mirahc can't *reference* them - and any mirah code that depends on those uncompiled Java classes will fail. If you compile the `.java` files first, then you have the opposite problem. Steve worked around this problem in Mirah by writing https://github.com/shannah/mirah-ant/blob/master/src/ca/weblite/asm/JavaExtendedStubCompiler.java[your own pseudo-compiler] that produced stub class files for the java source that would be referenced by mirahc when compiling the Mirah files. In this way he was able to have two-way dependencies between Java and Mirah in the same project. @@ -3192,7 +3193,7 @@ For both the Kotlin and Mirah support, you wanted integration to be seamless. Yo ---- -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. +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. For Mirah, Steve developed his own https://github.com/shannah/mirah-ant[ANT plugins] and https://github.com/shannah/mirah-nbm[Netbeans module] that do something similar in Netbeans. @@ -3202,7 +3203,7 @@ When Codename One launched in 2012 there was a need to ship updates and fixes fa The https://github.com/codenameone/UpdateCodenameOne[Update Framework] solves many problems in the old systems: -- Download once - if you've many projects the library will download once to the `.codenameone` directory. All the projects will update from local storage +- Download once - if you have many projects the library will download once to the `.codenameone` directory. All the projects will update from local storage - Skins update automatically - this is hugely important. When you change a theme you need to update it in the skins and if you don't update the skin you might see a difference between the simulator and the device @@ -3214,7 +3215,8 @@ You can also use the usual method of #Codename One Settings# -> #Basic# -> #Upda ==== How does it work -You can see the full code https://github.com/codenameone/UpdateCodenameOne[here] the gist of it's simple. You create a jar called `UpdateCodenameOne.jar` under `~/.codenameone` (`~` represents the users home directory). +// vale-skip: Microsoft.Contractions: "of it is simple" parses as the prepositional phrase "of it" plus the predicate "is simple"; collapsing it to "of it's" would read as a possessive ("of its simple") and lose the meaning. +You can see the full code https://github.com/codenameone/UpdateCodenameOne[here] the gist of it is simple. You create a jar called `UpdateCodenameOne.jar` under `~/.codenameone` (`~` represents the users home directory). An update happens by running this tool with a path to a Codename One project for example: @@ -3241,7 +3243,7 @@ Updating the file: /Users/shai/dev/AccordionDemo/lib/CodenameOne.jar Updated project files ---- -Notice that no download happened since the files were up to date. You can also force a check against the server by adding the force argument as such: +Notice that no download happened since the files were up-to-date. You can also force a check against the server by adding the force argument as such: [source,bash] ---- @@ -3252,7 +3254,7 @@ The way this works under the hood is thought a `Versions.properties` within your TIP: Exclude `Versions.properties` from Git -Under the `~/.codenameone` directory you've a more detailed `UpdateStatus.properties` file that includes versions of the locally downloaded files. Notice you can delete this file and it will be recreated as all the jars get downloaded over again. +Under the `~/.codenameone` directory you have a more detailed `UpdateStatus.properties` file that includes versions of the locally downloaded files. Notice you can delete this file and it will be recreated as all the jars get downloaded over again. ==== What isn't covered diff --git a/docs/developer-guide/Animations.asciidoc b/docs/developer-guide/Animations.asciidoc index 7fcf8d47b5..44065c4069 100644 --- a/docs/developer-guide/Animations.asciidoc +++ b/docs/developer-guide/Animations.asciidoc @@ -31,7 +31,7 @@ If you add components to a form that's showing, it's your responsibility to invo `animateLayout()` method is a fancy form of revalidate that animates the components into their laid out position. After changing the layout & invoking this method the components move to their new sizes/positions seamlessly. `Form` exposes convenience wrappers such as `animateLayout*()` that forward to the underlying content pane, so you can call the methods directly on the form unless you specifically need to animate a nested container. -This sort of behavior creates a special case where setting the size/position makes sense. When you set the size/position in the demo code here you're positioning the components at the animation start position above the frame. +This sort of behavior creates a special case where setting the size/position makes sense. When you set the size/position in the demo code here you're positioning the components at the animation start position above the frame: [source,java] ---- @@ -114,7 +114,7 @@ The `AndWait` variant blocks the calling thread until the animation completes. T include::../demos/common/src/main/java/com/codenameone/developerguide/animations/AnimationSynchronicityDemo.java[tag=animationSequence,indent=0] ---- -First the UI goes thru an "unlayout" animation, once that completes the layout itself is performed. +First the UI goes through an "unlayout" animation, once that completes the layout itself is performed. // HTML_ONLY_START IMPORTANT: The `AndWait` calls needs to be invoked on the Event Dispatch Thread despite being "blocking." This is a common convention in Codename One powered by a unique capability of Codename One: `invokeAndBlock`. + @@ -141,7 +141,7 @@ There are several more variations on the standard animate methods. Several metho The value for the fade argument is a number between 0 and 255 where 0 represents full transparency and 255 represents full opacity. -Some animate layout methods are hierarchy based. They work like the regular `animateLayout` methods but recurse into the entire https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container] hierarchy. These methods work well when you've components in a nested hierarchy that need to animate into place. This is demonstrated in the opening sequence of the kitchen sink demo: +Some animate layout methods are hierarchy based. They work like the regular `animateLayout` methods but recurse into the entire https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container] hierarchy. These methods work well when you have components in a nested hierarchy that need to animate into place. This is demonstrated in the opening sequence of the kitchen sink demo: [source,java] ---- @@ -154,7 +154,7 @@ WARNING: Avoid the hierarchy version where possible. Its slower but more importa ==== Sequencing animations via AnimationManager -All the animations go thru a per-form queue: the https://www.codenameone.com/javadoc/com/codename1/ui/AnimationManager.html[AnimationManager]. +All the animations go through a per-form queue: the https://www.codenameone.com/javadoc/com/codename1/ui/AnimationManager.html[AnimationManager]. This effectively prevents two animations from mutating the UI in parallel so you won't have collisions between two conflicting sides. Things get more interesting when you try to do something like this: [source,java] @@ -198,7 +198,7 @@ Every component in Codename One contains an `animate()` method that returns a bo If the `animate` method returns true then the animation will be painted (the `paint` method of the `Animation` interface would be invoked). -TIP: it's important to deregister animations when they aren’t needed to conserve battery life. +TIP: It's important to deregister animations when they aren’t needed to conserve battery life. If you derive from a component, which has its own animation logic you might damage its animation behavior by deregistering it, so use care with the low-level API’s. @@ -216,7 +216,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/animations ==== Why not just write code in paint -Animations are comprised of two parts, the logic (deciding the position etc) and the painting. The paint method should be dedicated to painting, not to the actual moving of the components. +Animations comprise two parts, the logic (deciding the position, etc.) and the painting. The paint method should be dedicated to painting, not to the actual moving of the components. The separation of concerns allows you to avoid redundant painting for example, if animate didn't trigger a change return `false` to avoid the overhead related to animations. @@ -243,21 +243,21 @@ When defining a transition you define the entering transition and the exiting tr For most cases the method `setFormTransitonIn` should go unused. That API exists for some elaborate custom transitions that might need to have a special effect both when transitioning in and out of a specific form. But, most of these effects are easier to achieve with layout animations (for example, components dropping into place etc.). -For `Dialog` the transition in shows its appearance and the transition out shows its disposal. in that case both transitions make a lot of sense. +For `Dialog` the transition in shows its appearance and the transition out shows its disposal. In that case both transitions make a lot of sense. **** .Back/Forward Transitions **** Transitions have a direction and can all be played either in incoming or outgoing direction. A transition can be flipped (played in reverse) when use an RTL language footnote:[Right to left/bidi language such as Hebrew or Arabic] or when you traverse backwards in the form navigation hierarchy. -Normally `Form.show()` displays the next `Form` with an incoming transition based on the current RTL mode. If use `Form.showBack()` it will play the transition in reverse. +Normally `Form.show()` displays the next `Form` with an incoming transition based on the current RTL mode. If you use `Form.showBack()` it will play the transition in reverse. **** TIP: When working with high-level animations you can select #Slow Motion# option in the simulator to slow down animations and inspect their details Themes define the default transitions used when showing a form, these differ based on the OS. In most platforms the default is `Slide` whereas in iOS the default is `SlideFade` which slides the content pane and title while fading in/out the content of the title area. -TIP: `SlideFade` is problematic without a title area. If you've a `Form` that lacks a title area you would recommend to disable `SlideFade` at least for that `Form`. +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`. // HTML_ONLY_START Check out the full set of theme constants in the https://www.codenameone.com/manual/advanced-theming.html#theme-constants-section[Theme Constants Section]. @@ -276,7 +276,7 @@ To apply a transition to a component you can use the `Container.replace()` metho include::../demos/common/src/main/java/com/codenameone/developerguide/animations/ReplaceTransitionDemo.java[tag=replaceTransition,indent=0] ---- -TIP: Replace even works when you've a layout constraint in place for example, replacing a component in a border layout will do the "right thing." But, some layouts such as `TableLayout` might be tricky in such cases so recommend wrapping a replaceable `Component` in a border layout and replacing the content. +TIP: Replace even works when you have a layout constraint in place for example, replacing a component in a border layout will do the "right thing." But, some layouts such as `TableLayout` might be tricky in such cases so recommend wrapping a replaceable `Component` in a border layout and replacing the content. `Container.replace()` can also be used with a null transition at which point it replaces instantly with no transition. @@ -285,7 +285,7 @@ TIP: Replace even works when you've a layout constraint in place for example, re The slide transitions are used to move the `Form/Component` in a sliding motion to the side or up/down. Four basic types of slide transitions exist: . Slide - the most commonly used transition -. Fast Slide - historically this provided better performance for old device types. it's no longer recommended for newer devices +. Fast Slide - historically this provided better performance for old device types. It's no longer recommended for newer devices . Slide Fade - the iOS default where the title area features a fade transition . Cover/Uncover - a kind of slide transition where the source or destination form slides while the other remains static in place @@ -306,7 +306,7 @@ image::img/transition-slide-vertical.jpg[The slide transition can be applied ver .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%] -TIP: `SlideFade` is problematic without a title area. If you've a `Form` that lacks a title area you would recommend to disable `SlideFade` at least for that `Form`. +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%] @@ -361,13 +361,13 @@ once. .Morph Transition image::img/mighty-morphing-components-1.png[Morph Transition,scaledwidth=15%] -To support this behavior you've the https://www.codenameone.com/javadoc/com/codename1/ui/animations/MorphTransition.html[MorphTransition] class that provides this same effect coupled with a fade to +To support this behavior you have the https://www.codenameone.com/javadoc/com/codename1/ui/animations/MorphTransition.html[MorphTransition] class that provides this same effect coupled with a fade to the rest of the UI (see <>). Since the transition is created before the form exists you can't reference explicit components within the form when creating the morph transition (to show which component becomes which) so you need to refer to them by name. This means you need to use `setName(String)` on the components in the source/destination -forms so the transition will be able to find them. +forms so the transition will be able to find them: [source,java] ---- @@ -376,7 +376,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/animations ==== SwipeBackSupport -iOS7+ allows swiping back one form to the previous form, Codenmae One has an API to enable back swipe transition: +iOS7+ allows swiping back one form to the previous form, Codename One has an API to enable back swipe transition: [source,java] ---- @@ -389,7 +389,7 @@ That one command will enable swiping back from `currentForm`. https://www.codena [source,java] ---- /** - * Useful when passing a value that might not exist to a function, e.g. when we + * 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 { diff --git a/docs/developer-guide/Casual-Game-Programming.asciidoc b/docs/developer-guide/Casual-Game-Programming.asciidoc index 4867282cc4..e3ff8f3097 100644 --- a/docs/developer-guide/Casual-Game-Programming.asciidoc +++ b/docs/developer-guide/Casual-Game-Programming.asciidoc @@ -4,7 +4,7 @@ Casual games are often the most influential games of all, they cross demographic Yes, framerates are important but ubiquity, social connectivity & gameplay are even more important for this sub genre of the game industry. The mobile aspect highlights this point further, the way app stores are built releasing often puts your game at an advantage over its competitor’s. Yet releasing to all platforms and all screen sizes becomes an issue soon enough. -Typically a game is comprised of a game loop which updates UI status based on game time and renders the UI. But, with casual games constantly rendering is redundant and with mobile games it could put a major drain on the battery life. Instead you will use components to build the game elements and let Codename One do the rendering for you. +Typically a game comprises a game loop which updates UI status based on game time and renders the UI. But, with casual games constantly rendering is redundant and with mobile games it could put a major drain on the battery life. Instead you will use components to build the game elements and let Codename One do the rendering for you. === The game @@ -20,7 +20,7 @@ In mobile device programming every pixel is crucial because of the small size of Solutions exist, such as using multiple images for every density (DPI). But, this is tedious for developers who need to scale the image and copy it every time for every resolution. Codename One has a feature called `MultiImage` which implicitly scales the images to all the resolutions on the desktop and places them within the res file, in runtime you will get the image that matches your devices density. -A catch exists though... `MultiImage` is designed for applications where you want the density to determine the size. an iPad will have the same density as an iPhone since both share the same amount of pixels per inch. This makes sense for an app since the images will be big enough to touch and clear. Furthermore, since the iPad screen is larger more data will fit on the screen! +A catch exists though... `MultiImage` is designed for applications where you want the density to determine the size. An iPad will have the same density as an iPhone since both share the same amount of pixels per inch. This makes sense for an app since the images will be big enough to touch and clear. Furthermore, since the iPad screen is larger more data will fit on the screen! Game developers have a different constraint when it comes to game elements. For a game you want the images to match the device resolution and take up as much screen real estate as possible, otherwise your game would be constrained to a small portion of the tablet and look small. A solution exists though: you can determine your own DPI level when loading resources and effectively force a DPI based on screen resolution when working with game images! @@ -73,14 +73,13 @@ This animation is easy to do although it does have several stages. In the first Notice that here you use `animateLayoutAndWait`, which effectively blocks the calling thread until the animation is completed. This is a important and tricky subject! -Codename One is a single threaded API, it supports working on other threads but it's your responsibility to invoke everything on the EDT (Event Dispatch Thread). Since the EDT does the entire rendering, events etc. if you block it you will effectively stop Codename One in its place! But, a trick exists: invokeAndBlock is a feature that allows you to stop the EDT and do stuff then restore the EDT without "" stopping it. Its tricky and out of scope for this article (this subject deserves an article of its own) but the gist of it's that you can’t invoke Thread.sleep() in a Codename One application (at least not on the EDT) but you can use clever methods such as `Dialog.show()`, `animateLayoutAndWait` etc. and they will block the EDT for you. This is convenient since you can write code serially without requiring event handling for every single feature. +Codename One is a single threaded API, it supports working on other threads but it's your responsibility to invoke everything on the EDT (Event Dispatch Thread). Since the EDT does the entire rendering, events etc. If you block it you will effectively stop Codename One in its place! But, a trick exists: invokeAndBlock is a feature that allows you to stop the EDT and do stuff then restore the EDT without "" stopping it. Its tricky and out of scope for this article (this subject deserves an article of its own) but the gist of it's that you can’t invoke Thread.sleep() in a Codename One application (at least not on the EDT) but you can use clever methods such as `Dialog.show()`, `animateLayoutAndWait` etc. and they will block the EDT for you. This is convenient since you can write code serially without requiring event handling for every single feature. 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. - +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] ---- @@ -151,7 +150,7 @@ public class Poker { } /** - * The splash screen is relatively bare bones. Its important to have a splash screen for iOS + * 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() { @@ -238,7 +237,7 @@ public class Poker { gameForm.addComponent(gameFormBorderLayout); gameForm.addComponent(gameUpperLayer); - // The game itself is comprised of 3 containers, one for each player containing a grid of 5 cards (grid layout + // 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)); diff --git a/docs/developer-guide/Events.asciidoc b/docs/developer-guide/Events.asciidoc index 1cbf1161f1..e83d498f22 100644 --- a/docs/developer-guide/Events.asciidoc +++ b/docs/developer-guide/Events.asciidoc @@ -106,7 +106,9 @@ Notice that you can check if an event was already consumed using the `isConsumed ===== NetworkEvent -https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html[NetworkEvent] is a subclass of `ActionEvent` that's passed to `actionPerformed` callbacks made in relation to generic network code. For example, [source,java] +https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html[NetworkEvent] is a subclass of `ActionEvent` that's passed to `actionPerformed` callbacks made in relation to generic network code. For example: + +[source,java] ---- NetworkManager.getInstance().addErrorListener(new ActionListener() { public void actionPerformed(NetworkEvent ev) { @@ -124,7 +126,7 @@ NetworkManager.getInstance().addErrorListener((ev) -> { }); ---- -The `NetworkEvent` allows the networking code to reuse the `EventDispatcher` infrastructure and to simplify event firing thru the EDT. But you should notice that some code might not be equivalent for example, you could do this to read the input stream: +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] ---- @@ -233,7 +235,7 @@ 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 a important event for general user code. it's recommended that developers don't bind a style listener. +for the component class but not an important event for general user code. It's recommended that developers don't bind a style listener. ==== Component state change events @@ -289,7 +291,7 @@ Each of those has advantages and disadvantages, specifically: * 'Form' based events and callbacks deliver pointer events in the 'Form' coordinate space. * 'Component' based events require focus -* 'Form' based events can block existing functionality from proceeding thru the event chain for example, you can avoid calling super in a form event and thus block other events from happening (for example, block a listener or component event from triggering). +* 'Form' based events can block existing functionality from proceeding through the event chain for example, you can avoid calling super in a form event and thus block other events from happening (for example, block a listener or component event from triggering). .Event type map [cols="4*"] @@ -342,7 +344,7 @@ Drag lifecycles also expose a completion hook. When `Component.addDragFinishedLi Drag events are quite difficult to handle across devices. Some devices send a ridiculous number of events for even the lightest touch while others send too little. It seems like too many drag events wouldn't be a problem, but if you drag over a button then it might disable the buttons action event (since this might be the user trying to scroll). -Drag sensitivity is about the component being dragged which is why you've the method `getDragRegionStatus` that allows you to "hint" to the drag API whether you're interested in drag events or not and if so in which directional bias. +Drag sensitivity is about the component being dragged which is why you have the method `getDragRegionStatus` that allows you to "hint" to the drag API whether you're interested in drag events or not and if so in which directional bias. For example, if your component is a painting app where you're trying to draw using drag gestures you would use code such as: @@ -363,7 +365,7 @@ 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. +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.: diff --git a/docs/developer-guide/Index.asciidoc b/docs/developer-guide/Index.asciidoc index 8deb915504..8f5133caaa 100644 --- a/docs/developer-guide/Index.asciidoc +++ b/docs/developer-guide/Index.asciidoc @@ -62,7 +62,7 @@ Since Android is already based on Java, Codename One is already native to Androi On iOS, Codename One built and open-sourced ParparVM, which is a conservative VM. ParparVM features a concurrent, non-blocking GC and is written entirely in Java/C. ParparVM is a transpiler that generates C source code matching the given Java bytecode. This means that an Xcode project is generated and compiled on the build servers. It's as if you hand-coded a native app and is thus future-proof against changes that Apple introduces. For example, Apple migrated to 64-bit and later introduced bitcode support to iOS. ParparVM needed no modifications to meet those changes. -NOTE: Codename One translates the bytecode to C, which is faster than Swift/Objective-C. The port code that invokes iOS API's is hand coded in Objective-C +NOTE: Codename One translates the bytecode to C, which is faster than Swift/Objective-C. The port code that invokes iOS APIs is hand coded in Objective-C Codename One earlier offered a UWP (Universal Windows Platform) target based on iKVM. That target was discontinued in release 7.0.229 and is preserved as historical context in older documentation and blog posts. @@ -76,9 +76,9 @@ What makes Codename One stand out is the approach it takes to UI: "`lightweight Lightweight architecture is the "`not so secrete sauce`" to Codename One's portability. Essentially it means all the components/widgets in Codename One are written in Java. Thus their behavior is consistent across all platforms and they're fully customizable from the developer code as they don't rely on OS internal semantics. This allows developers to preview the application accurately in the simulators and GUI builders. -One of the big accomplishments in Codename One is its unique ability to embed "`heavyweight`" widgets into place among the "`lightweights.`" This is crucial for apps such as Uber where the cars and widgets on top are implemented as Codename One components yet below them you've the native map component. +One of the big accomplishments in Codename One is its unique ability to embed "`heavyweight`" widgets into place among the "`lightweights.`" This is crucial for apps such as Uber where the cars and widgets on top are implemented as Codename One components yet below them, you have the native map component. -Codename One achieves fast performance by drawing using the native gaming API's of most platforms for example: OpenGL ES on iOS. The core technologies behind Codename One are all open source including most of the stuff developed by Codename One itself, for example: ParparVM but also the full library, platform ports, designer tool, device skins etc. +Codename One achieves fast performance by drawing using the native gaming APIs of most platforms for example: OpenGL ES on iOS. The core technologies behind Codename One are all open source including most of the stuff developed by Codename One itself, for example: ParparVM but also the full library, platform ports, designer tool, device skins etc. .Lightweight Architecture Origin **** @@ -128,7 +128,7 @@ NOTE: The Desktop port is available to pro grade subscribers of Codename One .LWUIT App Screenshot circa 2007 image::img/lwuit-screenshot.png[LWUIT App Screenshot,scaledwidth=15%] -Codename One was started by Chen Fishbein and Shai Almog who authored the Open Source LWUIT project at Sun Microsystems (circa 2007). The LWUIT project aimed to solve the fragmentation within J2ME/Blackberry devices by creating a higher standard of user interface than the common baseline at the time. LWUIT received critical acclaim and traction within many industries but was limited by the declining feature phone market. It was forked by many companies including Nokia. It was used as the base standard for DTV in Brazil. Another fork has brought a LWUIT into high end cars from Toyota and other companies. This fork later adapted Codename One as well. +Codename One was started by Chen Fishbein and Shai Almog who authored the Open Source LWUIT project at Sun Microsystems (circa 2007). The LWUIT project aimed to solve the fragmentation within J2ME/Blackberry devices by creating a higher standard of user interface than the common baseline at the time. LWUIT received critical acclaim and traction within many industries but was limited by the declining feature phone market. It was forked by many companies including Nokia. It was used as the base standard for DTV in Brazil. Another fork has brought a LWUIT into high-end cars from Toyota and other companies. This fork later adapted Codename One as well. In 2012 Shai and Chen formed Codename One as they left Oracle. The project has taken many of the basic concepts developed within the LWUIT project and adapted them to the smartphone world which is still experiencing similar issues to the device fragmentation of the old J2ME phones. @@ -158,7 +158,7 @@ The exact same image will look different on each device, sometimes to a comical This also highlights the need for working with measurements other than pixels. Codename One supports millimeters (or dips) as a unit of measurement. This is highly convenient and is a better representation of size when dealing with mobile devices. -However, there is a bigger conceptual issue involved. You need to build a UI that adapts to the wide differences in form factors. You might have fewer pixels on an iPad, but because of its physical size, you would expect the app to cram more information into that space so the app won't feel like a blown-up phone application. many strategies to address that, but one of the first steps is in the layout managers. (((Layouts, Layout))) +However, there is a bigger conceptual issue involved. You need to build a UI that adapts to the wide differences in form factors. You might have fewer pixels on an iPad, but because of its physical size, you would expect the app to cram more information into that space so the app won't feel like a blown-up phone application. Many strategies to address that, but one of the first steps is in the layout managers. (((Layouts, Layout))) Chapter 2 discusses the layout managers in depth, but the core concept is that they decide where a UI element is placed based on generic logic. That way, the user interface can adapt automatically to the huge variance in display size and density. @@ -195,11 +195,11 @@ If an app is big, it might not update over a cellular network connection. Google ===== Power drain -Desktop developers think about power usage within their apps. In mobile development, this is a crucial concept. Modern device OS's have tools that highlight misbehaving applications, and this can lead to bad reviews. +Desktop developers think about power usage within their apps. In mobile development, this is a crucial concept. Modern device OSes have tools that highlight misbehaving applications, and this can lead to bad reviews. Code that loops forever while waiting for input will block the CPU from sleeping and drain the battery. -Worse. Mobile OS's kill applications that drain the battery. If the app is draining the battery and is minimized, (for example, during an incoming call) the app could be killed. This will impact app performance and usability. +Worse. Mobile OSes kill applications that drain the battery. If the app is draining the battery and is minimized, (for example, during an incoming call) the app could be killed. This will impact app performance and usability. ==== Sandbox and permissions @@ -266,9 +266,9 @@ Apple, Google and Microsoft identify applications based on their package names. This can cause difficulties when submitting to Apple, Google, or Microsoft. Submitting to one of them is no guarantee of success when submitting to another. -To come up with the right package name use a reverse domain notation. if your website is `goodstuff.co.uk` your package name should start with `uk.co.goodstuff`. Follow these guidelines for package names: +To come up with the right package name use a reverse domain notation. If your website is `goodstuff.co.uk` your package name should start with `uk.co.goodstuff`. Follow these guidelines for package names: -* *Lower Case* - some OS's are case sensitive and handling a mistake in case is painful. The Java convention is lower case; stick to that +* *Lower Case* - some OSes are case-sensitive and handling a mistake in case is painful. The Java convention is lower case; stick to that * *Avoid Dash and Underscore* - You can't use a dash character (`-`) for a package name in Java. Underscore (`_`) doesn't work for iOS. If you want more than one word just use a deeper package e.g.: `com.mydomain.deeper.meaningful.name` @@ -285,20 +285,20 @@ With the `Skins` menu you can download device skins to see how your app will loo TIP: Some skins are bigger than the screen size, uncheck the `Scrollable` flag in the `Simulator` menu to handle them more effectively -Use your IDE's Debug button with the Run in Simulator task to launch the simulator under the debugger. +Use your IDEs Debug button with the Run in Simulator task to launch the simulator under the debugger. .Simulator vs. Emulator **** Codename One ships with a simulator similarly to the iOS toolchain which also has a simulator. Android ships with an emulator. Emulators go further. They create a virtual machine that's compatible with the device CPU and then boot the full mobile OS within that environment. This provides a proper runtime environment but is **slow**. -Simulators rely on the fact that OS's are similar and so they leave the low-level details in place and just map the API behavior. Since Codename One relies on Java it can start simulating on top of the virtual machine on the desktop. That provides several advantages including fast development cycles and full support for all the development tools/debuggers you can use on the desktop. +Simulators rely on the fact that OSes are similar and so they leave the low-level details in place and just map the API behavior. Since Codename One relies on Java it can start simulating on top of the virtual machine on the desktop. That provides several advantages including fast development cycles and full support for all the development tools/debuggers you can use on the desktop. Emulators make sense for developers who want to build OS level services for example, screensavers or low-level services. Standard applications are better served by simulators. **** ==== The source code of the hello world app -After clicking finish in the new project wizard you've a `HelloWorld` project with a few default settings. The following sections break the class down to small pieces and explain each piece starting with the enclosing class: +After clicking finish in the new project wizard you have a `HelloWorld` project with a few default settings. The following sections break the class down to small pieces and explain each piece starting with the enclosing class: [source,java,title='HelloWorld Class'] ---- @@ -316,7 +316,7 @@ public class HelloWorld { // <1> <3> Every app has a theme, it determines how everything within the application looks for example: colors, fonts etc. -Next consider the first lifecycle method `init(Object)`. The <> discusses the lifecycle in depth. +Next consider the first lifecycle method `init(Object)`. The <> discusses the lifecycle in depth: [source,java,title='HelloWorld init(Object)'] ---- @@ -338,7 +338,7 @@ public void init(Object context) { // <1> } ---- -<1> `init` is the first of the four lifecycle methods. it's responsible for initialization of variables and values +<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 @@ -352,9 +352,9 @@ public void init(Object context) { // <1> <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've an exception you can log it with this code +<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've a pro subscription +<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 @@ -419,7 +419,7 @@ Codename One has four standard callback methods in the lifecycle API: IMPORTANT: `destroy()` is optional there is no guarantee that it will be invoked. It should be used only as a last resort **** -Now that you've a general sense of the lifecycle lets look at the last two lifecycle methods: +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()'] ---- @@ -450,7 +450,7 @@ That's it. You should now have a general sense of the code. It's time to run on .Codename One Control Center image::img/control-center-main.png[Codename One Settings/Control Center,scaledwidth=50%] -You can use the Control Center to configure almost anything. Specifically, the application title, application version, application icon etc. are all found in the Codename One Settings maven target. +You can use the Control Center to configure almost anything. Specifically, the application title, application version, application icon etc. Are all found in the Codename One Settings maven target. There are many options within this UI that control almost every aspect of the application from signing to basic settings. @@ -463,7 +463,7 @@ image::img/control-center-builds-empty.png[Builds in Empty State,scaledwidth=50% All the modern mobile platforms require signed applications but they all take radically different approaches when implementing it. -Signing is a process that marks your final application for the device with a special value. This value (signature) is a value that you can generate based on the content of the application and your certificate. Effectively it guarantees the app came from you. This blocks a 3rd party from signing their apps and posing as you to the App Store or to the user. it's a crucial security layer. +Signing is a process that marks your final application for the device with a special value. This value (signature) is a value that you can generate based on the content of the application and your certificate. Effectively it guarantees the app came from you. This blocks a 3rd party from signing their apps and posing as you to the App Store or to the user. It's a crucial security layer. A certificate is the tool you use for signing. Think of it as a mathematical rubber stamp that generates a different value each time. Unlike a rubber stamp a signature can't be forged! @@ -472,7 +472,7 @@ A certificate is the tool you use for signing. Think of it as a mathematical rub .Backup your Android certificate and save its password! WARNING: If you lose your Android certificate you won't be able to update your app -Android uses a self signed certificate approach. You can generate a certificate by describing who you're and picking a password! +Android uses a self-signed certificate approach. You can generate a certificate by describing who you're and picking a password! Anyone can do that. For example, once a certificate is published it can't be replaced... @@ -480,7 +480,7 @@ If this wasn't the case someone else could push an "`upgrade`" to your app. Once With that in mind generating an Android certificate is trivial. -NOTE: The following chart illustrates a process that's identical on all IDE's +NOTE: The following chart illustrates a process that's identical on all IDEs .Process of Certificate Generation for Android image::img/android-certificate-generator.png[] @@ -492,7 +492,7 @@ TIP: Make sure to back that up and the password as losing these can have dire co **** In theory yes. In practice it's a pain... Keeping multiple certificates and managing them is a pain so you often just use one. -The drawback of this approach occurs when you're building an app for someone else or want to sell the app. Giving away your certificate is akin to giving away your house keys. it makes sense to have separate certificates for each app. +The drawback of this approach occurs when you're building an app for someone else or want to sell the app. Giving away your certificate is akin to giving away your house keys. It makes sense to have separate certificates for each app. **** ====== Signing and provisioning iOS @@ -532,17 +532,17 @@ image::img/ios-certificate-wizard-3.png[Using the iOS Certificate Wizard Steps 5 [TIP] ==== -If you've more than one project you should use the same iOS P12 certificate files in all the projects and regenerate the provisioning. In this situation the certificate wizard asks you if you want to revoke the existing certificate which you shouldn't revoke in such a case. You can update the provisioning profile in Apple's iOS developer website. +If you have more than one project you should use the same iOS P12 certificate files in all the projects and regenerate the provisioning. In this situation the certificate wizard asks you if you want to revoke the existing certificate which you shouldn't revoke in such a case. You can update the provisioning profile in Apple's iOS developer website. ==== -One important aspect of provisioning on iOS is the device list in the provisioning step. Apple allows you to install the app on 100 devices during development. This blocks developers from skipping the App Store altogether. it's important you list the correct UDID for the device in the list otherwise install will fail. +One important aspect of provisioning on iOS is the device list in the provisioning step. Apple allows you to install the app on 100 devices during development. This blocks developers from skipping the App Store altogether. It's important you list the correct UDID for the device in the list otherwise install will fail. WARNING: Many apps and tools offer the UDID of the device, but they aren't necessarily reliable and might give a fake number! .Get the UDID of a Device image::img/get-device-udid.png[Get the UDID of a Device] -TIP: You can right click the UDID and select #copy# to copy it +TIP: You can right-click the UDID and select #copy# to copy it The simplest and most reliable process for getting a UDID is through iTunes. Other approaches have worked in the past but this approach is guaranteed. @@ -552,7 +552,7 @@ NOTE: Ad hoc provisioning allows 1000 beta testers for your application but it's Before you continue with the build you should sign up at https://www.codenameone.com/build-server.html where you can soon follow the progress of your builds. You need a Codename One account to build for the device. -Now that you've certificates the process of device builds is a right click away for both OS's. You can right click the project and select #Codename One# -> #Send iOS Debug Build# or #Codename One# -> #Send Android Build#. +Now that you have certificates, the process of device builds is a right click away for both OSes. You can right-click the project and select #Codename One# -> #Send iOS Debug Build# or #Codename One# -> #Send Android Build#. .Right click menu options for sending device builds image::img/getting-started-right-click-menu.png[Right click menu options for sending device builds,scaledwidth=50%] @@ -568,7 +568,7 @@ TIP: On iOS make sure you use Safari when installing, as 3rd party browsers migh Once you go through those steps you should have the #HelloWorld# app running on your device. This process is non-trivial when starting so if you run into difficulties don't despair and seek help at the discussion forum (https://www.codenameone.com/discussion-forum.html) or stack overflow (https://stackoverflow/tags/codenameone/). Once you go through signing and installation, it becomes easier. -TIP: You can also install the application either by emailing the install link to your account (using the #e-mail Link# +TIP: You can also install the application either by emailing the installation link to your account (using the #e-mail Link# button) You can also download the binaries to upload them to the appstores. @@ -578,11 +578,11 @@ You can also download the binaries to upload them to the appstores. Codename One started before Kotlin became public. Kotlin has since shown itself as an interesting option for developers within the Android community. With that in mind, Codename One added support for Kotlin. -To use Kotlin with Codename One you can create a kotlin directory next to the java directory under the `common/src/main` directory. Kotlin code that resides there can work as usual and interact with the Java code. +To use Kotlin with Codename One you can create a `kotlin` directory next to the `java` directory under the `common/src/main` directory. Kotlin code that resides there can work as usual and interact with the Java code. Please notice the following: -- don't use the project conversion tools or accept the warning that the project isn't a Kotlin project. You do your own build process +- Don't use the project conversion tools or accept the warning that the project isn't a Kotlin project. You do your own build process - Warnings and errors aren't listed and builds that claim to have errors might pass @@ -664,7 +664,7 @@ class MyApplication { } ---- -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. +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: @@ -677,7 +677,7 @@ This is essential as the build server will fail with weird errors related to ins NOTE: This applies to the main class of the project, other classes in Codename One can remain `final` -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 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: diff --git a/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc b/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc index d708e523bb..de0178fc1b 100644 --- a/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc +++ b/docs/developer-guide/Maven-Appendix-Rich-Properties.adoc @@ -4,7 +4,7 @@ The rich properties file (rpf) format is used to store configuration for the `generate-app-project` goal. The format is the same as a regular properties file except that it can accommodate properties whose values are "rich" and lengthy. -"Rich" properties use the following syntax. +"Rich" properties use the following syntax: [source,rpf] ---- diff --git a/docs/developer-guide/Maven-Creating-CN1Libs.adoc b/docs/developer-guide/Maven-Creating-CN1Libs.adoc index 5112cd84a0..cbd49adef6 100644 --- a/docs/developer-guide/Maven-Creating-CN1Libs.adoc +++ b/docs/developer-guide/Maven-Creating-CN1Libs.adoc @@ -7,10 +7,10 @@ A cn1lib may contain any of the following: . Cross-platform Java classes. . Native code that targets specific platforms. -. Build hints, which will affect how projects will be built that include this library. These can contain things like Gradle dependencies on Android, cocopods dependencies on iOS, and other hints to affect the build-server process. +. Build hints, which will affect how projects will be built that include this library. These can contain things like Gradle dependencies on Android, Cocoapods dependencies on iOS, and other hints to affect the build-server process. . CSS files. -..cn1lib vs.jar +.`.cn1lib` vs `.jar` [sidebar] **** You may be wondering why the .cn1lib format is even necessary. Why not just distribute libraries as .jar files? The .cn1lib format offers several advantages over the plain .jar format: @@ -44,7 +44,7 @@ mvn archetype:generate \ -DinteractiveMode=false ---- -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 modiy 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) +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) In the above snippet you would change the `groupId`, `artifactId`, and `version` properties to reflect your project settings. @@ -65,7 +65,7 @@ mvn archetype:generate -DarchetypeGroupId=com.codenameone \ -DarchetypeArtifactId=cn1lib-archetype ---- -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 modiy 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) +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) And follow the prompts. This will, result in fewer prompts because you've already specified the archetype to use. ==== @@ -130,7 +130,7 @@ version, and package. The "Package" is unimportant here as it isn't used anywher + Once you've entered the information to your liking press the "Finish" button. -This will create a new libary project for you at the location you specified. +This will create a new library project for you at the location you specified. ==== Eclipse IDE @@ -152,7 +152,7 @@ Select that option, and press _Next_ + image:img/eclipse-new-maven-project-details.png[] -This will create a new libary project for you at the location you specified. +This will create a new library project for you at the location you specified. ==== Project structure @@ -167,7 +167,7 @@ The module where you can implement native interfaces for the iOS platform. This android:: The module where you can implement native interfaces for the Android platform. This module is in the "android" directory of the main project. JavaScript:: -The module where you can implement native interfaces for the Javascript platform. This module is in the "JavaScript" directory of the main project. +The module where you can implement native interfaces for the JavaScript platform. This module is in the "JavaScript" directory of the main project. lib:: The library module which includes all the other modules as dependencies, and can be used as a pom dependency in Codename One application projects that wish to use this library. This module is in the "lib" directory of the main project. tests:: @@ -194,7 +194,7 @@ image::img/netbeans-myfirstlibrary-project-inspector.png[] This top-level view of the modules doesn't provide a clear view of the project landscape, but, since 99% of your development will occur inside the `common` submodule. Open that "common" sub-module project as well and take a peek. -Right click on the "Common" sub-module, and select "Open Project" as shown below: +Right-click on the "Common" sub-module, and select "Open Project" as shown below: image::img/netbeans-myfirstlibrary-open-common-submodule.png[] @@ -321,13 +321,13 @@ The maven configuration file of the root module is where you will set project-wi When/if you decide to deploy your module to Maven central, you'll need to add more deployment-related settings in this file. common/pom.xml:: -The maven configuration file for the "common" module, which will contain most of your cn1lib's soure code, CSS files, and properties files. If your library depends on other libraries or jar files, you'll be adding them as dependencies in this file, and not the root pom.xml file. +The maven configuration file for the "common" module, which will contain most of your cn1lib's source code, CSS files, and properties files. If your library depends on other libraries or jar files, you'll be adding them as dependencies in this file, and not the root pom.xml file. common/codenameone_library_appended.properties:: This file is where you can specify properties that should be merged with the codenameone_settings.properties of application projects that include this library as a dependency. This is where you would add, for example, Gradle dependencies required for the Android builds, or CocoaPods dependencies that are required for iOS builds. common/codenameone_library_required.properties:: -This file allows you to specific build hints that *must* be present in application projects that include this library. If this libary requires a particular android build tools version, or a specific Java version, then those requirements should be specified in this file. +This file allows you to specific build hints that *must* be present in application projects that include this library. If this library requires a particular android build tools version, or a specific Java version, then those requirements should be specified in this file. ===== Important directories @@ -360,7 +360,7 @@ Press the "build" image:img/intellij-build-icon.png[] button on the toolbar. ===== NetBeans -Right click on the "root" module in the project explorer and select `Build`. +Right-click on the "root" module in the project explorer and select `Build`. image::img/netbeans-right-click-build.png[] @@ -370,7 +370,7 @@ Or you could have selected the "root" module in the project explorer and pressed ===== Eclipse IDE -Right click on the "root" module in the project explorer and select _Run as_ > _Maven Install_ +Right-click on the "root" module in the project explorer and select _Run as_ > _Maven Install_ image::img/eclipse-build-cn1lib.png[] @@ -381,7 +381,7 @@ TIP: If the build fails for any reason, check to make sure that your project is When using the Maven build tool, you no longer require the.cn1lib file at all. Your library projects can be handled entirely via Maven's dependency mechanism. The preferred way to distribute your libraries is on Maven central, and the preferred way to add a library to an application is via a Maven "pom" dependency. -That being said, you may still want to distribute your library as a.cn1lib file for the sake of users who are still using Ant as their build tool. For that reason, when you bulid a library project, the cn1lib is automatically built as well. After running a build, you can look in the common/target directory and find your.cn1lib file ready to be distributed. +That being said, you may still want to distribute your library as a.cn1lib file for the sake of users who are still using Ant as their build tool. For that reason, when you build a library project, the cn1lib is automatically built as well. After running a build, you can look in the common/target directory and find your.cn1lib file ready to be distributed. ==== Editing Java Code @@ -413,7 +413,7 @@ IMPORTANT: Make sure you're editing the common/pom.xml file of the *application This file may look a little hairy as there is a lot of configuration in there. You will be looking for the `` section. -The common/pom.xml file will have more than one `` tag, as it includes some profiles handling things like kotlin support. There will be one particular `` tag that includes a comment like +The common/pom.xml file will have more than one `` tag, as it includes some profiles handling things like Kotlin support. There will be one particular `` tag that includes a comment like [source,xml] ---- @@ -461,7 +461,7 @@ Every menu item is also reachable from CN1 UnitTests (or any app code) through ` ==== The contract -Each cn1lib ships a properties file at a well-known classpath location. The simulator scans every jar on its classpath for this resource and merges the results, so multiple cn1libs coexist cleanly. +Each cn1lib ships a properties file at a well-known classpath location. The simulator scans every jar on its classpath for this resource and merges the results, so multiple cn1libs coexist cleanly: [source,properties] ---- @@ -496,7 +496,7 @@ No groups, no submenus, no priority — flat by design. If you need ordering rel ==== The action method -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. +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] ---- diff --git a/docs/developer-guide/Maven-Getting-Started.adoc b/docs/developer-guide/Maven-Getting-Started.adoc index 5818dd7ade..3ede19c356 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -29,7 +29,7 @@ The easiest way to create a new project is to use the https://www.codenameone.co image::img/codenameone-initializr-screenshot.png[] -This tool will allow you to choose from a growing selection of project templates, and download a starter project that you can open in your preferred IDE (IntelliJ IDEA, NetBeans, etc..), or build directly on the command-line using Maven. +This tool will allow you to choose from a growing selection of project templates, and download a starter project that you can open in your preferred IDE (IntelliJ IDEA, NetBeans, etc.), or build directly on the command-line using Maven. The starter projects are based on the <>. @@ -52,7 +52,7 @@ See https://shannah.github.io/cn1app-archetype-kotlin-template/getting-started.h [#cn1app-archetype-example] ===== Bare-bones Java project -If you prefer to generate your projects directly on the command-line, you can use the <> to generate the project directly on the command-line. +If you prefer to generate your projects directly on the command-line, you can use the <> to generate the project directly on the command-line: [source,bash] ---- @@ -92,9 +92,9 @@ You can learn more about using the archetype in <> at its core, but it includes some more configuration settings and sources to change the template. You can use such templates as starter projects by using the `generate-app-project` goal of the Codename One Maven plugin. +The https://shannah.github.io/cn1app-archetype-kotlin-template/getting-started.html[bare-bones Kotlin App project template] is an alternative starter project that uses Kotlin as the primary language instead of Java. It's built on the <> at its core, but it includes some more configuration settings and sources to change the template. You can use such templates as starter projects by using the `generate-app-project` goal of the Codename One Maven plugin. -Here is an example which generates a project based on the bare-bones kotlin template: +Here is an example which generates a project based on the bare-bones Kotlin template: [source,bash,subs="+attributes"] ---- @@ -110,7 +110,7 @@ mvn com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:genera -DsourceProject=/path/to/kotlin-example-app ---- -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 modiy 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) +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) Like the `archetype:generate` goal, this will create the project in a directory named after your specified artifact ID. For example, If your command included `-DartifactId=myapp`, then the project would be in a newly created directory named "myapp." @@ -118,9 +118,9 @@ Some notes here: . The `com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project` argument is the fully qualified goal name for the `generate-app-project`. This is necessary since you aren't running this goal in the context of any existing project. You should adjust the version number (`{cn1-plugin-release-version}`) to reflect the https://search.maven.org/search?q=a:codenameone-maven-plugin[latest available Codename One version on Maven Central]. . The `archetypeGroupId`, `archetypeArtifactId`, and `archetypeVersion` parameters are the same as when using the `archetype:generate` goal, and they will (almost) always refer to the <>. -. The `groupId`, `artifactId`, and `version` work the same as for the `archetype:generate` goal. that's, that they specify the coordinates for your newly created project. +. The `groupId`, `artifactId`, and `version` work the same as for the `archetype:generate` goal. That's, that they specify the coordinates for your newly created project. . The `mainName` specifies the Main class name for your app. This is the class name, and shouldn't include the full package. For example, `MyApp`, not "com.example.MyApp" -. The `sourceProject` property is the path to the "template" project. In this case, you will assume that you've cloned the https://github.com/shannah/cn1app-archetype-kotlin-template[bare-bones kotlin project template repository] at /path/to/kotlin-example-app. +. The `sourceProject` property is the path to the "template" project. In this case, you will assume that you've cloned the https://github.com/shannah/cn1app-archetype-kotlin-template[bare-bones Kotlin project template repository] at /path/to/kotlin-example-app. [TIP] ==== @@ -132,7 +132,7 @@ See <> for instructions on building your own project [#migrate-existing-project] === Migrating an existing project -If you've an existing Codename One application project that uses the old Ant project structure, you can use the `generate-app-project` goal to migrate the project over to maven. This goal doesn't make any changes to the Ant project. It creates a new Maven project and copies over all the project sources and libraries, reorganized to fit the new project structure. +If you have an existing Codename One application project that uses the old Ant project structure, you can use the `generate-app-project` goal to migrate the project over to maven. This goal doesn't make any changes to the Ant project. It creates a new Maven project and copies over all the project sources and libraries, reorganized to fit the new project structure. A minimal invocation of this goal would look like: @@ -149,7 +149,7 @@ mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ -Dcn1Version=$CN1VERSION ---- -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 modiy 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) +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) This will generate the new project in the current directory inside a folder named after the `artifactId` parameter. @@ -183,7 +183,7 @@ If all goes well it should open in the Codename One simulator. ==== Eclipse IDE -Open Eclipse, and select "File" > "Import.." +Open Eclipse, and select "File" > "Import..." image::img/eclipse-file-menu-import.png[] @@ -228,7 +228,7 @@ If all goes well it should open in the Codename One simulator. Consider a concrete example, now. Download the KitchenSink Ant project from https://github.com/codenameone/KitchenSink/archive/refs/tags/v1.0-cn7.0.11.zip[here] and extract it. -The following is a bash script that uses curl to download this project as a zip file, and then converts it to a fully functional Maven project. +The following is a bash script that uses curl to download this project as a zip file, and then converts it to a fully functional Maven project: [source,bash,subs="+attributes"] ---- @@ -247,7 +247,7 @@ mvn com.codenameone:codenameone-maven-plugin:${CN1_VERSION}:generate-app-project -DsourceProject=KitchenSink-1.0-cn7.0.11 ---- -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 modiy 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) +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) This will generate the maven project in a directory named "kitchensink" in the current working directory because of the `-DartifactId=kitchensink` directory. @@ -259,20 +259,20 @@ TIP: For the easiest and recommended approach to adding dependencies to your pro One of the reasons to use Maven as the build tool is because it makes the management of project dependencies *almost* trivial. If the library you want to add is on Maven central, then you can copy and paste its `` snippet into your pom.xml file and you're good to go. Maven does the rest. -NOTE: See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html[Introducton to the Dependency Mechanism] on the Maven website for a gentle, but comprehensive introduction to Maven dependencies. +NOTE: See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html[Introduction to the Dependency Mechanism] on the Maven website for a gentle, but comprehensive introduction to Maven dependencies. -With Codename One projects, there are a few caveats (see <>), and a few added nicities that make it easier to find and install add-on libraries in your project (see <>). +With Codename One projects, there are a few caveats (see <>), and a few added niceties that make it easier to find and install add-on libraries in your project (see <>). ==== Which `pom.xml` to add the `` snippet to -Suppose you've a Maven `` snippet that you've copied from Maven central, and it's burning a hole in your clipboard while you're trying to figure out where to paste it into your project. +Suppose you have a Maven `` snippet that you've copied from Maven central, and it's burning a hole in your clipboard while you're trying to figure out where to paste it into your project. Codename One application projects, being multi-module projects, have more than one `pom.xml` file; One per module. **Question:** Which pom.xml file should you paste the snippet into? **Answer:** common/pom.xml (almost always). -The "common" module is where all your Codename One application resides. It houses your Java and Kotlin files, your CSS files, your GUI builder files, your Codename One configuration files (that is, `codenameone_settings.properties`). Pretty much everything. The things you'd place in the other modules (for example, `Java SE`, `ios`, etc...) are your platform-specific native interface implementations; And in many applications you won't need any of that. +The "common" module is where all your Codename One application resides. It houses your Java and Kotlin files, your CSS files, your GUI builder files, your Codename One configuration files (that is, `codenameone_settings.properties`). Pretty much everything. The things you'd place in the other modules (for example, `Java SE`, `ios`, etc.) are your platform-specific native interface implementations; And in many applications you won't need any of that. When adding dependencies into your app, you'll therefore almost always place them inside the pom.xml file for the "common" module. @@ -281,7 +281,7 @@ TIP: You can add dependencies without needing to change XML configuration files .When to use the "other" pom.xml Files [sidebar] **** -The instructions say that you *almost* always add dependencies in the common/pom.xml file. what are the other modules' pom.xml files for, and when do you need to change them, or add dependencies to them? +The instructions say that you *almost* always add dependencies in the common/pom.xml file. What are the other modules' pom.xml files for, and when do you need to change them, or add dependencies to them? Here's an overview: @@ -299,7 +299,7 @@ These modules don't use Maven for their dependencies (Android may deserve a smal **** [#maven-dependency-example] -==== Example: Adding Google maps dependency via Maven central +==== Example: Adding Google Maps dependency via Maven central Add the GoogleMaps library to your app as a maven dependency. @@ -368,7 +368,7 @@ Type in "Maps" into the search box, and it should narrow the options down to thr image:img/control-center-extensions-search-maps.png[Control Center Extensions filtered on maps] -The one in the middle `Codename One Google Native`, is the Google maps lib that you want. +The one in the middle `Codename One Google Native`, is the Google Maps lib that you want. Press the "Download" button. @@ -386,7 +386,7 @@ You shouldn't need to worry about this, as it happens seamlessly. If you're curi ==== Installing legacy cn1libs -The recommended approach for installing add-ons to your project is to use the <>, or by <>. But, in some situations you may not be able to use those methods. For example, If you've a legacy cnlib file that you need to use in your app, and it isn't available on Maven central or the control center. +The recommended approach for installing add-ons to your project is to use the <>, or by <>. But, in some situations you may not be able to use those methods. For example, If you have a legacy cnlib file that you need to use in your app, and it isn't available on Maven central or the control center. In cases like this you can use the `install-cn1lib` Maven goal to install it as follows: diff --git a/docs/developer-guide/Maven-Project-Templates.adoc b/docs/developer-guide/Maven-Project-Templates.adoc index 975bedaee7..4fa2960047 100644 --- a/docs/developer-guide/Maven-Project-Templates.adoc +++ b/docs/developer-guide/Maven-Project-Templates.adoc @@ -7,7 +7,7 @@ Any Codename One project can be converted into a project template. === Converting a Codename One application project into a project template -If you've an existing maven Codename One application project, you can convert it into a project template by adding a file named `generate-app-project.rpf` in the root directory of the project. +If you have an existing maven Codename One application project, you can convert it into a project template by adding a file named `generate-app-project.rpf` in the root directory of the project. The contents of this file should look like: @@ -49,5 +49,5 @@ You can test your project template by using it as the `sourceProject` parameter === Add your template to Codename One intializr -If you've a project template that you want to share with the community, please file an issue in the https://github.com/codenameone/CodenameOne/issues[Codename One issue tracker] with a link to a Github Repository of your project template, and request to have it added https://www.codenameone.com/initializr[Codename One initializr]. +If you have a project template that you want to share with the community, please file an issue in the https://github.com/codenameone/CodenameOne/issues[Codename One issue tracker] with a link to a GitHub Repository of your project template, and request to have it added https://www.codenameone.com/initializr[Codename One initializr]. diff --git a/docs/developer-guide/Maven-Updating-Codename-One.adoc b/docs/developer-guide/Maven-Updating-Codename-One.adoc index 9f1ed773f9..b8ea48f0f9 100644 --- a/docs/developer-guide/Maven-Updating-Codename-One.adoc +++ b/docs/developer-guide/Maven-Updating-Codename-One.adoc @@ -1,10 +1,12 @@ [#updating] == Updating Codename One -Codename One releases new versions weekly on Maven central. it's recommended that you stay up to date with the latest version as much as possible to ensure compatibility with the Codename One build server, which is always running the latest version. +Codename One releases new versions weekly on Maven central. It's recommended that you stay up to date with the latest version as much as possible to ensure compatibility with the Codename One build server, which is always running the latest version. You can use the <> to update both the Codename One libraries, and the Codename One dependencies in your project. -For example, [source,bash] +For example: + +[source,bash] ---- mvn cn:update ---- @@ -33,7 +35,7 @@ Then press the image:img/intellij-run-icon.png[Run] button. [discrete] === NetBeans -Or you can right click on the project in the project inspector, and select "Run Maven" > "Update Codename One" as shown here: +Or you can right-click on the project in the project inspector, and select "Run Maven" > "Update Codename One" as shown here: image::img/netbeans-update-codenameone.png[] diff --git a/docs/developer-guide/Miscellaneous-Features.asciidoc b/docs/developer-guide/Miscellaneous-Features.asciidoc index 08234a48f9..c9d4e5c0a6 100644 --- a/docs/developer-guide/Miscellaneous-Features.asciidoc +++ b/docs/developer-guide/Miscellaneous-Features.asciidoc @@ -80,7 +80,7 @@ On iOS, `isInCall()` is inferred from app interruption lifecycle events, which m Because iOS toggles this flag during app lifecycle transitions, polling it with a `UITimer` can miss the interruption window entirely (timers are paused while the app is inactive, and the flag is reset when the app becomes active again). If you need to react, check it from lifecycle callbacks such as your app's `stop()`/`start()` flow instead of periodic polling. -On Android, call detection is unsupported because robust detection would require invasive telephony permissions that are intentionally avoided. +On Android, call detection is unsupported because robust detection would require invasive telephony permissions that are intentionally avoided: [source,java] ---- @@ -114,7 +114,7 @@ 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 +NOTE: Some features such as attachments etc. Don't work in the simulator but should work on iOS/Android === Larger text accessibility @@ -162,7 +162,7 @@ The contacts API provides you with the means to query the phone’s address book Notice that on some platforms this will prompt the user for permissions and the user might choose not to grant that permission. To detect whether this is the case you can invoke `isContactsPermissionGranted()` after invoking `getAllContacts()`. This can help you adapt your error message to the user. -Once you've 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 +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] ---- @@ -175,7 +175,7 @@ Here you can specify true for the attributes that actually matter to you. Another capability of the contacts API is the ability to extract all the contacts. This isn't supported on all platforms but platforms such as Android can get a boost from this API as extracting the contacts one by one is slow on Android. -You can check if a platform supports the extraction of all the contacts thru `ContactsManager.isAllContactsFast()`. +You can check if a platform supports the extraction of all the contacts through `ContactsManager.isAllContactsFast()`. IMPORTANT: When retrieving all the contacts, notice that you should probably not retrieve all the data and should set some fields to false to perform a more efficient query @@ -268,7 +268,7 @@ You can install the bundle using code like this: UIManager.getInstance().setBundle(res.getL10N("l10n", local)); ---- -The device language (as an ISO 639 two letter code) could be retrieved with this: +The device language (as an ISO 639 two-letter code) could be retrieved with this: [source,java] ---- @@ -313,7 +313,7 @@ image::img/l10n-basic.png[Localized label,scaledwidth=30%] The https://www.codenameone.com/javadoc/com/codename1/l10n/L10NManager.html[L10NManager] class includes a multitude of features useful for common localization tasks. -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. +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] ---- @@ -355,15 +355,15 @@ Resource bundles can also include special case constant @rtl, which indicates if When in `RTL` mode the UI will be the exact mirror so `WEST` will become `EAST`, `RIGHT` will become `LEFT` and this would be true for paddings/margins as well. -If you've a special case where you don’t want this behavior you will need to wrap it with an `isRTL` check. You can also use `setRTL` on a per `Component` basis to disable RTL behavior for a specific `Component`. +If you have a special case where you don’t want this behavior you will need to wrap it with an `isRTL` check. You can also use `setRTL` on a per `Component` basis to disable RTL behavior for a specific `Component`. -NOTE: Most UI API's have special cases for BiDi instead of applying it globally for example: AWT introduced constants such as `LEADING` instead of making `WEST` mean the opposite direction. You think that was a mistake since the cases where you wouldn't want the behavior of automatic reversal are rare. +NOTE: Most UI APIs have special cases for BiDi instead of applying it globally for example: AWT introduced constants such as `LEADING` instead of making `WEST` mean the opposite direction. You think that was a mistake since the cases where you wouldn't want the behavior of automatic reversal are rare. Codename One's support for bidi includes the following components: * *Bidi algorithm* - allows converting between logical to visual representation for rendering * *Global RTL flag* - default flag for the entire application indicating the UI should flow from right to left -* *Individual RTL flag* - flag indicating that the specific component/container should be presented as an RTL/LTR component (for example: for displaying English elements within a RTL UI). +* *Individual RTL flag* - flag indicating that the specific component/container should be presented as an RTL/LTR component (for example: for displaying English elements within an RTL UI). * *RTL text field input* Most of Codename One's RTL support is under the hood, the https://www.codenameone.com/javadoc/com/codename1/ui/plaf/LookAndFeel.html[LookAndFeel] global RTL flag can be enabled using: @@ -425,9 +425,9 @@ iOS requires you to supply usage descriptions for many features that will be dis ios.NSCameraUsageDescription=This app needs to use your camera to scan bar codes ---- -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. +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. -See <<_example_localizing_the_app_name, the above example>> for instructions on localizing values in the Info.plist file. Then add translations to the _InfoPlist.strings_ file for your usage descriptions. +See <<_example_localizing_the_app_name, the above example>> for instructions on localizing values in the Info.plist file. Then add translations to the `InfoPlist.strings` file for your usage descriptions. .ios/src/main/strings/en.lproj/InfoPlist.strings [source,strings] @@ -480,7 +480,7 @@ iOS doesn't localize launcher icons natively, so Codename One wires up https://d * The `CodenameOne_GLAppDelegate` is patched to call `-[UIApplication setAlternateIconName:completionHandler:]` at launch. The delegate reads `[NSLocale preferredLanguages]`, tries the full `_` key first, then falls back to the language-only key, and clears the alternate icon (reverting to the default) if no variant matches. * The injection is idempotent and runs before the `ios.afterFinishLaunching` hook, so any custom code you supply via that build hint is unaffected. -NOTE: iOS displays a system alert the first time an app switches to an alternate icon. This is platform-standard behaviour—Codename One can't suppress it. +NOTE: IOS displays a system alert the first time an app switches to an alternate icon. This is platform-standard behaviour—Codename One can't suppress it. ===== Tips and troubleshooting @@ -507,7 +507,7 @@ IMPORTANT: In order for location to work on iOS you *MUST* define the build hint // vale-skip: Microsoft.Adverbs: "repeatedly" is the precise frequency contrast with "once" here, not authorial padding. The `getCurrentLocationSync()` method is good for cases where you need to fetch a current location once and not repeatedly query location. It activates the GPS then turns it off to avoid excessive battery usage. For example, if an application needs to track motion or position over time it should use the location listener API to track location as such: -TIP: Notice that there is a method called `getCurrentLocation()` which will return the current state and might not be right for some cases. +TIP: Notice that there is a method called `getCurrentLocation()` which will return the current state and might not be right for some cases: [source,java] ---- @@ -523,7 +523,7 @@ public MyListener implements LocationListener { LocationManager.getLocationManager().setLocationListener(new MyListener()); ---- -IMPORTANT: On Android location maps to low-level API's if you disable the usage of Google Play Services. By default location should perform well if you leave the Google Play Services on +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 ==== Location in the background - geofencing @@ -541,7 +541,7 @@ When implementing this make sure that: - The class passed to the API is a public class in the global scope. Not an inner class or anything like that! - The class has a public no-argument constructor -- You need to pass it as a class literal for example: `MyClassName.class`. don't use `Class.forName("my.package.MyClassName")`! + +- You need to pass it as a class literal for example: `MyClassName.class`. Don't use `Class.forName("my.package.MyClassName")`! + Class names are problematic since device builds are obfuscated, you should use literals which the obfuscator detects and handles. The following code demonstrates usage of the GeoFence API: @@ -559,7 +559,7 @@ LocationManager.getLocationManager() On Android 11 (API level 30) and higher, requesting background location permission requires a two-step process. First, foreground location permissions must be granted. Then, the app must request background location access, which will direct the user to the system settings to select `Allow all the time`. Codename One handles this flow automatically when you use `LocationManager`. -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`. +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] ---- @@ -1027,7 +1027,7 @@ If your application isn't a GUI builder application or you would like to send mo In 2013 Google introduced an improved application level analytics API that's specifically built for mobile apps. For example, it requires a slightly different API usage. You can activate this specific mode by invoking `setAppsMode(true)`. -When using this mode you can also report errors and crashes to the Google analytics server using the `sendCrashReport(Throwable, String message, boolean fatal)` method. +When using this mode you can also report errors and crashes to the Google Analytics server using the `sendCrashReport(Throwable, String message, boolean fatal)` method. You recommend using this mode and setting up an apps analytics account as the results are more refined. @@ -1097,7 +1097,7 @@ IMPORTANT: The activity name should match the main class name followed by the wo .Android Activity definition image::img/chat-app-tutorial-facebook-login-7.png[Details,scaledwidth=50%] -To build the native Android app you must make sure that you setup the keystore for your application. If you don't have +To build the native Android app you must make sure that you set up the keystore for your application. If you don't have an Android certificate you can use the visual wizard (in the Android section in the project preferences the button labeled #Generate#) or use the command line: [source,bash] @@ -1132,7 +1132,7 @@ image::img/chat-app-tutorial-facebook-login-9.png[Enable The App,scaledwidth=50% ==== IDE setup -You now need to set some important build hints in the project so it will work. To set the build hints right click the project select project properties and in the Codename One section pick the second tab. Add this entry into the table: +You now need to set some important build hints in the project so it will work. To set the build hints right-click the project select project properties and in the Codename One section pick the second tab. Add this entry into the table: [source,bash] ---- @@ -1314,10 +1314,10 @@ Click on "Google Sign-In" Then you'll be presented with a field to enter the Android Signing Certificate SHA-1. -.Android Signing Certifiate SHA-1 -image::img/google-signin-android-signing-sha1.png[Android Signing Certifiate SHA-1,scaledwidth=40%] +.Android Signing Certificate SHA-1 +image::img/google-signin-android-signing-sha1.png[Android Signing Certificate SHA-1,scaledwidth=40%] -The value that you enter here should be obtained from the certificate that you're using to build your app. You an use the *keytool* app that's distributed with the JDK to extract this value +The value that you enter here should be obtained from the certificate that you're using to build your app. You can use the *keytool* app that's distributed with the JDK to extract this value [source,bash] ---- @@ -1408,9 +1408,9 @@ image::img/google-sign-in-google-cloud-platform-credentials.png[Credentials,scal image::img/google-sign-in-oauth-setup-redirect-url.png[Redirect URL,scaledwidth=30%] [[javascript-setup]] -==== Javascript setup instructions +==== JavaScript setup instructions -The Javascript port can use the same OAuth2.0 credentials as the simulator does. It doesn't require your Client Secret or redirect URL. It requires your Client ID, which you can specify using the `GoogleConnect.setClientID()` method. +The JavaScript port can use the same OAuth2.0 credentials as the simulator does. It doesn't require your Client Secret or redirect URL. It requires your Client ID, which you can specify using the `GoogleConnect.setClientID()` method. [[the-code]] ==== The Code @@ -1462,9 +1462,9 @@ For example, components like https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html[MultiButton], https://www.codenameone.com/javadoc/com/codename1/components/SpanButton.html[SpanButton] & https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html[SpanLabel] don't necessarily seem like the right candidate for compositing but they're all `Container` subclasses. Using a `Container` provides you a lot of flexibility in layout & functionality for a specific component. `MultiButton` -is a great example of that. it's a `Container` internally that's composed of 5 labels and a `Button`. +is a great example of that. It's a `Container` internally that's composed of 5 labels and a `Button`. -Codename One makes the `MultiButton` "feel" like a single button thru the use of `setLeadComponent(Component)` which +Codename One makes the `MultiButton` "feel" like a single button through the use of `setLeadComponent(Component)` which turns the button into the "leader" of the component. When a `Container` hierarchy is placed under a leader all events within the hierarchy are sent to the leader, so if a label within the lead component receives a pointer pressed event this event will be sent to the leader. @@ -1544,11 +1544,11 @@ public class SpanButton extends Container { The `Component` class has two methods that allow you to exclude a component from lead behavior: `setBlockLead(boolean)` & `isBlockLead()`. -Effectively when you've a `Component` within the lead hierarchy that you would like to treat differently from the rest you can use this method to exclude it from the lead component behavior while keeping the rest in line... +Effectively when you have a `Component` within the lead hierarchy that you would like to treat differently from the rest you can use this method to exclude it from the lead component behavior while keeping the rest in line... This should have no effect if the component isn't a part of a lead component. -The sample below is based on the `Accordion` component which uses a lead component internally. +The sample below is based on the `Accordion` component which uses a lead component internally: [source,java] ---- @@ -1608,9 +1608,9 @@ image::img/pull-to-refresh.png[Simple pull to refresh demo,scaledwidth=20%] The https://www.codenameone.com/javadoc/com/codename1/ui/Display.html[Display] class's `execute` method allows you to invoke a URL which is bound to a particular application. This works rather well assuming the application is installed. For example: link:http://wiki.akosma.com/IPhone_URL_Schemes[this list] -contains a set of valid URL's that can be used on iOS to run common applications and use built-in functionality. +contains a set of valid URLs that can be used on iOS to run common applications and use built-in functionality. -Some URL's might not be supported if an app isn't installed, on Android there isn't much that can be done but iOS has a `canOpenURL` method for Objective-C. +Some URLs might not be supported if an app isn't installed, on Android there isn't much that can be done but iOS has a `canOpenURL` method for Objective-C. On iOS you can use the `Display.canExecute()` method which returns a `Boolean` instead of a `boolean` which allows you to support 3 result states: @@ -1635,24 +1635,24 @@ if(can != null && can) { === Automatic build hint configuration -You try to make Codename One "seamless," this expresses itself in small details such as the automatic detection of permissions on Android etc. The build servers go a long way in setting up the environment as intuitive. However, it isn't enough, build hints are often confusing and obscure. it's hard to abstract the mess that's native mobile OS's and the odd policies from Apple/Google... +You try to make Codename One "seamless," this expresses itself in small details such as the automatic detection of permissions on Android etc. The build servers go a long way in setting up the environment as intuitive. However, it isn't enough, build hints are often confusing and obscure. It's hard to abstract the mess that's native mobile OSes and the odd policies from Apple/Google... A good example for a common problem developers face is location code that doesn't work in iOS. This is due to the `ios.locationUsageDescription` build hint that's required. The reason that build hint was added is a need by Apple to provide a description for every app that uses the location service. -To solve this sort of used case you've two API's in `Display`: +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/API's to verify user settings/build hints exist + * 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/API's to verify user settings/build hints exist. + * 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 @@ -1662,7 +1662,7 @@ 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... -If you will use the location API from the simulator and you didn't define `ios.locationUsageDescription` Codename One will implicitly define a string there. The cool thing is that you will now see that string in your settings and you would be able to customize it. +If you use the location API from the simulator and you didn't define `ios.locationUsageDescription` Codename One will implicitly define a string there. The cool thing is that you will now see that string in your settings and you would be able to customize it. For example, this gets way better than that trivial example! @@ -1698,7 +1698,7 @@ However, it gets better, say you want to return a value: 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)`. +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: @@ -1763,22 +1763,22 @@ lib/impl/ native/internal_tmp/ ---- -The important lines are `lib/impl/` and `native/internal_tmp/`. Technically cn1libs are zips. When you do a refresh libs they unzip into the right directories under `lib/impl` and `native/internal_tmp`. By excluding these directories you can remove duplicates that can result in conflicts. +The important lines are `lib/impl/` and `native/internal_tmp/`. Technically cn1libs are zips. When you run a refresh-libs operation they unzip into the right directories under `lib/impl` and `native/internal_tmp`. By excluding these directories you can remove duplicates that can result in conflicts. ==== Resource files -Committing the res file is a matter of personal choice. it's committed in the git ignore files above but you can remove it. The res file is at risk of corruption and in that case having a history you can refer to, matters a lot. +Committing the res file is a matter of personal choice. It's committed in the git ignore files above but you can remove it. The res file is at risk of corruption and in that case having a history you can refer to, matters a lot. -However, the resource file is a bit of a problematic file. As a binary file if you've a team working with it the conflicts can be a major blocker. This was far worse with the old GUI builder, that was one of the big motivations of moving into the new GUI builder which works better for teams. +However, the resource file is a bit of a problematic file. As a binary file if you have a team working with it the conflicts can be a major blocker. This was far worse with the old GUI builder, that was one of the big motivations of moving into the new GUI builder which works better for teams. -Still, if you want to keep an eye of every change in the resource file you can switch on the #File# -> #XML Team Mode# which should be on by default. This mode creates a file hierarchy under the `res` directory to match the res file you opened. For example: if you've a file named `src/theme.res` it will create a matching `res/theme.xml` and also nest all the images and resources you use in the res directory. +Still, if you want to keep an eye of every change in the resource file you can switch on the #File# -> #XML Team Mode# which should be on by default. This mode creates a file hierarchy under the `res` directory to match the res file you opened. For example: if you have a file named `src/theme.res` it will create a matching `res/theme.xml` and also nest all the images and resources you use in the res directory. That's useful as you can edit the files directly and keep track of every file in git. For example, this has two big drawbacks: - it's flaky - while this mode works it never reached the stability of the regular res file mode -- It conflicts - the simulator/device are oblivious to this mode. if you fetch an update you also need to update the res file and you might still have conflicts related to that file +- It conflicts - the simulator/device are oblivious to this mode. If you fetch an update you also need to update the res file and you might still have conflicts related to that file -Both of these issues shouldn't be a deal breaker. Even though this mode is a bit flaky it's better than the alternative as you can "see" the content of the resource file. You can revert and reapply your changes to the res file when merging from git, it's tedious but again not a deal breaker. +Both of these issues shouldn't be a deal-breaker. Even though this mode is a bit flaky it's better than the alternative as you can "see" the content of the resource file. You can revert and reapply your changes to the res file when merging from git, it's tedious but again not a deal-breaker. ==== Eclipse version diff --git a/docs/developer-guide/Monetization.asciidoc b/docs/developer-guide/Monetization.asciidoc index 8657b96bfc..dc3a222d56 100644 --- a/docs/developer-guide/Monetization.asciidoc +++ b/docs/developer-guide/Monetization.asciidoc @@ -32,14 +32,14 @@ Four classifications for products exist: 1. **Non-consumable Product** - This is a product that the user purchases once to "own." They can't re-buy it. One example is a product that upgrades your app to a "Pro" version. 2. **Consumable Product** - This is a product that the user can buy more than once. For example, you might have a product for "10 Credits" that lets the user buy items in a game. // vale-skip: Microsoft.Auto: "auto-renewed" matches the App Store / Play Store wording for the renewal action; rewriting it as "autorenewed" diverges from Apple and Google's documented terminology. -3. **Non-Renewable Subscription** - A subscription that you buy once, and won't be "auto-renewed" by the app store. These are almost identical to consumable products, except that subscriptions need to be transferable across all the user's devices. This means that non-renewable subscriptions require that you've a server that keeps track of the subscriptions. +3. **Non-Renewable Subscription** - A subscription that you buy once, and won't be "auto-renewed" by the app store. These are almost identical to consumable products, except that subscriptions need to be transferable across all the user's devices. This means that non-renewable subscriptions require that you have a server that keeps track of the subscriptions. 4. **Renewable Subscriptions** - A subscription that the app store manages. The user will be automatically billed when the subscription period ends, and the subscription will renew. NOTE: These subscription categories may not be explicitly supported by a given store, or they may use different names. You can integrate each product type in a cross-platform way using Codename One. For example, Google Play doesn't distinguish between consumable products and non-renewable subscriptions, but iTunes does. ==== The "hello world" of in-app purchase -Start with a simple example of an app that sells `Worlds`. First, pick the SKU for the product. Here you use `com.codename1.world`. +Start with a simple example of an app that sells `Worlds`. First, pick the SKU for the product. Here you use `com.codename1.world`: [source,java] ---- @@ -104,7 +104,7 @@ Now in the `start()` method, add a button that lets the user buy the world: } ---- -At this point, the app can track the sale of the world. To make it more useful, add `ToastBar` feedback for purchase completion. +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] ---- @@ -141,7 +141,7 @@ image::img/iap-demo3.png[In App buy already owned,scaledwidth=20%] In the "Buy World" example above, the "world" product was non-consumable, since you could only buy the world once. You could change it to a consumable product by disregarding whether it was purchased before & keeping track of how many times it had been purchased. -You'll use storage to keep track of the number of worlds that the user purchased. You need two methods to manage this count. One method gets the number of worlds that you own, and another adds a world to this count. +You'll use storage to keep track of the number of worlds that the user purchased. You need two methods to manage this count. One method gets the number of worlds that you own, and another adds a world to this count: [source,java] ---- @@ -273,8 +273,8 @@ In order for any of this to work, you must implement the `ReceiptStore` interfac You'll expand on the theme of "Buying" the world for this app, except, this time you will "Rent" the world for a period of time. You'll have two products: -. A 1 month subscription -. A 1 year subscription +. A 1-month subscription +. A 1-year subscription [source,java] ---- @@ -287,11 +287,11 @@ public static final String[] PRODUCTS = { }; ---- -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. +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. -NOTE: Different SKUs that sell the same service/product but for different periods form a "subscription group." Conceptually, customers aren't subscribing to a particular SKU, they're subscribing to the subscription group of which that SKU is a member. As an example, if a user purchases a 1 month subscription to "the world," they're actually subscribing to "the world" subscription group. +NOTE: Different SKUs that sell the same service/product but for different periods form a "subscription group." Conceptually, customers aren't subscribing to a particular SKU, they're subscribing to the subscription group of which that SKU is a member. As an example, if a user purchases 1-month subscription to "the world," they're actually subscribing to "the world" subscription group. -It's up to you to know the grouping of your SKUs. Any methods in the `Purchase` class that check subscription status or expiry date of a SKU should be passed *all* SKUs of that subscription group. For example, If you want to know if the user is subscribed to the `SKU_WORLD_1_MONTH` subscription, it would not be enough to call `iap.isSubscribed(SKU_WORLD_1_MONTH)`, because that wouldn't consider if the user had purchased a 1 year subscription. The correct way is to always call `iap.isSubscribed(SKU_WORLD_1_MONTH, SKU_WORLD_1_YEAR)`, or `iap.isSubscribed(PRODUCTS)` since you have placed both SKUs into your PRODUCTS array. +It's up to you to know the grouping of your SKUs. Any methods in the `Purchase` class that check subscription status or expiry date of a SKU should be passed *all* SKUs of that subscription group. For example, If you want to know if the user is subscribed to the `SKU_WORLD_1_MONTH` subscription, it would not be enough to call `iap.isSubscribed(SKU_WORLD_1_MONTH)`, because that wouldn't consider if the user had purchased 1-year subscription. The correct way is to always call `iap.isSubscribed(SKU_WORLD_1_MONTH, SKU_WORLD_1_YEAR)`, or `iap.isSubscribed(PRODUCTS)` since you have placed both SKUs into your PRODUCTS array. ===== Implementing the receipt store @@ -307,7 +307,7 @@ A basic receipt store needs to implement just two methods: . `fetchReceipts` . `submitReceipt` -You'll register it in your app's init() method so that it's always available. +You'll register it in your app's init() method so that it's always available: [source,java] ---- @@ -361,7 +361,7 @@ This is straight forward. You're checking to see if you already have a list of r NOTE: `Receipt` implements `Externalizable` so you are able to write instances directly to Storage. -The `submitReceipt()` method is a little more complex, as it needs to calculate the new expiry date for your subscription. +The `submitReceipt()` method is a little more complex, as it needs to calculate the new expiry date for your subscription: [source,java] ---- @@ -462,7 +462,7 @@ public void start() { } ---- -And you also provide a button to allow the user to manually synchronize the receipts. +And you also provide a button to allow the user to manually synchronize the receipts: [source,java] ---- @@ -478,7 +478,7 @@ syncReceipts.addActionListener(e->{ ===== Expiry dates and subscription status -Now that you have a receipt store registered, and you have synchronized your receipts, you can query the `Purchase` instance to see if a SKU or set of SKUs is subscribed. three useful methods in this realm: +Now that you have a receipt store registered, and you have synchronized your receipts, you can query the `Purchase` instance to see if a SKU or set of SKUs is subscribed. Three useful methods in this realm: . `boolean isSubscribed(String... skus)` - Checks to see if the user is subscribed to any of the provided SKUs. . `Date getExpiryDate(String... skus)` - Gets the latest expiry date of a set of SKUs. @@ -486,7 +486,7 @@ Now that you have a receipt store registered, and you have synchronized your rec If you need to know more information about subscriptions, you can always just call `getReceipts()` to get a list of all the current receipts and determine for yourself what the user should have access to. -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. +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] ---- @@ -510,7 +510,7 @@ syncReceipts.addActionListener(e->{ ===== Allowing the user to purchase the subscription -You should now have all the background required to implement the Hello World Subscription app. you'll return to the code and see how the user purchases a subscription. +You should now have all the background required to implement the Hello World Subscription app. You'll return to the code and see how the user purchases a subscription. In the main form, you want two buttons to subscribe to the `World`, for one month and one year respectively. They look like: @@ -567,7 +567,7 @@ The `Purchase` class includes two methods for initiating a purchase: 1. `purchase(sku)` 2. `subscribe(sku)` -Which one you use depends on the type of product that's being purchased. If your product is set up as a subscription in the Google Play store, then you should use `subscribe(sku)`. Otherwise, you should use `purchase(sku)`. +Which one you use depends on the type of product that's being purchased. If your product is set up as a subscription in the Google Play Store, then you should use `subscribe(sku)`. Otherwise, you should use `purchase(sku)`. ===== Handling purchase callbacks @@ -635,7 +635,7 @@ When deciding between auto-renewable and non-renewable subscriptions, as always, NOTE: Some developers https://marco.org/2013/12/02/auto-renewable-subscriptions[are opposed to auto-renewable subscriptions]. There isn't enough information to make a solid recommendation on this matter. // vale-skip: Microsoft.Auto: "auto-renewable" matches Apple's documented subscription type. -On a practical level, if you are using auto-renewable subscriptions (and so subscription products in the Google play store) you must use the `Purchase.subscribe(sku)` method for initiating the purchase workflow. For non-renewable subscriptions (and so regular products in the Google play store), you must use the `Purchase.purchase(sku)` method. +On a practical level, if you are using auto-renewable subscriptions (and so subscription products in the Google Play Store) you must use the `Purchase.subscribe(sku)` method for initiating the purchase workflow. For non-renewable subscriptions (and so regular products in the Google Play Store), you must use the `Purchase.purchase(sku)` method. ==== Learning by example @@ -661,7 +661,7 @@ private static final String localHost = "http://10.0.1.32"; ===== Setting up the server project -Download the CN1-IAP-Server demo project from Github, and run its "install-deps" ANT task to download and install its dependencies to your local Maven repo. +Download the CN1-IAP-Server demo project from GitHub, and run its "install-deps" ANT task to download and install its dependencies to your local Maven repo. NOTE: For the following commands to work, make sure you have "ant," "mvn," and "git" in your environment PATH. @@ -958,7 +958,7 @@ if (validator == null) { As you can see from this snippet, the complexity of receipt validation has been reduced to entering three configuration strings: 1. `APPLE_SECRET` - This is a "secret" string that you will get from iTunes connect when you set up your in-app products. -2. `GOOGLE_DEVELOPER_API_CLIENT_ID` - A client ID that you'll get from the google developer API console when you set up your API service credentials. +2. `GOOGLE_DEVELOPER_API_CLIENT_ID` - A client ID that you'll get from the Google developer API console when you set up your API service credentials. 3. `GOOGLE_DEVELOPER_PRIVATE_KEY` - A PKCS8 encoded string with an RSA private key that you'll receive at the same time as the `GOOGLE_DEVELOPER_API_CLIENT_ID`. The next section walks through the steps to get these values. @@ -1125,7 +1125,7 @@ IMPORTANT: You can't buy in-app products from your app using your publisher acco To test your app, you need to set up a test account. A test account must be associated with a real gmail email address. If you have a domain that's managed by Google apps, then you can also use an address from that domain. -The full process for testing in-app billing can be found in https://developer.android.com/google/play/billing/billing_testing.html[this google document]; the documentation is dense and can be difficult to follow. +The full process for testing in-app billing can be found in https://developer.android.com/google/play/billing/billing_testing.html[this Google document]; the documentation is dense and can be difficult to follow. For your purposes, you'll need to set up a tester list in Google Play. Choose "Settings" > `Tester Lists`. Then create a list with all the email address that you want to have treated as test accounts. Any purchases made by these email addresses will be treated as "Sandbox" purchases, and won't require real money to change hands. @@ -1136,7 +1136,7 @@ To test in-app purchase on Android, you *must* first publish your app. You can't For more information about setting up alpha testing on Google play see https://support.google.com/googleplay/android-developer/answer/3131213?hl=en[this Google support document on the subject]. -Once you have set your app up for alpha testing, you can send an invite link to your test accounts. You can find the link in the Google Play console under the APK section, under the "Alpha" tab (and assuming you've enabled alpha testing. +Once you have set your app up for alpha testing, you can send an invitation link to your test accounts. You can find the link in the Google Play console under the APK section, under the "Alpha" tab (and assuming you've enabled alpha testing. .Alpha testing tab in google play image::img/iap3-alpha-testing-tab.png[Alpha testing tab in google play,scaledwidth=40%] @@ -1149,8 +1149,8 @@ Also, before proceeding with testing in-app purchases, you need to add the in-ap After you have published your APK to the alpha channel, you can create the products. For the purposes of this tutorial, you'll just add two products: -. **iapdemo.noads.month.auto** - The 1 month subscription. -. **iapdemo.noads.3month.auto** - The 3 month subscription. +. **iapdemo.noads.month.auto** - The 1-month subscription. +. **iapdemo.noads.3month.auto** - The 3-month subscription. IMPORTANT: Since you will be adding products as "Subscriptions" in the pay store, your app *must* use the `Purchase.subscribe(sku)` method for initiating a purchase on these products, and *not* the `Purchase.purchase(sku)` method. If you use `purchase()` to buy a subscription on Android, the payment will go through, but your buy callback will receive an error. @@ -1170,7 +1170,7 @@ image::img/iap3-add-product-google.png[Add product to google] **Adding 3 month Subscription** -Follow the same process as for the 1 month subscription except use "iapdemo.noads.3month.auto" for the product ID, and select "3 months" for the billing period instead of `Monthly`. +Follow the same process as for the 1-month subscription except use "iapdemo.noads.3month.auto" for the product ID, and select "3 months" for the billing period instead of `Monthly`. ===== Testing the app @@ -1180,7 +1180,7 @@ Open the app, click on `Subscriptions`, and try to buy a 1-month subscription. I ===== Creating Google play receipt validation credentials -Google play receipt validation is accomplished via the https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get[android-publisher Purchases: get API]. The CN1-IAP-Validation library shields you from most of the complexities of using this API, but you still need to get a "private key" and a "client id" to access this API. Both of these are provided when you set up an https://developers.google.com/identity/protocols/OAuth2ServiceAccount[OAuth2 Service Account] for your app. +Google play receipt validation is accomplished via the https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get[android-publisher Purchases: get API]. The CN1-IAP-Validation library shields you from most of the complexities of using this API, but you still need to get a "private key" and a "client ID" to access this API. Both of these are provided when you set up an https://developers.google.com/identity/protocols/OAuth2ServiceAccount[OAuth2 Service Account] for your app. NOTE: The following steps assume that you have already created your app in Google play and have published it to at least the alpha channel. See your previous post on this topic here (Link to be provided). @@ -1205,7 +1205,7 @@ image::img/iap3-credentials-dropdown.png[Credentials dropdown,scaledwidth=20%] + .Create service account key image::img/iap3-create-service-account-key.png[Create service account key,scaledwidth=20%] -7. Enter anything you like for the `Service account name`. For the role, you'll select "Project" > "Owner" for now just so you don't run into permissions issues. You'll probably want to investigate further to fine a more limited role that only allows receipt verification, but for now, avoid any unnecessary road blocks for getting this to work. You're probably going to run into "permission denied" errors at first anyways, so the fewer reasons for this, the better. +7. Enter anything you like for the `Service account name`. For the role, you'll select "Project" > "Owner" for now just so you don't run into permissions issues. You'll probably want to investigate further to fine a more limited role that only allows receipt verification, but for now, avoid any unnecessary road blocks for getting this to work. You're probably going to run into "permission denied" errors at first anyway, so the fewer reasons for this, the better. 8. It will autogenerate an account ID for you. 9., for the `Key type`, select `JSON`. Then click the "Create" button. @@ -1217,7 +1217,7 @@ This should prompt the download of a JSON file that will have contents like the "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", + "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", @@ -1282,20 +1282,20 @@ Then build and run the server app. The `validateSubscriptionsCron()` method is s NOTE: You're assuming you've already added a receipt in the previous test that you did. If necessary, you should buy the subscription again in your app. -After a minute or so, you should see "----------- VALIDATING RECEIPTS ---------" written in the Glassfish log, and it will check your receipts. If it works, your receipt's expiry date will get populated in the database, and you can press "Synchronize Receipts" in your app to see this reflected. If it fails, there will like be a big ugly stack trace and exception readout with some clues about what went wrong. +After a minute or so, you should see "----------- VALIDATING RECEIPTS ---------" written in the Glassfish log, and it will check your receipts. If it works, your receipt's expiry date will get populated in the database, and you can press "Synchronize Receipts" in your app to see this reflected. If it fails, there will likely be a big ugly stack trace and exception readout with some clues about what went wrong. Realistically, your first try will fail for some reason. Use the error codes and stack traces to help lead you to the problem. And feel free to post questions here. ==== iTunes connect setup -The process for setting up and testing your app on iOS is much simpler than on Android (IMHO). It took you a couple hours to get the iTunes version working, vs a couple days on the Google Play side of things. One notable difference that makes things simpler is that you don't need to actually upload your app to the store to test in-app purchase. You can just use your debug build on your device. It's also *much* easier to roll a bunch of test accounts than on Google Play. You don't need to set up an alpha program, you just create a few "test accounts" (and this is easy to do) in your iTunes connect account, and then make sure to use one of these accounts when making a purchase. You can switch accounts on your device from the "Settings" app, where you can just log out of the iTunes store - which will cause you to be prompted in your app the next time you make a purchase. +The process for setting up and testing your app on iOS is much simpler than on Android (IMHO). It took you a couple of hours to get the iTunes version working, vs a couple of days on the Google Play side of things. One notable difference that makes things simpler is that you don't need to actually upload your app to the store to test in-app purchase. You can just use your debug build on your device. It's also *much* easier to roll a bunch of test accounts than on Google Play. You don't need to set up an alpha program, you just create a few "test accounts" (and this is easy to do) in your iTunes connect account, and then make sure to use one of these accounts when making a purchase. You can switch accounts on your device from the "Settings" app, where you can just log out of the iTunes store - which will cause you to be prompted in your app the next time you make a purchase. ===== Setting up In-App products -The process to add products in iTunes connect is outlined https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/CreatingInAppPurchaseProducts.html#//apple_ref/doc/uid/TP40013727-CH3-SW1[in this apple developer document]. You'll add your two SKUs: +The process to add products in iTunes connect is outlined https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/CreatingInAppPurchaseProducts.html#//apple_ref/doc/uid/TP40013727-CH3-SW1[in this Apple developer document]. You'll add your two SKUs: -. **iapdemo.noads.month.auto** - The 1 month subscription. -. **iapdemo.noads.3month.auto** - The 3 month subscription. +. **iapdemo.noads.month.auto** - The 1-month subscription. +. **iapdemo.noads.3month.auto** - The 3-month subscription. // vale-skip: Microsoft.Auto: "auto-renewable" matches Apple's documented subscription type. Just make sure you add them as auto-renewable subscriptions, and that you specify the appropriate renewal periods. Use the SKU as the product ID. Both of these products will be added to the same subscription group. Call the group whatever you like. @@ -1335,4 +1335,4 @@ Change this to `false`. If you rebuild and run the server project, and wait for the `validateSubscriptionsCron()` method to run, it should check the receipt. After about a minute (or less), you'll see the text "----------- VALIDATING RECEIPTS ---------" written to the Glassfish log file, followed by some output from connecting to the iTunes validation service. If all went well, you should see your receipt expiration date updated in the database. If not, you'll likely see some exception stack traces in the Glassfish log. -NOTE: Sandbox receipts in the iTunes store are set to run on an accelerated schedule. A 1 month subscription is actually 5 minutes, 3 months is 15 minutes etc... Also sandbox subscriptions don't seem to persist in perpetuity until the user has cancelled it. In practice they renew only 4 or 5 times before they're allowed to lapse by Apple. +NOTE: Sandbox receipts in the iTunes store are set to run on an accelerated schedule. A 1-month subscription is actually 5 minutes, 3 months is 15 minutes etc. Also sandbox subscriptions don't seem to persist in perpetuity until the user has cancelled it. In practice they renew only 4 or 5 times before they're allowed to lapse by Apple. diff --git a/docs/developer-guide/Native-Themes.asciidoc b/docs/developer-guide/Native-Themes.asciidoc index 2b4d8865e6..ccf78e6ab3 100644 --- a/docs/developer-guide/Native-Themes.asciidoc +++ b/docs/developer-guide/Native-Themes.asciidoc @@ -155,7 +155,7 @@ rebrand to apply at *runtime* (for in-app accent toggles, branded flavours, A/B tests) without recompiling the theme, see `Runtime accent palette override` further down - a single `addThemeProps({"@accent-color": ...})` call retunes every -accent-bearing UIID at once. +accent-bearing UIID at once: [source,css] ---- @@ -244,7 +244,8 @@ look. To layer a brand color on top, override `RaisedButton` and accent-driven UIIDs the same way as Android above. The color names -match Apple's `UIColor.systemBlue` etc. so you can mirror the SF +// vale-skip: write-good.So: "So" connects this consequence to the preceding sentence about Apple's UIColor names; replacing it would lose the bridging clause that motivates the next sentence. +match Apple's `UIColor.systemBlue` etc. So you can mirror the SF Symbols semantics if you want. === Accent palette override diff --git a/docs/developer-guide/Push-Notifications.asciidoc b/docs/developer-guide/Push-Notifications.asciidoc index 08e343469f..f7921c92ba 100644 --- a/docs/developer-guide/Push-Notifications.asciidoc +++ b/docs/developer-guide/Push-Notifications.asciidoc @@ -9,7 +9,7 @@ TIP: For a quick reference on setting up Push notifications, check out the https === Understanding push notifications -Push notifications provide a way to inform users that something of interest has taken place. it's one of the few mechanisms available to interact with the user *even when the app isn't running*. If your app is registered to receive push notifications, then you can send messages to the user's device through REST API either from another device, or, as is the case, from a web server. +Push notifications provide a way to inform users that something of interest has taken place. It's one of the few mechanisms available to interact with the user *even when the app isn't running*. If your app is registered to receive push notifications, then you can send messages to the user's device through REST API either from another device, or, as is the case, from a web server. Messages may contain a short title and text body that will be displayed to the user in their device's messages stream. They may also specify a badge to display on the app's icon (for example: that red circle on your mail app icon that indicates how many unread messages you've), a sound to play then the message arrives, an image attachment, and a set of "actions" that the user can perform directly in the push notification. @@ -18,7 +18,7 @@ In addition to messages that the user *sees*, a push notification can contain no === Implementing push support -Enabling push support in your application is a matter of implementing the `PushCallback` interface in your application's main class (that's: the class that includes your `start()`, `init()`, etc... methods. +Enabling push support in your application is a matter of implementing the `PushCallback` interface in your application's main class (that's: the class that includes your `start()`, `init()`, etc. methods: [source,java] ---- @@ -38,7 +38,7 @@ public class MyApplication implements PushCallback { /** * 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() + * @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) { @@ -57,7 +57,7 @@ public class MyApplication implements PushCallback { ---- -There will be more steps required to deploy to each platform (for example: iOS requires you to generate push certificates, Android needs you to register your app ID with their cloud messaging platform, etc...), but, fundamentally, this is all that's required to enable push support in your app. +There will be more steps required to deploy to each platform (for example: iOS requires you to generate push certificates, Android needs you to register your app ID with their cloud messaging platform, etc.), but, fundamentally, this is all that's required to enable push support in your app. === The push lifecycle @@ -83,11 +83,11 @@ Codename One provides a secure REST API for sending push notifications. As an HT There are two different scenarios to be aware of when it comes to receiving push notifications. If your app is running in the foreground when the message arrives, then it will be passed directly to your `push()` callback. If your app is either in the background, or not running, then the notification will be displayed in your device's notifications. If the user then taps the notification, it will open your app, and the `push()` callback will be run with the contents of the message. -Some push message types include hidden content that won't be displayed in your device's notifications. These hidden messages (or portions of messages) are passed directly to the `push()` callback of your app for processing. +Some push-message types include hidden content that won't be displayed in your device's notifications. These hidden messages (or portions of messages) are passed directly to the `push()` callback of your app for processing. NOTE: On iOS, hidden push messages (push type 2) won't be delivered when the app is in the background. -TIP: You can set the `android.background_push_handling` build hint to "true" to deliver push messages on Android when the app is minimized (running in the background). no equivalent setting on other platforms. +TIP: You can set the `android.background_push_handling` build hint to "true" to deliver push messages on Android when the app is minimized (running in the background). No equivalent setting on other platforms. [[delay-push-completion]] === Handling Long-Running background push tasks @@ -190,7 +190,7 @@ In Android (before API 27) this will trigger the push(String) call with the mess - `3` - `1 + 2 = 3` allows combining a visual push with a non-visual portion. Expects a message in the form: `This is what the user will see;This is something he won't see`. For example: you can bundle a special ID or even a JSON string in the hidden part while including a friendly message in the visual part. + When active this will trigger the push(String) method twice, once with the visual and once with the hidden data. -- `4` - Allows splitting a visual push request based on the format `title;body` to provide better visual representation in some OS's. +- `4` - Allows splitting a visual push request based on the format `title;body` to provide better visual representation in some OSes. - `5` - Sends a regular push message but doesn't play a sound when the push arrives @@ -249,7 +249,7 @@ This push will result in your `push()` callback being fired twice; once with the **Push Type 4; Message Body "Hello World;I am saying hello"** -Push type 4 specifies a title and a message body. In this example, alert title will be `Hello World`, and the body will by `I am saying hello`. +Push type 4 specifies a title and a message body. In this example, alert title will be `Hello World`, and the body will be `I am saying hello`. .Push type 4 "Hello World" message in simulator. image::img/push-type-4-simulator-paused.png[Push popup when app paused,scaledwidth=30%] @@ -305,9 +305,9 @@ On platforms that don't support badges or titles, the payload will fall back to .Badging on iOS **** -The badge number can be set thru code as well, this is useful if you want the badge to represent the unread count within your application. +The badge number can be set through code as well, this is useful if you want the badge to represent the unread count within your application. -To do this you've two methods in the https://www.codenameone.com/javadoc/com/codename1/ui/Display.html[Display] class: `isBadgingSupported()` and `setBadgeNumber(int)`. +To do this you have two methods in the https://www.codenameone.com/javadoc/com/codename1/ui/Display.html[Display] class: `isBadgingSupported()` and `setBadgeNumber(int)`. Notice that even if `isBadgingSupported` will return `true`, it won't work unless you activate push support! To truly use this you might need to disable the clearing of the badges on startup which you can do with the @@ -320,9 +320,9 @@ Rich push notifications refer to push notifications that include functionality a ==== Image attachment support -When you attach an image to a push notification, it will appear as a large image in the push notification on the user's device if that device supports it. iOS supports image attachments in iOS 10, Android supports them in API 26. The Javascript port doesn't support image attachments. If a platform that doesn't support image attachments receives a push notification with an image attachment, it will ignore it. +When you attach an image to a push notification, it will appear as a large image in the push notification on the user's device if that device supports it. iOS supports image attachments in iOS 10, Android supports them in API 26. The JavaScript port doesn't support image attachments. If a platform that doesn't support image attachments receives a push notification with an image attachment, it will ignore it. -Push type "99" is used to send rich push notifications. it's sort of a "meta" push type, or a "container," as it can be used to send any of the other push types, but to attach more content, such as image attachments. +Push type "99" is used to send rich push notifications. It's sort of a "meta" push type, or a "container," as it can be used to send any of the other push types, but to attach more content, such as image attachments. The message body should be an XML string. A minimal example of a push type 99 that *actually* sends a push type 1, which message "Hello World," but with an attached image is: @@ -480,7 +480,7 @@ You've implemented the Push callback in your app, and tested it in the push simu TIP: To set the push icon place a 24×24 icon named `ic_stat_notify.png` under the `native/android` folder of the app. The icon can be white with transparency areas -Android Push goes thru Google servers and to do that you need to register with Google to get keys for server usage. Google uses Firebase for its cloud messaging, so you will begin by creating a Firebase project. +Android Push goes through Google servers and to do that you need to register with Google to get keys for server usage. Google uses Firebase for its cloud messaging, so you will begin by creating a Firebase project. Go to https://console.firebase.google.com/ and click #Add project#: @@ -544,7 +544,7 @@ The "Server Key" displayed here is the `FCM_SERVER_API_KEY` that you refer to th .Save the Server key for later use. image::img/push-fcm-server-key.png[Save the Server Key for later use,scaledwidth=30%] -NOTE: The Sender ID shown in the above isn't required for your Android app, but it's helpful/required to support Push notifications in Javascript builds (in Chrome). This value is referred to elsewhere in this document as `GCM_SENDER_ID`. +NOTE: The Sender ID shown in the above isn't required for your Android app, but it's helpful/required to support Push notifications in JavaScript builds (in Chrome). This value is referred to elsewhere in this document as `GCM_SENDER_ID`. [[push-bureaucracy-ios-section]] ==== The push bureaucracy - iOS @@ -566,7 +566,7 @@ IMPORTANT: If you already have signing certificated defined for your app skip th You can then install the push certificates locally and use them later on but there is an easier way. You need to host the push certificates in the cloud so they will be reachable by the push servers, Codename One that for you seamlessly. + -Once you go thru the wizard you should get an automated email containing information about push and the location of the push certificates, it should start like this: +Once you go through the wizard you should get an automated email containing information about push and the location of the push certificates, it should start like this: ---- iOS Push certificates have been created for your app with bundle ID com.mycompany.myapp.mypushdemo. Please file this email away for safe keeping as you will need the details about the certificate locations and passwords to use Push successfully in your apps. @@ -582,7 +582,7 @@ https://codename-one-push-certificates.s3.amazonaws.com/com.mycompany.myapp.mypu Production Push Certificate Password: ssDfdsfer324 ---- -The URL's and passwords are everything that you will need later on to get push working on iOS. Notice that the wizard also performs a couple of other tasks specifically it sets the `ios.includePush` build hint to true & adds push to the provisioning profile etc. +The URLs and passwords are everything that you will need later on to get push working on iOS. Notice that the wizard also performs a couple of other tasks specifically it sets the `ios.includePush` build hint to true & adds push to the provisioning profile etc. // HTML_ONLY_START You can read more about the certificate wizard in the https://www.codenameone.com/manual/signing.html[signing section]. @@ -609,7 +609,7 @@ Codename One apps support push in browsers that implement the Web Push API. At t Firefox doesn't require any special setup for Push. If your main class implements the `PushCallback` interface, it should * work*. -Chrome uses FCM for its push notifications - the same system that Android uses. The directions for setting up a FCM account are the same as provided <>, and you can reuse the same `GCM_SENDER_ID` and `FCM_API_SERVER_KEY` values. For Chrome push support you will need to add the `GCM_SENDER_ID` in the `gcm.sender_id` build hint so that the GCM_SENDER_ID will be added to the app's manifest file: +Chrome uses FCM for its push notifications - the same system that Android uses. The directions for setting up an FCM account are the same as provided <>, and you can reuse the same `GCM_SENDER_ID` and `FCM_API_SERVER_KEY` values. For Chrome push support you will need to add the `GCM_SENDER_ID` in the `gcm.sender_id` build hint so that the GCM_SENDER_ID will be added to the app's manifest file: ---- gcm.sender_id=GCM_SENDER_ID @@ -617,18 +617,18 @@ gcm.sender_id=GCM_SENDER_ID Where `GCM_SENDER_ID` is your GCM_SENDER_ID. -NOTE: Push support requires that your app be served over https with a valid SSL certificate. It won't work with the "preview" version of your app. You'll need to download the.zip or.war file and host the file on your own site - with a valid SSL certificate. +NOTE: Push support requires that your app be served over https with a valid SSL certificate. It won't work with the "preview" version of your app. You'll need to download the `.zip` or `.war` file and host the file on your own site - with a valid SSL certificate. === Sending push messages -You can send a push message in many ways for example: from another device or any kind of server but there are some values that you will need regardless of the way in which you send the push message. +You can send a push message in many ways for example: from another device or any kind of server but there are some values that you will need regardless of the way in which you send the push message: [source,java] ----- private static final String PUSH_TOKEN = "********-****-****-****-*************"; ----- -The push token is a unique "key" that you can use to send push thru your Codename One account. It allows you to send push messages without placing your Codename One email or password into your source files. +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. You can get it by going to the Codename One build server dashboard at https://www.codenameone.com/build-server.html and selecting the #Account# tab. @@ -637,7 +637,7 @@ The token should appear at the bottom as such: .Push Token from the build server image::img/push-token.png[Push Token from the build server,scaledwidth=30%] -The instructions for extracting the API key for Google are <>. +The instructions for extracting the API key for Google are <>: [source,java] ----- @@ -647,11 +647,11 @@ private static final String FCM_SERVER_API_KEY = "******************-*********** Historical UWP push credentials (SID/client secret) were used by the discontinued UWP target, which was removed in release 7.0.229. -When sending push to iOS devices you've two modes: +When sending push to iOS devices you have two modes: - Production - Distribution -This allows you to debug the push related functionality without risking the possibility of sending a push into a production app. Its important to send the values to the right server during development/production. +This allows you to debug the push related functionality without risking the possibility of sending a push into a production app. Its important to send the values to the right server during development/production: [source,java] ----- @@ -662,7 +662,7 @@ iOS needs a certificate to send a push, this allows you to prove to Apples push IMPORTANT: These aren't the signing certificates and are separate from them! -You can get these two certificates (for development/App Store) through the certificate wizard as <>. +You can get these two certificates (for development/App Store) through the certificate wizard as <>: [source,java] ----- @@ -720,14 +720,14 @@ Sending a push message from the server is a more elaborate affair and might requ You can send a push message as an HTTP `POST` request to https://push.codenameone.com/push/push[https://push.codenameone.com/push/push]. That URL accepts the following arguments: -- *token* - your developer token to identify the account sending the push - `PUSH_TOKEN` -- *device* - one or more device keys to send the push to. You can send push to up to 500 devices with a single request - -- *type* - the message type identical to the old set of supported types in the old push servers -- *body* - the body of the message -- *auth* - the Google push auth key - `FCM_SERVER_API_KEY` (also used for sending to Chrome Javascript Apps) -- *production* - `true`/`false` whether to push to production or sandbox environment in iOS - `ITUNES_PRODUCTION_PUSH` -- *certPassword* - password for the push certificate in iOS push - `ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD` or `ITUNES_PRODUCTION_PUSH_CERT_PASSWORD` -- *cert* - http or https URL containing the push certificate for an iOS push - `ITUNES_DEVELOPMENT_PUSH_CERT` or `ITUNES_PRODUCTION_PUSH_CERT` +- `token` - your developer token to identify the account sending the push - `PUSH_TOKEN` +- `device` - one or more device keys to send the push to. You can send push to up to 500 devices with a single request - +- `type` - the message type identical to the old set of supported types in the old push servers +- `body` - the body of the message +- `auth` - the Google push auth key - `FCM_SERVER_API_KEY` (also used for sending to Chrome JavaScript Apps) +- `production` - `true`/`false` whether to push to production or sandbox environment in iOS - `ITUNES_PRODUCTION_PUSH` +- `certPassword` - password for the push certificate in iOS push - `ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD` or `ITUNES_PRODUCTION_PUSH_CERT_PASSWORD` +- `cert` - http or https URL containing the push certificate for an iOS push - `ITUNES_DEVELOPMENT_PUSH_CERT` or `ITUNES_PRODUCTION_PUSH_CERT` You can thus send a push from Java EE using code like this: @@ -766,9 +766,9 @@ Notice that you can send a push to 500 devices. To send in larger batches you ne ===== Server JSON responses -The push servers send responses in JSON form. it's crucial to parse and manage those as they might contain important information. +The push servers send responses in JSON form. It's crucial to parse and manage those as they might contain important information. -If there is an error that isn't fatal such as quota exceeded etc. you will get an error message like this: +If there is an error that isn't fatal such as quota exceeded etc. You will get an error message like this: [source,JavaScript] ----- @@ -791,7 +791,7 @@ A normal response, will be an array with results: There are many things to notice in the responses above: -- If the response contains `status=updateId` it means that the GCM server wants you to update the device id to a new device id. You should do that in the database and avoid sending pushes to the old key +- If the response contains `status=updateId` it means that the GCM server wants you to update the device ID to a new device ID. You should do that in the database and avoid sending pushes to the old key - iOS doesn't acknowledge device receipt but it does send a `status=inactive` result which you should use to remove the device from the list of devices IMPORTANT: APNS (Apple's push service) returns uppercase key results. This means that code for managing the keys in your database must be case insensitive diff --git a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc index 443a7451d6..6a84a07ec9 100644 --- a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc +++ b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc @@ -27,7 +27,7 @@ Codename One components share a generic inheritance hierarchy. For example, http Some components are composites and derive from the https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container] class. For example, the https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html[MultiButton] is a composite button that derives from `Container` but behaves like a `Button`. Most of the time this feels seamless, but a few details matter. -- don't use the `Container`-derived methods on such a composite component (for example, `add` and `remove`). +- Don't use the `Container`-derived methods on such a composite component (for example, `add` and `remove`). - You can't cast it to the type it relates to. For example, you can't cast `MultiButton` to `Button`. @@ -157,7 +157,7 @@ The second layer is the glass pane which allows you to draw arbitrary things on 2. `LayeredPane` is second 3. `GlassPane` is painted last -NOTE: it's important to notice that a layered pane is on top of the `ContentPane` and doesn't stretch to the title. A `GlassPane` stretches all the way but with a "lightweight" title area, for example, the https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html[Toolbar API]. +NOTE: It's important to notice that a layered pane is on top of the `ContentPane` and doesn't stretch to the title. A `GlassPane` stretches all the way but with a "lightweight" title area, for example, the https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html[Toolbar API]. The `GlassPane` allows developers to overlay UI on top of existing UI and paint as they see fit. This is useful for things that provide notification but don't want to intrude with application functionality. @@ -167,10 +167,10 @@ NOTE: In earlier versions of Codename One (pre-3.6), `LayeredPane` & `GlassPane` A https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html[Dialog] is a special kind of `Form` that can occupy a portion of the screen. It also has the more functionality of the modal `show` method. -When showing a dialog you've two basic options: modeless and modal: +When showing a dialog you have two basic options: modeless and modal: - Modal dialogs (the default) block the current EDT thread until the dialog is dismissed. To understand how they do it, read about `invokeAndBlock`. + -Modal dialogs are an useful way to prompt the user since the code can assume the user responded in the next line of execution. This promotes a linear and intuitive way of writing code. +Modal dialogs are a useful way to prompt the user since the code can assume the user responded in the next line of execution. This promotes a linear and intuitive way of writing code. - Modeless dialogs return, so a call to show such a dialog can't assume anything in the next line of execution. This is useful for features such as progress indicators where you aren't waiting for user input. @@ -239,7 +239,7 @@ NOTE: `hi` is the name of the parent `Form` in the sample above. It's important to style a `Dialog` using https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html#getDialogStyle--[getDialogStyle()] or https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html#setDialogUIID-java.lang.String-[setDialogUIID] methods rather than styling the dialog object directly. -The reason for this is that the `Dialog` is a `Form` that takes up the whole screen. The `Form` that's visible behind the `Dialog` is rendered as a screenshot. customizing the actual `UIID` of the `Dialog` won't produce the desired results. +The reason for this is that the `Dialog` is a `Form` that takes up the whole screen. The `Form` that's visible behind the `Dialog` is rendered as a screenshot. Customizing the actual `UIID` of the `Dialog` won't produce the desired results. ==== Tint and blurring @@ -276,9 +276,9 @@ hi.show(); .Dialog with green tinted background image::img/components-dialog-green-tint.png[Dialog with green tinted background,scaledwidth=20%] -You can apply Gaussian blur to the background of a dialog to highlight the foreground further and produce a attractive effect. You can use the `setDefaultBlurBackgroundRadius` to apply this globally, you can use the theme constant `dialogBlurRadiusInt` to do the same or you can do this on a per `Dialog` basis using `setBlurBackgroundRadius`. +You can apply Gaussian blur to the background of a dialog to highlight the foreground further and produce an attractive effect. You can use the `setDefaultBlurBackgroundRadius` to apply this globally, you can use the theme constant `dialogBlurRadiusInt` to do the same or you can do this on a per `Dialog` basis using `setBlurBackgroundRadius`. -NOTE: Not all device types support blur you can test if your device supports it using `Display.getInstnace().isGaussianBlurSupported()`. If blur isn't supported the blur setting will be ignored. +NOTE: Not all device types support blur you can test if your device supports it using `Display.getInstnace().isGaussianBlurSupported()`. If blur isn't supported the blur setting will be ignored: [source,java] ---- @@ -306,7 +306,7 @@ image::img/components-dialog-blur-no-tint.png[The blur effect is more pronounced ==== Popup dialog -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: +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] ---- @@ -327,9 +327,9 @@ When Codename One was young you needed a popup arrow implementation but your low Today Codename One supports pointing an arrow from the `RoundRectBorder` class. This is implicit for the `PopupDialog` UI. This allows for better customization of the border (color etc.) and it looks better on newer displays. It also works on all OSs. Right now the iOS theme has the old image border approach. -NOTE: This will change with a future update where all OS's will align and iOS will use the lightweight popup too +NOTE: This will change with a future update where all OSes will align and iOS will use the lightweight popup too -TIP: You can make all OS's act the same way by overriding the `PopupDialog` UIID and defining its style to `RoundRectBorder` +TIP: You can make all OSes act the same way by overriding the `PopupDialog` UIID and defining its style to `RoundRectBorder` The new `RoundRectBorder` support works by setting the track component property on border. When that’s done the border implicitly points to the right location. @@ -388,7 +388,7 @@ dlg.show(0, 0, Display.getInstance().getDisplayWidth() - (pre.getWidth() + pre.g .Interaction Dialog image::img/components-interaction-dialog.png[Interaction Dialog,scaledwidth=20%] -This will show the dialog on the right hand side of the screen, which is pretty useful for a floating in place dialog. +This will show the dialog on the right-hand side of the screen, which is pretty useful for a floating in place dialog. NOTE: The `InteractionDialog` can be shown at absolute or popup locations. This is inherent to its use case which is "non-blocking." When using this component you need to be aware of its location. @@ -421,7 +421,7 @@ hi.add(left).add(right).add(bottom).add(top); .Label positions image::img/components-label-text-position.png[Label positions,scaledwidth=20%] -`Label` allows a single line of text, line breaking is a expensive operation on mobile devices footnote:[String width is the real expensive part here, the complexity of font kerning and the recursion required to reflow text is a big performance hurdle] and so the `Label` class doesn't support it. +`Label` allows a single line of text, line breaking is an expensive operation on mobile devices footnote:[String width is the real expensive part here, the complexity of font kerning and the recursion required to reflow text is a big performance hurdle] and so the `Label` class doesn't support it. TIP: <> supports many lines with a single label, notice that it does carry a performance penalty for this functionality. @@ -437,7 +437,7 @@ You can use the theme constant `labelGap` which is a floating point value you ca ==== Autosizing labels -One of the common requests you received over the years is a way to let text "fit" into the allocated space so the font will match almost the width available. In some designs this is important but it's also tricky. Measuring the width of a String is a expensive operation on some OS's., there is no other way other than trial & error to find the "best size." +One of the common requests you received over the years is a way to let text "fit" into the allocated space so the font will match almost the width available. In some designs this is important but it's also tricky. Measuring the width of a String is an expensive operation on some OSes., there is no other way other than trial & error to find the "best size." Still although something is "slow" you might still want to use it for some cases, this isn't something you should use in a renderer, infinite scroll etc. and recommend minimizing the usage of this feature as much as possible. @@ -488,7 +488,7 @@ NOTE: The semantic difference between `TextField` & `TextArea` dates back to the Because it lacks the blinking cursor capability `TextArea` is often used as a multi-line label and is used internally in `SpanLabel`, `SpanButton` etc. -TIP: A common use case is to have an important text component in edit mode as you enter a `Form`. Codename One forms support this exact use case thru the https://www.codenameone.com/javadoc/com/codename1/ui/Form.html#setEditOnShow-com.codename1.ui.TextArea-[Form.setEditOnShow(TextArea)] method. +TIP: A common use case is to have an important text component in edit mode as you enter a `Form`. Codename One forms support this exact use case through the https://www.codenameone.com/javadoc/com/codename1/ui/Form.html#setEditOnShow-com.codename1.ui.TextArea-[Form.setEditOnShow(TextArea)] method. `TextField` & `TextArea` support constraints for various types of input such as `NUMERIC`, `EMAIL`, `URL`, etc. Those usually @@ -541,7 +541,7 @@ hi.add("First Name").add(firstName). .Simple text component sample image::img/components-text-component.png[Simple text component sample,scaledwidth=20%] -TIP: The <> contains a elaborate `TextField` search sample with `DataChangeListener` and rather unique styling. +TIP: The <> contains an elaborate `TextField` search sample with `DataChangeListener` and rather unique styling. ==== Masking @@ -601,7 +601,7 @@ This will adapt the icon for the action on the keys. ===== Next and done on iOS -You try to hide a lot of the platform differences in Codename One, input is **** different between OS's. A common reliance is the ability to send the "Done" event when the user presses the #Done# button. This button doesn't always exist for example: if there is an #Enter# button (due to multiline input) or if there is a #Next# button in that place. +You try to hide a lot of the platform differences in Codename One, input is **** different between OSes. A common reliance is the ability to send the "Done" event when the user presses the #Done# button. This button doesn't always exist for example: if there is an #Enter# button (due to multiline input) or if there is a #Next# button in that place. To unify the behavior you slightly customized the iOS keyboard as such: @@ -759,7 +759,7 @@ image::img/pixel-perfect-text-field-picker-ios.png[Picker component taking place .And in Android image::img/pixel-perfect-text-field-picker-android.png[And in Android,scaledwidth=30%] -The one tiny thing you should notice with the `PickerComponent` is that you don't construct the picker component using `new PickerComponent()`. Instead you use create methods such as `PickerComponent.createDate(new Date())`. The reason for that's that you've many types of pickers and it wouldn't make sense to have one constructor. +The one tiny thing you should notice with the `PickerComponent` is that you don't construct the picker component using `new PickerComponent()`. Instead you use create methods such as `PickerComponent.createDate(new Date())`. The reason for that's that you have many types of pickers and it wouldn't make sense to have one constructor. ==== Underlying theme constants and UIID's @@ -770,7 +770,7 @@ There are many related theme constants that can manipulate some pieces of this f - `textComponentErrorColor` a hex RGB color which defaults to null in which case this has no effect. When defined this will change the color of the border and label to the given color to match the material design styling. This implements the red border underline in cases of error and the label text color change - `textComponentErrorLineBorderBool` toggles the material-style underline that appears on validation errors. Set it to `false` if you prefer to supply a different border when errors are shown -- `textComponentOnTopBool` toggles the on top mode which makes things look like they do on Android. This defaults to true on Android and false on other OS's. This can also be manipulated through the `onTopMode(boolean)` method in `InputComponent`, but the layout will use the theme constant +- `textComponentOnTopBool` toggles the on top mode which makes things look like they do on Android. This defaults to true on Android and false on other OSes. This can also be manipulated through the `onTopMode(boolean)` method in `InputComponent`, but the layout will use the theme constant - `textComponentAnimBool` toggles the animation mode which again can be manipulated by a method in `InputComponent`. If you want to keep the UI static without the floating hint effect set this to false. Notice this defaults to true on Android @@ -806,7 +806,7 @@ b.addActionListener((e) -> Log.p("Clicked")); .Simple button in the iOS styling, notice iOS doesn't have borders on buttons... image::img/components-button.png[Simple button in the iOS styling, notice iOS doesn't have borders on buttons...,scaledwidth=40%] -Such a button can be styled to look like a link using code like this or by making these settings in the theme and using code such as `btn.setUIID("Hyperlink")`. +Such a button can be styled to look like a link using code like this or by making these settings in the theme and using code such as `btn.setUIID("Hyperlink")`: [source,java] ---- @@ -823,7 +823,7 @@ image::img/components-link-button.png[Button styled to look like a link,scaledwi ==== Uppercase buttons -Buttons on Android's material design UI use upper case styling which isn't the case for iOS. To solve this you've the method `setCapsText(boolean)` in `Button` which has the corresponding `isCapsText`, `isCapsTextDefault` & `setCapsTextDefault(boolean)`. This is pretty core to Codename One so to prevent this from impacting everything unless you explicitly invoke `setCapsText(boolean)` the default value of `true` will apply when the UIID is `Button`, `RaisedButton` or for the built-in `Dialog` buttons. +Buttons on Android's material design UI use upper case styling which isn't the case for iOS. To solve this you have the method `setCapsText(boolean)` in `Button` which has the corresponding `isCapsText`, `isCapsTextDefault` & `setCapsTextDefault(boolean)`. This is pretty core to Codename One so to prevent this from impacting everything unless you explicitly invoke `setCapsText(boolean)` the default value of `true` will apply when the UIID is `Button`, `RaisedButton` or for the built-in `Dialog` buttons. You also have a theme constant: `capsButtonTextBool`. This constant controls caps text behavior from the theme and is set to true in the Android native theme. @@ -831,9 +831,9 @@ You also have a theme constant: `capsButtonTextBool`. This constant controls cap Raised button is a style of button that's available on Android and used to highlight an important action within a form. To confirm with the material design UI guidelines you might want to leverage a raised button UI element on Android but use a regular button everywhere else. -First you need to know whether a raised button exists in the theme. on Android this will return true but on other OS's it will return false. A potential future update might make another platform true based on UI guidelines in other OS's. +First you need to know whether a raised button exists in the theme. On Android this will return true but on other OSes it will return false. A potential future update might make another platform true based on UI guidelines in other OSes. -For this purpose you've got the theme constant `hasRaisedButtonBool` which will return true on Android but will be false elsewhere. You can use it like this: +For this purpose you have got the theme constant `hasRaisedButtonBool` which will return true on Android but will be false elsewhere. You can use it like this: [source,java] ---- @@ -842,7 +842,7 @@ if(UIManager.getInstance().isThemeConstant("hasRaisedButtonBool", false)) { } ---- -To enable this you've 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: +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: .Raised and flat button in simulator image::img/raised-flat-buttons.png[Raised and flat button in simulator,scaledwidth=40%] @@ -883,7 +883,7 @@ Both `CheckBox` & `RadioButton` have a selected state that allows you to determi TIP: `RadioButton` doesn't allow you to "deselect" it, the way to "deselect" a `RadioButton` is by selecting another `RadioButton`. -The `CheckBox` can be added to a `Container` like any other `Component` but the `RadioButton` must be associated with a `ButtonGroup` otherwise if you've more than one set of `RadioButton's` in the form you might have an issue. +The `CheckBox` can be added to a `Container` like any other `Component` but the `RadioButton` must be associated with a `ButtonGroup` otherwise if you have more than one set of `RadioButton's` in the form you might have an issue. 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: @@ -941,7 +941,7 @@ image::img/components-toggle-buttons.png[Toggle button converted sample,scaledwi That's half the story though: to get the full effect of some cool toggle button UI's you can use a https://www.codenameone.com/javadoc/com/codename1/ui/ComponentGroup.html[ComponentGroup]. This allows you to create a button bar effect with the toggle buttons. -For example: lets enclose the `CheckBox` components in a vertical `ComponentGroup` and the `RadioButton's` in a horizontal group. You can do this by changing the last line of the code above as such: +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] ---- @@ -1004,7 +1004,7 @@ https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html[Mu NOTE: The `MultiButton` was inspired by the aesthetics of the `UITableView` iOS component. -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. +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] ---- @@ -1052,7 +1052,7 @@ Since the `MultiButton` is a composite component setting its `UIID` will impact To customize everything you need to customize the UIID's for `MultiLine1`, `MultiLine2`, `MultiLine3`, `MultiLine4` & `Emblem`. -You can customize the individual `UIID's` thru the API directly using the `setIconUIID`, `setUIIDLine1`, `setUIIDLine2`, `setUIIDLine3`, `setUIIDLine4` & `setEmblemUIID`. +You can customize the individual `UIID's` through the API directly using the `setIconUIID`, `setUIIDLine1`, `setUIIDLine2`, `setUIIDLine3`, `setUIIDLine4` & `setEmblemUIID`. Recent versions also include a badge overlay that can be rendered in the corner of the main icon. Use `setBadgeText()` to display a value (for example a notification count) and `setBadgeUIID()` if you need a custom UIID instead of the default `Badge` styling. When you need to inspect or adjust the badge style programmatically, `getBadgeStyleComponent()` returns the component whose styles are applied to the badge so you can tweak padding, colors or borders before showing the `MultiButton`. @@ -1061,7 +1061,7 @@ Recent versions also include a badge overlay that can be rendered in the corner https://www.codenameone.com/javadoc/com/codename1/components/SpanButton.html[SpanButton] is a <> that looks/acts like a `Button` but can break lines rather than crop them when the text is long. -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. +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] ---- @@ -1082,7 +1082,7 @@ https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html[Span `SpanLabel` uses the `TextArea` internally to break lines seamlessly and so doesn't provide all the elaborate configuration options of `Label`. -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. +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] ---- @@ -1130,12 +1130,12 @@ TIP: You can force the Android or iOS mode by using the theme constant `onOffIOS ==== Validation Validation is an inherent part of text input, and the https://www.codenameone.com/javadoc/com/codename1/ui/validation/Validator.html[Validator] class allows that. You can enable validation -thru the `Validator` class to add constraints for a specific component. -it's also possible to define components that would be enabled/disabled based on validation state and the way in which validation errors are rendered (change the components `UIID`, paint an emblem on top, etc.). A https://www.codenameone.com/javadoc/com/codename1/ui/validation/Constraint.html[Constraint] is an interface +through the `Validator` class to add constraints for a specific component. +It's also possible to define components that would be enabled/disabled based on validation state and the way in which validation errors are rendered (change the components `UIID`, paint an emblem on top, etc.). A https://www.codenameone.com/javadoc/com/codename1/ui/validation/Constraint.html[Constraint] is an interface that represents validation requirements. You can define a constraint in Java or use some built-in constraints such as https://www.codenameone.com/javadoc/com/codename1/ui/validation/LengthConstraint.html[LengthConstraint], https://www.codenameone.com/javadoc/com/codename1/ui/validation/RegexConstraint.html[RegexConstraint], etc. -This sample below continues from the place where the <> stopped by adding validation to that code. +This sample below continues from the place where the <> stopped by adding validation to that code: [source,java] ---- @@ -1162,7 +1162,7 @@ The https://www.codenameone.com/javadoc/com/codename1/components/InfiniteProgres TIP: This style of animation is often nicknamed "washing machine" as it spins endlessly. -`InfiniteProgress` can be used in one of two ways either by embedding the component into the UI thru something like this: +`InfiniteProgress` can be used in one of two ways either by embedding the component into the UI through something like this: [source,java] ---- @@ -1186,14 +1186,14 @@ image::img/infinite-progress.png[Infinite progress,scaledwidth=10%] The image used in the `InfiniteProgress` animation is defined by the native theme. You can override that definition either by defining the theme constant `infiniteImage` or by invoking the https://www.codenameone.com/javadoc/com/codename1/components/InfiniteProgress.html#setAnimation-com.codename1.ui.Image-[setAnimation] method. -NOTE: Despite the name of the method `setAnimation` expects a static image that will be rotated internally. don't use an animated image. +NOTE: Despite the name of the method `setAnimation` expects a static image that will be rotated internally. Don't use an animated image. === InfiniteScrollAdapter and InfiniteContainer https://www.codenameone.com/javadoc/com/codename1/components/InfiniteScrollAdapter.html[InfiniteScrollAdapter] & https://www.codenameone.com/javadoc/com/codename1/ui/InfiniteContainer.html[InfiniteContainer] allow you to create a scrolling effect that "never" ends with the typical `Container`/`Component` paradigm. -The motivation behind these classes is simple, say you've a lot of data to fetch from storage or from the internet. You can fetch the data in batches and show progress sign while you do this. +The motivation behind these classes is simple, say you have a lot of data to fetch from storage or from the internet. You can fetch the data in batches and show progress sign while you do this. Infinite scroll fetches the next batch of data dynamically as you reach the end of the `Container`. `InfiniteScrollAdapter` & `InfiniteContainer` represent two similar ways to do that task. @@ -1236,7 +1236,7 @@ IMPORTANT: The demo code here doesn't do any error handling! This is a bad pract The `fetchPropertyData` is a simplistic tool that fetches the next page of listings for the nestoria webservice. Notice that this method is synchronous and will block the calling thread (legally) until the network operation completes. -Now that you've a webservice lets proceed to create the UI. Check out the code annotations below: +Now that you have a webservice lets proceed to create the UI. Check out the code annotations below: [source,java] ---- @@ -1277,7 +1277,7 @@ InfiniteScrollAdapter.createInfiniteScroll(hi.getContentPane(), () -> { // <2> <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 +IMPORTANT: Don't violate the EDT in the callback. It's invoked on the event dispatch thread and it's crucial ==== The InfiniteContainer @@ -1367,7 +1367,7 @@ This is all generic, but a bit too much for most, doing a list correctly require A quick recap of what MVC is: - #Model# - Represents the data for the component (list), the model can tell you how many items are in it and which item resides at a given offset within the model. This differs from a simple `Vector` (or array), since all access to the model is controlled (the interface is simpler), and unlike a `Vector`/Array, the model can tell you of changes that occur within it. -- #View# - The view draws the content of the model. it's a "dumb" layer that has no notion of what's displayed and knows how to draw. It tracks changes in the model (the model sends events) and redraws itself when it changes. +- #View# - The view draws the content of the model. It's a "dumb" layer that has no notion of what's displayed and knows how to draw. It tracks changes in the model (the model sends events) and redraws itself when it changes. - #Controller# - The controller accepts user input and performs changes to the model, which in turn cause the view to refresh. .Typical MVC Diagram footnote:[Image by RegisFrey - Own work, Public Domain, https://commons.wikimedia.org/w/index.php?curid=10298177] @@ -1388,7 +1388,7 @@ Since the model is a lightweight interface, it can be implemented by you and rep 3. The is no need for state copying. Since renderers allow you to display any object type, the list model interface can be implemented by the application's data structures (for example: persistence/network engine), which would return internal application data structures saving you the need of copying application state into a list specific data structure. Note that this advantage applies with a custom renderer which is pretty difficult to get right. -4. Using the proxy pattern you can layer logic such as filtering, sorting, caching, etc. on top of existing models without changing the model source code. +4. Using the proxy pattern you can layer logic such as filtering, sorting, caching, etc. On top of existing models without changing the model source code. 5. You can reuse generic models for many views, for example: a model that fetches data from the server can be initialized with different arguments, to fetch different data for different views. View objects in different Forms can display the same model instance in different view instances, thus they would update automatically when you change one global model. @@ -1471,7 +1471,7 @@ TIP: Since the `MultiList` uses the `GenericListCellRenderer` internally you can ===== Going further with the ListModel -Lets assume that http://www.georgerrmartin.com/[GRRM] was prolific and wrote 1 million books. The default list model won't make much sense in that case but you would still be able to render everything in a list model. +Suppose that http://www.georgerrmartin.com/[GRRM] was prolific and wrote 1 million books. The default list model won't make much sense in that case but you would still be able to render everything in a list model. You will fake it a bit but notice that 1M components won't be created even if you somehow scroll all the way down... @@ -1582,7 +1582,7 @@ public Component getListFocusComponent(List list){ 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. +Now make it a bit more useful: [source,java] ---- @@ -1607,7 +1607,7 @@ Then you invoke `Label.getAllStyles().setBgTransparency(100)` to give the select That's still not efficient because you create a new `Label` each time the method is invoked. -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. +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] ---- @@ -1627,7 +1627,7 @@ class MyRenderer extends Label implements ListCellRenderer { } ---- -Now Let you've a look at a more advanced Renderer. +Now look at a more advanced Renderer: [source,java] ---- @@ -1672,7 +1672,7 @@ In this renderer you want to render a `Contact` object to the Screen, you build Notice that in this renderer you return a focus `Label` with semi transparency, as mentioned before, the focus component can be modified within this method. -For example, you can change the focus `Component` to have an icon. +For example, you can change the focus `Component` to have an icon: [source,java] ---- @@ -1828,7 +1828,7 @@ myMultiList.setRenderingPrototype(map); The https://www.codenameone.com/javadoc/com/codename1/ui/ComboBox.html[ComboBox] is a specialization of `List` that displays a single selected entry. When clicking that entry a popup is presented allowing the user to pick an entry from the full list of entries. -TIP: The `ComboBox` UI paradigm isn't as common on OS's such as iOS where there is no native equivalent to it. Recommend using either the https://www.codenameone.com/javadoc/com/codename1/ui/spinner/Picker.html[Picker] class or the https://www.codenameone.com/javadoc/com/codename1/ui/AutoCompleteTextField.html[AutoCompleteTextField]. +TIP: The `ComboBox` UI paradigm isn't as common on OSes such as iOS where there is no native equivalent to it. Recommend using either the https://www.codenameone.com/javadoc/com/codename1/ui/spinner/Picker.html[Picker] class or the https://www.codenameone.com/javadoc/com/codename1/ui/AutoCompleteTextField.html[AutoCompleteTextField]. `ComboBox` is notoriously hard to style as it relies on a complex dynamic of popup renderer and instantly visible renderer. The `UIID` for the `ComboBox` is `ComboBox` but if you set it to something else all the other `UIID's` will also change their prefix. For example: the `ComboBoxPopup` @@ -1883,7 +1883,7 @@ image::img/slider.png[Slider,scaledwidth=25%] The interesting part about the slider is that it has two separate style `UIID’s`, `Slider` & `SliderFull`. The `Slider` `UIID` is always painted and `SliderFull` is rendered on top based on the amount the `Slider` should be filled. -`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`: +`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] ---- @@ -1940,9 +1940,9 @@ TIP: This slider goes all the way to 0 stars which is less common. You can use a [[table-section]] === Table -https://www.codenameone.com/javadoc/com/codename1/ui/table/Table.html[Table] is a composite component (but it isn't a <>), this means it's a subclass of https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container]. it's effectively built from many components. +https://www.codenameone.com/javadoc/com/codename1/ui/table/Table.html[Table] is a composite component (but it isn't a <>), this means it's a subclass of https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container]. It's effectively built from many components. -TIP: `Table` is based on the https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.html[TableLayout] class. it's important to be familiar with that layout manager when working with `Table`. +TIP: `Table` is based on the https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.html[TableLayout] class. It's important to be familiar with that layout manager when working with `Table`. Here is a trivial sample of using the standard table component: @@ -2101,7 +2101,7 @@ image::img/components-table-multiline-landscape.png[Multiline table cell in land ==== Sorting tables -Sorting tables by clicking the titles is something that should work out of the box by using an API like `setSortSupported(true)`. +Sorting tables by clicking the titles is something that should work out of the box by using an API like `setSortSupported(true)`: [source,java] ---- @@ -2276,13 +2276,13 @@ IMPORTANT: `ShareButton` behaves differently on the device... image::img/components-sharebutton-android.png[The share button running on the Android device and screenshot sent into twitter,scaledwidth=50%] -IMPORTANT: The `ShareButton` features some share service classes to allow plugging in more share services. For example, this functionality is relevant to devices where native sharing isn't supported. this code isn't used on iOS/Android... +IMPORTANT: The `ShareButton` features some share service classes to allow plugging in more share services. For example, this functionality is relevant to devices where native sharing isn't supported. This code isn't used on iOS/Android... === Tabs The https://www.codenameone.com/javadoc/com/codename1/ui/Tabs.html[Tabs] `Container` arranges components into groups within "tabbed" containers. `Tabs` is a container type that allows leafing through its children using labeled toggle buttons. The tabs can be placed in many different ways (top, bottom, left or right) with the default being determined by the platform. This class also allows swiping between components to leaf between said tabs (for this purpose the tabs themselves can also be hidden). -Since `Tabs` are a `Container` its a common mistake to try and add a `Tab` using the `add` method. That method won't work since a `Tab` can have both an `Image` and text String associated with it. +Since `Tabs` are a `Container` its a common mistake to try and add a `Tab` using the `add` method. That method won't work since a `Tab` can have both an `Image` and text String associated with it: [source,java] ---- @@ -2372,7 +2372,7 @@ The `MediaManager` is the core class responsible for media interaction in Codena 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. +In the demo code below you use the gallery functionality to pick a video from the device's video gallery: [source,java] ---- @@ -2410,7 +2410,7 @@ IMPORTANT: Video playback in the simulator will work with JavaFX enabled. This i === 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 -you've a set of images (using an image list model). +you have a set of images (using an image list model). IMPORTANT: The `ImageViewer` is a complex rich component designed for user interaction. If you want to display an image use https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] if you want the image to scale seamlessly use https://www.codenameone.com/javadoc/com/codename1/components/ScaleImageLabel.html[ScaleImageLabel]. @@ -2728,7 +2728,7 @@ image::img/toolbar-search-mode.jpg[built-in toolbar search functionality,scaledw You can customize the appearance of the search bar by using the UIID's: `ToolbarSearch`, `TextFieldSearch` & `TextHintSearch`. -In the sample below you fetch all the contacts from the device and enable search thru them, notice it expects and image called `duke.png` which is the default Codename One icon renamed and placed in the src folder: +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] ---- @@ -2808,9 +2808,9 @@ This places the component below the side menu bar. Notice that this component co [[title-animations-section]] ==== Title animations -Modern UI's often animate the title upon scrolling to balance the highly functional smaller title advantage with the gorgeous large image based title. This is pretty easy to do with the Toolbar API thru the Title animation API. +Modern UI's often animate the title upon scrolling to balance the highly functional smaller title advantage with the gorgeous large image based title. This is pretty easy to do with the Toolbar API through the Title animation API. -The code below shows off an attractive title based on a book by GRRM on top of text footnote:[The text below is from A Wiki of Ice & Fire: http://awoiaf.westeros.org/index.php/A_Game_of_Thrones] that's scrollable. As the text is scrolled the title fades out. +The code below shows off an attractive title based on a book by GRRM on top of text footnote:[The text below is from A Wiki of Ice & Fire: http://awoiaf.westeros.org/index.php/A_Game_of_Thrones] that's scrollable. As the text is scrolled the title fades out: [source,java] ---- @@ -2889,7 +2889,7 @@ NOTE: The scrollbars appear in the simulator, device versions of the browser com IMPORTANT: A `BrowserComponent` should be in the center of a BorderLayout. Otherwise its preferred size might be zero before the HTML finishes loading/layout in the native layer and layout might be wrong as a result. -You can use `WebBrowser` and `BrowserComponent` interchangeably for most basic usage. For example, if you need access to JavaScript or native browser functionality then there is no use in going thru the `WebBrowser` abstraction. +You can use `WebBrowser` and `BrowserComponent` interchangeably for most basic usage. For example, if you need access to JavaScript or native browser functionality then there is no use in going through the `WebBrowser` abstraction. The `BrowserComponent` has full support for executing local web pages from within the jar. The basic support uses the `jar:///` URL as such: @@ -2933,14 +2933,14 @@ NOTE: This issue doesn't affect packaged native applications, only the desktop S ==== BrowserComponent hierarchy When Codename One packages applications into native apps it hides a lot of details to make the process simpler. One of the things hidden is the fact that you aren't dealing with a JAR anymore, so -`getResource`/`getResourceAsStream` are problematic... Both of these API's support hierarchies and a concept of package -relativity both of which might not be supported on all OS's. +`getResource`/`getResourceAsStream` are problematic... Both of these APIs support hierarchies and a concept of package +relativity both of which might not be supported on all OSes. Codename One has its own getResourceAsSteam in the https://www.codenameone.com/javadoc/com/codename1/ui/Display.html[Display] class and that works fine, but it requires that all files be in the src root directory. -TIP: that's why recommend that you place files inside res files. A resource file allows you to add arbitrary data files and you can have as many resource files as you need. +TIP: That's why recommend that you place files inside res files. A resource file allows you to add arbitrary data files and you can have as many resource files as you need. -For web developers this isn't enough since hierarchies are used often to represent the various dependencies, this means that many links & references are relative. To work with such hierarchies place all your resources in a hierarchy under the html package in the project source directory (`src/html`). The build server will `tar` the entire content of that package and add an `html.tar` file into the native package. This `tar` is seamlessly extracted on the device when you actually need the resources and with new application versions (not on every launch). assuming the resources are under the html root package they can be displayed with code like this: +For web developers this isn't enough since hierarchies are used often to represent the various dependencies, this means that many links & references are relative. To work with such hierarchies place all your resources in a hierarchy under the HTML package in the project source directory (`src/html`). The build server will `tar` the entire content of that package and add an `html.tar` file into the native package. This `tar` is seamlessly extracted on the device when you actually need the resources and with new application versions (not on every launch). Assuming the resources are under the HTML root package they can be displayed with code like this: [source,java] ---- @@ -2951,21 +2951,21 @@ try { } ---- -Notice that the path is relative to the html directory and starts with `/` but inside the HTML files you should use +Notice that the path is relative to the HTML directory and starts with `/` but inside the HTML files you should use relative (not absolute) paths. Also notice that an `IOException` can be thrown due to the process of untarring. Its unlikely to happen but is entirely possible. ==== NavigationCallback -At the core of the `BrowserComponent` you've the https://www.codenameone.com/javadoc/com/codename1/ui/events/BrowserNavigationCallback.html[BrowserNavigationCallback]. It might not seem like the most important interface within the browser but it's the "glue" that allows the JavaScript code to communicate back into the Java layer. +At the core of the `BrowserComponent` you have the https://www.codenameone.com/javadoc/com/codename1/ui/events/BrowserNavigationCallback.html[BrowserNavigationCallback]. It might not seem like the most important interface within the browser but it's the "glue" that allows the JavaScript code to communicate back into the Java layer. You can bind a `BrowserNavigationCallback` by invoking `setBrowserNavigationCallback` on the `BrowserComponent`. At that point with every navigation within the browser the callback will get invoked. IMPORTANT: The `shouldNavigate` method from the `BrowserNavigationCallback` is invoked in a native thread and **NOT ON THE EDT**! + it's crucial that this method returns and that it won't do any changes on the UI. -The `shouldNavigate` indicates to the native code whether navigation should proceed or not. For example: if a user clicks a specific link you might choose to do something in the Java code so you can return false and block the navigation. You can invoke https://www.codenameone.com/javadoc/com/codename1/ui/Display.html#callSerially java.lang.Runnable-[callSerially] to do the actual task in the Java side. +The `shouldNavigate` indicates to the native code whether navigation should proceed or not. For example: if a user clicks a specific link you might choose to do something in the Java code so you can return false and block the navigation. You can invoke https://www.codenameone.com/javadoc/com/codename1/ui/Display.html#callSerially java.lang.Runnable-[callSerially] to do the actual task in the Java side: [source,java] ---- @@ -3005,10 +3005,10 @@ NOTE: The JavaScript Bridge is implemented on top of the `BrowserNavigationCallb ==== JavaScript -TIP: The JavaScript bridge is sometimes confused with the JavaScript Port. The JavaScript bridge allows you to communicate with JavaScript from Java (and visa versa). The JavaScript port allows you to compile the Codename One application into a JavaScript application that runs in a standard web browser without code changes (think GWT without source changes and with thread support).+ +TIP: The JavaScript bridge is sometimes confused with the JavaScript Port. The JavaScript bridge allows you to communicate with JavaScript from Java (and vice versa). The JavaScript port allows you to compile the Codename One application into a JavaScript application that runs in a standard web browser without code changes (think GWT without source changes and with thread support).+ You discuss the JavaScript port further later in the guide. -Codename One 4.0 introduced a new API for interacting with Javascript in Codename One. This API is part of the `BrowserComponent` class, and effectively replaces the https://www.codenameone.com/javadoc/com/codename1/JavaScript/package-summary.html[com.codename1.JavaScript package], which is now deprecated. +Codename One 4.0 introduced a new API for interacting with JavaScript in Codename One. This API is part of the `BrowserComponent` class, and effectively replaces the https://www.codenameone.com/javadoc/com/codename1/JavaScript/package-summary.html[com.codename1.JavaScript package], which is now deprecated. ===== What was wrong with the old API @@ -3021,13 +3021,13 @@ 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. -The problem with the `ctx.get("window")` call is that it looks the same as a call to `Map.get(key)`. There’s no sign that this call is expensive and could take time. One call like this probably isn't a big deal, but it doesn't take long before you've dozens or even hundreds of calls like this littered throughout your codebase, and they can be hard to pick out. +The problem with the `ctx.get("window")` call is that it looks the same as a call to `Map.get(key)`. There’s no sign that this call is expensive and could take time. One call like this probably isn't a big deal, but it doesn't take long before you have dozens or even hundreds of calls like this littered throughout your codebase, and they can be hard to pick out. ===== The new API -The new API fully embraces the asynchronous nature of Javascript. It uses callbacks instead of return values, and provides convenience wrappers with the appropriate "AndWait()" naming convention to allow for synchronous usage. Let’s look at a simple example: +The new API fully embraces the asynchronous nature of JavaScript. It uses callbacks instead of return values, and provides convenience wrappers with the appropriate "AndWait()" naming convention to allow for synchronous usage. Let’s look at a simple example: -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]. +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] ---- @@ -3037,7 +3037,7 @@ bc.execute( ); ---- -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: +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] ---- @@ -3048,7 +3048,7 @@ The first parameter is a JavaScript expression. This JavaScript *MUST* call eith The second parameter is your callback that's executed from the JavaScript side, when `callback.onSuccess(res)` is called. The callback takes a single parameter of type https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.JSRef.html[JSRef] which is a generic wrapper around a JavaScript variable. JSRef has accessors to retrieve the value as some primitive types. For example: `getBoolean()`, `getDouble()`, `getInt()`, `toString()`, and it provides some introspection through the `getType()` method. -NOTE: it's worth noting that the callback method can take a single parameter. If you need to pass many parameters, you may consider including them in a single string which you parse in your callback. +NOTE: It's worth noting that the callback method can take a single parameter. If you need to pass many parameters, you may consider including them in a single string which you parse in your callback. ===== Synchronous wrappers @@ -3064,11 +3064,11 @@ Log.p("The result was "+res.Int()); Prints `The result was 7`. -IMPORTANT: When using the `andWait()` variant, it's critical that your Javascript calls your callback method at some point - otherwise it will block *indefinitely*. You provide variants of executeAndWait() that include a timeout in case you want to hedge against this possibility. +IMPORTANT: When using the `andWait()` variant, it's critical that your JavaScript calls your callback method at some point - otherwise it will block *indefinitely*. You provide variants of executeAndWait() that include a timeout in case you want to hedge against this possibility. ===== Multi-use callbacks -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 life on after its first use. For example: Consider the following code: +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] ---- @@ -3111,13 +3111,13 @@ bc.execute( ); ---- -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.. +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. ===== Proxy objects -The new API also includes a https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.JSProxy.html[JSProxy] class that encapsulates a Javascript object simplify the getting and setting of properties on Javascript objects - and the calling of their methods. It provides essentially three core methods, along with many variants of each to allow for async or synchronous usages, parameters, and timeouts. +The new API also includes a https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.JSProxy.html[JSProxy] class that encapsulates a JavaScript object simplify the getting and setting of properties on JavaScript objects - and the calling of their methods. It provides essentially three core methods, along with many variants of each to allow for async or synchronous usages, parameters, and timeouts. -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. +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] ---- @@ -3158,7 +3158,7 @@ location.call("replace", new Object[]{"http://www.google.com"}, ===== Legacy JSObject support -This section describes the now deprecated `JSObject` approach. it's here for reference by developers working with older code. You suggest using the new API when starting a new project. +This section describes the now deprecated `JSObject` approach. It's here for reference by developers working with older code. You suggest using the new API when starting a new project. `BrowserComponent` can communicate with the HTML code using JavaScript calls. For example: you can create HTML like this: @@ -3198,14 +3198,14 @@ Coupled with `shouldNavigate` you can effectively do everything which is what th ===== The JavaScript bridge -While it's possible to build everything on top of `execute` and `shouldNavigate`, both of these methods have their limits. that's why Codename One introduced the JavaScript package, it allows you to communicate with JavaScript using intuitive code/syntax. +While it's possible to build everything on top of `execute` and `shouldNavigate`, both of these methods have their limits. That's why Codename One introduced the JavaScript package, it allows you to communicate with JavaScript using intuitive code/syntax. The https://www.codenameone.com/javadoc/com/codename1/JavaScript/JavascriptContext.html[JavascriptContext] class lays the foundation by enabling you to call JavaScript code directly from Java. It provides automatic type conversion between Java and JavaScript types as follows: .Java to JavaScript [cols="2*",options="header"] |==== -| Java Type | Javascript Type +| Java Type | JavaScript Type | `String` | `String` | `Double/Integer/Float/Long` | `Number` | `Boolean` | `Boolean` @@ -3218,7 +3218,7 @@ The https://www.codenameone.com/javadoc/com/codename1/JavaScript/JavascriptConte .JavaScript to Java [cols="2*",options="header"] |==== -| Javascript Type | Java Type +| JavaScript Type | Java Type | `String` | `String` | `Number` | `Double` | `Boolean` | `Boolean` @@ -3247,7 +3247,7 @@ bc.setPage( "\n" + "", null); hi.add(BorderLayout.CENTER, bc); bc.addWebEventListener("onLoad", (e) -> { - // Create a Javascript context for this BrowserComponent + // Create a JavaScript context for this BrowserComponent JavascriptContext ctx = new JavascriptContext(bc); String pageContent = (String)ctx.get("document.body.innerHTML"); @@ -3257,8 +3257,8 @@ bc.addWebEventListener("onLoad", (e) -> { hi.show(); ---- -.The contents was copied from the DOM and placed in the south position of the form -image::img/components-browsercomponent-context.png[The contents was copied from the DOM and placed in the south position of the form,scaledwidth=20%] +.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`: @@ -3283,7 +3283,7 @@ bc.setPage( "\n" + "", null); hi.add(BorderLayout.CENTER, bc); bc.addWebEventListener("onLoad", (e) -> { - // Create a Javascript context for this BrowserComponent + // Create a JavaScript context for this BrowserComponent JavascriptContext ctx = new JavascriptContext(bc); JSObject jo = (JSObject)ctx.get("window"); @@ -3297,9 +3297,9 @@ This code effectively navigates to the Codename One home page by fetching the DO PhoneGap was one of the first web app packager tools in the market: a tool that's effectively a browser component within a native wrapper coupled with native access APIs, and Cordova is the open source extension of this popular project. -Codename One supports embedding PhoneGap/Cordova applications directly into Codename One applications. This is easy to do with the `BrowserComponent` and JavaScript integration. The main aspect that this integration requires is support for Cordova plugins & its JavaScript API's. +Codename One supports embedding PhoneGap/Cordova applications directly into Codename One applications. This is easy to do with the `BrowserComponent` and JavaScript integration. The main aspect that this integration requires is support for Cordova plugins & its JavaScript APIs. -The effort to integrate Cordova/PhoneGap support into Codename One is handled within an open source github project https://github.com/codenameone/CN1Cordova[here]. The chief benefits of picking Codename One rather than using Cordova directly are: +The effort to integrate Cordova/PhoneGap support into Codename One is handled within an open-source GitHub project https://github.com/codenameone/CN1Cordova[here]. The chief benefits of picking Codename One rather than using Cordova directly are: - Build Cloud - Better Native Code Support @@ -3458,7 +3458,7 @@ current.add(ac); current.show(); ---- -<1> you've duplicate arrays that are partial for clarity. This is a separate list of data element but you can fetch the more data from anywhere +<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 <2> You create the renderer UI instantly in the fields with the helper methods for wrapping elements which is pretty cool & terse <3> In a renderer it's important to always set the value if you don't have a value in place @@ -3482,7 +3482,7 @@ At this time there are 4 types of pickers: If a platform doesn't support native pickers an internal fallback implementation is used. This is the implementation you always use in the simulator so assume different behavior when building for the device. -TIP: While Android supports Date, Time native pickers it doesn't support the Date & Time native picker UX and will fallback in that case. +TIP: While Android supports Date, Time native pickers it doesn't support the Date & Time native picker UX and will fall back in that case. The sample below includes al picker types: @@ -3552,7 +3552,7 @@ The text displayed by the picker on selection is generated automatically by the A common use case is to format date values based on a specific appearance and `Picker` has built-in support for a custom display formatter. Just use the `setFormatter(SimpleDateFormat)` method and set the appearance for the field. -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. +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] ---- @@ -3581,7 +3581,7 @@ Button placement options are: The https://www.codenameone.com/javadoc/com/codename1/ui/SwipeableContainer.html[SwipeableContainer] allows you to place a component such as a https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html[MultiButton] on top of more "options" 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). +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] ---- @@ -3622,13 +3622,13 @@ The necessity for `EmbeddedContainer` came about due to iPhone inspired designs This didn't mesh well with the GUI builder navigation logic and so it needed a rethink. The aim was to reuse GUI as much as possible while still enjoying the advantage of navigation being managed for you. -Android does this with Activities and the iPhone itself has a view controller, both approaches are problematic for Codename One. The problem is that you've what's effectively two incompatible hierarchies to mix and match. +Android does this with Activities and the iPhone itself has a view controller, both approaches are problematic for Codename One. The problem is that you have what's effectively two incompatible hierarchies to mix and match. -The Component/Container hierarchy is powerful enough to represent such a UI but you needed a "marker" to show to the https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html[UIBuilder] where a "root" component exists so navigation occurs within the given "root." Here `EmbeddedContainer` comes into play, its a simple container that can contain another GUI from the GUI builder. Nothing else. you can place it in any form of UI and effectively have the UI change appropriately and navigation would default to "sensible values." +The Component/Container hierarchy is powerful enough to represent such a UI but you needed a "marker" to show to the https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html[UIBuilder] where a "root" component exists so navigation occurs within the given "root." Here `EmbeddedContainer` comes into play, its a simple container that can contain another GUI from the GUI builder. Nothing else. You can place it in any form of UI and effectively have the UI change appropriately and navigation would default to "sensible values." Navigation replaces the content of the embedded container; it finds the embedded container based on the component that broadcast the event. If you want to navigate manually use the showContainer() method which accepts a component, you can give any component that's under the `EmbeddedContainer` you want to replace and Codename One will be smart enough to replace that component. -The nice part about using the `EmbeddedContainer` is that the resulting UI can be refactored to provide a more traditional form based UI without duplicating effort and can be adapted to a more tablet oriented UI (with a side bar) again without much effort. +The nice part about using the `EmbeddedContainer` is that the resulting UI can be refactored to provide a more traditional form based UI without duplicating effort and can be adapted to a more tablet oriented UI (with a sidebar) again without much effort. === MapComponent @@ -3674,7 +3674,7 @@ 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: +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: https://developers.google.com/maps/documentation/places/ .MapComponent with Google Location API @@ -3789,8 +3789,8 @@ charts, pie charts and more. ==== Chart types -The `com.codename1.charts` package includes models and renderers for many different types of charts. it's also -extensible so that you can add your own chart types if required. The following screen shots show a small +The `com.codename1.charts` package includes models and renderers for many different types of charts. It's also +extensible so that you can add your own chart types if required. The following screenshots show a small sampling of the types of charts that can be created. .Line Charts @@ -3848,7 +3848,7 @@ For example: the colors, fonts, styles, to use. 4. **Create a https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html[ChartComponent]**. To add your chart to the UI, you need to wrap it in a https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html[ChartComponent] object. 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. +app for specific examples, but here is a high-level view of some code that creates a Pie Chart: [source,java] ---- @@ -3989,7 +3989,7 @@ 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 +// ... Some time later, clear the status... This may be before it shows at all status.clear(); ---- @@ -4001,7 +4001,7 @@ image::img/components-statusbar-multiline.png[ToastBar with a multiline message, Probably the best usage example for actions in toast is in the gmail style undo. If you aren't a gmail user then the gmail app essentially never prompts for confirmation! -It does whatever you ask and pops a "toast message" with an option to undo. if you clicked by mistake you've 3-4 seconds to take that back. +It does whatever you ask and pops a "toast message" with an option to undo. If you clicked by mistake you have 3-4 seconds to take that back. This simple example shows you how you can undo any addition to the UI in a similar way to gmail: @@ -4081,7 +4081,7 @@ image::img/components-accordion.png[The Accordion Component,scaledwidth=30%] === Floating hint -https://www.codenameone.com/javadoc/com/codename1/components/FloatingHint.html[FloatingHint] wraps a text component with a special container that can animate the hint label into a title label when the text component is edited or has content within it. +https://www.codenameone.com/javadoc/com/codename1/components/FloatingHint.html[FloatingHint] wraps a text component with a special container that can animate the hint label into a title label when the text component is edited or has content within it: [source,java] ---- @@ -4101,7 +4101,7 @@ image::img/components-floatinghint.png[The FloatingHint component with one compo The material design floating action button is a powerful tool for promoting an action within your application. -https://www.codenameone.com/javadoc/com/codename1/components/FloatingActionButton.html[FloatingActionButton] is a round button that resides on top of the UI typically in the bottom right hand side. + +https://www.codenameone.com/javadoc/com/codename1/components/FloatingActionButton.html[FloatingActionButton] is a round button that resides on top of the UI typically in the bottom right-hand side. + It has a drop shadow to distinguish it from the UI underneath and it can hide two or more actions under the surface. For example: you can create a simple single click button such as this: [source,java] @@ -4129,7 +4129,7 @@ Those familiar with this widget know that there are many nuances to this UI that ==== Using floating Button as a badge -Floating buttons can also be used to badge an arbitrary component in the style popularized by iOS/Mac OS. A badge appears at the top right corner and includes special numeric details such as `unread count.`. +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: @@ -4187,7 +4187,7 @@ private Container encloseInMaximizableGrid(Component cmp1, Component cmp2) { .Split Pane in the Kitchen Sink Demo image::img/splitpane.png[Split Pane in the Kitchen Sink Demo,scaledwidth=30%] -This is self-explanatory but "mostly." you've 5 arguments the first 3 make sense: +This is self-explanatory but "mostly." you have 5 arguments the first 3 make sense: - Split orientation - Components to split diff --git a/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc b/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc index 188932b3a2..050bc3d2bf 100644 --- a/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc +++ b/docs/developer-guide/The-EDT---Event-Dispatch-Thread.asciidoc @@ -42,7 +42,7 @@ Codename One includes helper methods in the https://www.codenameone.com/javadoc/ `callSerially(Runnable)` should normally be called off the EDT (in a separate thread), the run method within the submitted runnable will be invoked on the EDT. -IMPORTANT: The Runnable passed to the `callSerially` and `callSeriallyAndWait` methods isn't a `Thread`. use the `Runnable` interface as a convenient callback interface. +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] ---- @@ -91,7 +91,7 @@ If the work you're posting back to the EDT is expensive and can wait until the U ==== callSerially On The EDT -One of the misunderstood topics is why you'd ever want to invoke `callSerially` when you're still on the EDT. This is best explained by example. Say you've a button that has quite a bit of functionality tied to its events e.g.: +One of the misunderstood topics is why you'd ever want to invoke `callSerially` when you're still on the EDT. This is best explained by example. Say you have a button that has quite a bit of functionality tied to its events e.g.: 1. A user added an action listener to show a Dialog. @@ -234,7 +234,7 @@ All events and EDT behavior still work while `invokeAndBlock` is running, this i IMPORTANT: Notice that `invokeAndBlock` comes at a slight performance penalty. Also notice that nesting `invokeAndBlock` calls (or over using them) isn't recommended. + However, they're convenient when working with multiple threads/UI. -Even if you never call `invokeAndBlock` directly you're probably using it indirectly in API's such as https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html[Dialog] that show a dialog while blocking the current thread e.g.: +Even if you never call `invokeAndBlock` directly you're probably using it indirectly in APIs such as https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html[Dialog] that show a dialog while blocking the current thread e.g.: [source,java] ---- @@ -248,7 +248,7 @@ public void actionPerformed(ActionEvent ev) { Notice that the dialog show method will block the calling thread until the user clicks OK or Not OK... -NOTE: Other API's such as `NetworkManager.addToQueueAndWait()` also make use of this feature. Pretty much every "AndWait" method or blocking method uses this API internally! +NOTE: Other APIs such as `NetworkManager.addToQueueAndWait()` also make use of this feature. Pretty much every "AndWait" method or blocking method uses this API internally! To explain how invokeAndBlock works you can return to the sample above of how the EDT works: @@ -262,7 +262,7 @@ while(codenameOneRunning) { } ---- -`invokeAndBlock()` works in a similar way to this pseudo code: +`invokeAndBlock()` works in a similar way to this pseudo-code: [source,java] ---- diff --git a/docs/developer-guide/Theme-Basics.asciidoc b/docs/developer-guide/Theme-Basics.asciidoc index 48e3be0066..928b59ba7b 100644 --- a/docs/developer-guide/Theme-Basics.asciidoc +++ b/docs/developer-guide/Theme-Basics.asciidoc @@ -45,7 +45,7 @@ try { === Customizing your theme You can launch the designer tool by double clicking on the `theme.res` file found in typical Codename One applications. -In the left side you can see the section picker and within it the Theme section. +On the left side you can see the section picker and within it the Theme section. .The theme area in the designer image::img/theme-area-in-designer.png[The theme area in the designer] @@ -97,7 +97,7 @@ The `Title` is surrounded by a `TitleArea` container that encloses it, above the .Title Area UIID's image::img/theme-title-area-UIIDs.png[Title Area UIID's,scaledwidth=50%] -TIP: The `StatusBar` UIID is a special case that's there on iOS. In iOS the application needs to render the section under the status bar (which isn't the case for other OS's) and the `StatusBar` UIID was added so developers can ignore that behavior. +TIP: The `StatusBar` UIID is a special case that's there on iOS. In iOS the application needs to render the section under the status bar (which isn't the case for other OSes) and the `StatusBar` UIID was added so developers can ignore that behavior. ==== Background priorities and types @@ -118,7 +118,7 @@ The order for UIID settings for background is as follows: ==== The background behavior and Image -Lets start in the first page of the style entry, you will customize the background behavior for the `Title` UIID and show/explain some behaviors. +Start in the first page of the style entry, you will customize the background behavior for the `Title` UIID and show/explain some behaviors. The pictures below show the different types of background image behaviors. @@ -167,24 +167,24 @@ image::img/theme-background-image-aligned-left.png[IMAGE_ALIGNED_LEFT places the .IMAGE_ALIGNED_RIGHT places the image centered at the right part of the component image::img/theme-background-image-aligned-right.png[IMAGE_ALIGNED_RIGHT places the image centered at the right part of the component] -.IMAGE_ALIGNED_TOP_LEFT places the image at the top left corner -image::img/theme-background-image-aligned-top-left.png[IMAGE_ALIGNED_TOP_LEFT places the image at the top left corner] +.IMAGE_ALIGNED_TOP_LEFT places the image in the top left corner +image::img/theme-background-image-aligned-top-left.png[IMAGE_ALIGNED_TOP_LEFT places the image in the top left corner] -.IMAGE_ALIGNED_TOP_RIGHT places the image at the top right corner -image::img/theme-background-image-aligned-top-right.png[IMAGE_ALIGNED_TOP_RIGHT places the image at the top right corner] +.IMAGE_ALIGNED_TOP_RIGHT places the image in the top right corner +image::img/theme-background-image-aligned-top-right.png[IMAGE_ALIGNED_TOP_RIGHT places the image in the top right corner] -.IMAGE_ALIGNED_BOTTOM_LEFT places the image at the bottom left corner -image::img/theme-background-image-aligned-bottom-left.png[IMAGE_ALIGNED_BOTTOM_LEFT places the image at the bottom left corner] +.IMAGE_ALIGNED_BOTTOM_LEFT places the image in the bottom left corner +image::img/theme-background-image-aligned-bottom-left.png[IMAGE_ALIGNED_BOTTOM_LEFT places the image in the bottom left corner] -.IMAGE_ALIGNED_BOTTOM_RIGHT places the image at the bottom right corner -image::img/theme-background-image-aligned-bottom-right.png[IMAGE_ALIGNED_BOTTOM_RIGHT places the image at the bottom right corner] +.IMAGE_ALIGNED_BOTTOM_RIGHT places the image in the bottom right corner +image::img/theme-background-image-aligned-bottom-right.png[IMAGE_ALIGNED_BOTTOM_RIGHT places the image in the bottom right corner] .IMAGE_ALIGNED_CENTER places the image in the middle of the component image::img/theme-background-image-aligned-center.png[IMAGE_ALIGNED_CENTER places the image in the middle of the component] ==== The color settings -The color settings are much simpler than the background behavior. As explained <> the priority for color is at the bottom so if you've a border, image or gradient defined the background color settings will be ignored. +The color settings are much simpler than the background behavior. As explained <> the priority for color is at the bottom so if you have a border, image or gradient defined the background color settings will be ignored. .Add theme entry color settings image::img/theme-entry-color.png[Add theme entry color settings] @@ -201,7 +201,7 @@ TIP: Setting the background will have no effect unless transparency is higher th ==== Alignment -Not all component types support alignment and even when they do they don't support it for all elements. For example: a https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] and its subclasses support alignment but will apply it to the text and not the icon. +Not all component types support alignment and even when they do, they don't support it for all elements. For example: a https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] and its subclasses support alignment but will apply it to the text and not the icon. Notice that https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container] doesn't support alignment. You should use the layout manager to tune component positioning. @@ -210,7 +210,7 @@ image::img/theme-entry-align.png[Alignment of the text within some component typ WARNING: Aligning text components to anything other than the default alignment might be a problem if they're editable. The native editing capabilities might collide with the alignment behavior. -NOTE: Bidi/RtL layout reverses the alignment value so left becomes right and visa versa +NOTE: Bidi/RtL layout reverses the alignment value so left becomes right and vice versa ==== Padding and margin @@ -254,14 +254,14 @@ image::img/theme-entry-9-piece-wizard-stage1.png[Stage 1: create or pick an imag TIP: Use an image that's designed for a high DPI device -For your convenience you can create a rudimentary image with the create image stage but for a professional looking application you would want to use a design by a professional designer. +For your convenience you can create a rudimentary image with the `create image` stage but for a professional looking application you would want to use a design by a professional designer. .Stage 2: Cutting the image and adapting it to the DPI's image::img/theme-entry-9-piece-wizard-stage2.png[Stage 2: Cutting the image and adapting it to the DPI's] The second stage is probably the hardest and most important one in this wizard! -You can change the values of the top/bottom/left/right spinners to move the position of the guide lines that show the various 9 pieces. The image shows the correct cut for this image type with special attention to the following: +You can change the values of the top/bottom/left/right spinners to move the position of the guidelines that show the various 9 pieces. The image shows the correct cut for this image type with special attention to the following: - The left/right position is high enough to fit in the rounded corners in their entirety. Notice that you didn't leave 1 pixel as that performs badly, you want to leave as much space as possible! @@ -273,7 +273,7 @@ image::img/theme-entry-9-piece-wizard-stage2-bad-border.png[This is why it's imp .When the lines are close together the gradient effect grows more effectively image::img/theme-entry-9-piece-wizard-stage2-better-border.png[When the lines are close together the gradient effect grows more effectively,scaledwidth=30%] -- The elements on the right hand side include the #Generate Multi Image# options. Here you can show the density of the source image you're using (for example: if its for iPhone 5 class device pick High). You can then select in the checkboxes below the densities that should be generated automatically for you. This allows fine detail on the border to be maintained in the various high/low resolution devices. +- The elements on the right-hand side include the #Generate Multi Image# options. Here you can show the density of the source image you're using (for example: if its for iPhone 5 class device pick High). You can then select in the checkboxes below the densities that should be generated automatically for you. This allows fine detail on the border to be maintained in the various high/low resolution devices. TIP: You go into a lot of details about multi images in the advanced theming section. @@ -317,9 +317,9 @@ NOTE: Notice that the other elements in the UI are disabled when the image borde // vale-skip: write-good.Weasel — 'rarely used' is the natural phrase here; deleting 'rarely' would lose the meaning. The 9-piece border has a (rarely used) special case: 3 image mode. In this mode a developer can specify the top left corner, the top image and the center image to produce a 9 piece border. The corner and top piece are then rotated dynamically to produce a standard 9-piece border on the device. -This is useful for reducing application code size but isn't used often as it requires a more symetric UI. +This is useful for reducing application code size but isn't used often as it requires a more symmetric UI. -NOTE: don't confuse the 3-image mode for the 9-piece border with the horizontal/vertical image border below +NOTE: Don't confuse the 3-image mode for the 9-piece border with the horizontal/vertical image border below **** ==== Horizontal/Vertical Image Border @@ -362,7 +362,7 @@ image::img/round-border-theme.png[Round Border,scaledwidth=50%] The `RoundRectBorder` was developed based on the `RoundBorder` and has similar features. It produces a rounded rectangle UI. -TIP: don't confuse the Rounded Rectangle border with the deprecated `Rounded` border... +TIP: Don't confuse the Rounded Rectangle border with the deprecated `Rounded` border... It's a pretty simple border type akin to the `RoundBorder`. @@ -372,7 +372,7 @@ image::img/rounded-rectangle-border.png[Rounded Rectangle Border,scaledwidth=50% ==== Bevel/Etched borders -You recommend avoiding bevel/etched border types as they aren't as efficient and look a bit dated in todays applications. You cover them here for completeness. +You recommend avoiding bevel/etched border types as they aren't as efficient and look a bit dated in today's applications. You cover them here for completeness. .Bevel border image::img/theme-entry-border-edit-bevel.png[Bevel border] @@ -565,7 +565,7 @@ image::img/theme-font-catalog-opo.png[The same demo running on a OnePlus One dev You can define an effect to be applied to a specific font, specifically: - Underline -- Strike thru +- Strike through - 3d text raised/lowered - 3d shadow north diff --git a/docs/developer-guide/Video-Capture-Constraints.asciidoc b/docs/developer-guide/Video-Capture-Constraints.asciidoc index b00150e91c..a0a4c9d960 100644 --- a/docs/developer-guide/Video-Capture-Constraints.asciidoc +++ b/docs/developer-guide/Video-Capture-Constraints.asciidoc @@ -21,7 +21,7 @@ VideoCaptureConstraints cnst = new VideoCaptureConstraint() .preferredMaxLength(5); ---- -This constraint can then be passed to `Capture.captureVideo()` to get the captured file. +This constraint can then be passed to `Capture.captureVideo()` to get the captured file: [source,java] ---- @@ -32,7 +32,9 @@ String videoPath = Capture.captureVideo(cnst); how do you know if your constraints will be obeyed? If the platform doesn't support the max length, constraint, you may want to do something different. You can find out if a constraint is supported by asking out constraint object. -For example, [source,java] +For example: + +[source,java] ---- if (cnst.isMaxLengthSupported()) { // The max length constraint that we specified is supported on this platform. @@ -42,7 +44,7 @@ if (cnst.isMaxLengthSupported()) { // supported int effectiveMaxLength = cnst.getMaxLength(); if (effectiveMaxLength == 0) { - // Max length is not supported at all... the user will be able + // 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 @@ -70,9 +72,9 @@ VideoCaptureConstraints cnst = new VideoCaptureConstraints() .preferredHeight(240); ---- -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.. +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. +Find out if this constraint will be obeyed: [source,java] ---- @@ -108,9 +110,9 @@ if (cnst.isSizeSupported()) { | iOS | Limited | Yes | Yes | No | Android | No* | Yes | Yes | Yes | Simulator/Java SE | No | No | No | No -| Javascript (Desktop) | Yes | Yes | Yes | No -| Javascript (Android) | Yes | Yes | Yes | No -| Javascript (iOS) | Yes | Yes | Yes | No +| JavaScript (Desktop) | Yes | Yes | Yes | No +| JavaScript (Android) | Yes | Yes | Yes | No +| JavaScript (iOS) | Yes | Yes | Yes | No |==================== `*` If size is specified, the platform will try to translate to the appropriate quality constraint. diff --git a/docs/developer-guide/Working-With-Javascript.asciidoc b/docs/developer-guide/Working-With-Javascript.asciidoc index 19b522992b..84a060ba02 100644 --- a/docs/developer-guide/Working-With-Javascript.asciidoc +++ b/docs/developer-guide/Working-With-Javascript.asciidoc @@ -1,8 +1,8 @@ == Working with JavaScript -This section covers the Codename One Javascript port, which allows you to compile your app as native JavaScript and run it inside a browser. This is different from the https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html[BrowserComponent] and other methods of displaying HTML/Javascript inside a Codename One app. +This section covers the Codename One JavaScript port, which allows you to compile your app as native JavaScript and run it inside a browser. This is different from the https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html[BrowserComponent] and other methods of displaying HTML/JavaScript inside a Codename One app. -[id="limitations", reftext="Limitations of the Javascript Port"] +[id="limitations", reftext="Limitations of the JavaScript Port"] === Limitations of the JavaScript port [id="static_initializers",reftext="Static Initializers"] @@ -10,11 +10,11 @@ This section covers the Codename One Javascript port, which allows you to compil NOTE: This section pertains to Codename One 3.6 and older. Newer versions of Codename One support multithreaded code inside static initializers now. -Codename One's Javascript port uses http://teavm.org/[TeaVM] to compile your application directly to Javascript so that it can run inside modern web browsers without the need for any plugins (that is, NOT as an applet). One of the revolutionary features that TeaVM provides is the ability to run multi-threaded code (that is, it has full support for Object.wait(), Object.tell(), Object.notifyAll(), and the `synchronized` keyword). The one caveat to be aware of is that **you can't use any threading primitives inside static initializers**. This is due to technical limitations in the browser environment and the way that TeaVM compiles class definitions into Javascript. The workaround for this issue is to do lazy initialization in cases where you need to use multithreaded code. +Codename One's JavaScript port uses http://teavm.org/[TeaVM] to compile your application directly to JavaScript so that it can run inside modern web browsers without the need for any plugins (that is, NOT as an applet). One of the revolutionary features that TeaVM provides is the ability to run multi-threaded code (that is, it has full support for Object.wait(), Object.tell(), Object.notifyAll(), and the `synchronized` keyword). The one caveat to be aware of is that **you can't use any threading primitives inside static initializers**. This is due to technical limitations in the browser environment and the way that TeaVM compiles class definitions into JavaScript. The workaround for this issue is to do lazy initialization in cases where you need to use multithreaded code. **Example** -The following code will result in a build error when deploying a Javascript build: +The following code will result in a build error when deploying a JavaScript build: Class1.java @@ -39,7 +39,7 @@ class Class2 { } ---- -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. +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. If you remove the call to `Log.p()` from `getValue()`, as follows: @@ -54,21 +54,21 @@ Then everything would be fine. **But How do you Know if A method includes `wait()`/`notify` somewhere along the line?** -When you try to build your app as a Javascript app, it will fail (if code in your static initializers uses `wait()`/`notify()` somewhere along the line). +When you try to build your app as a JavaScript app, it will fail (if code in your static initializers uses `wait()`/`notify()` somewhere along the line). **How to Work Around this Issue** -Use lazy initialization wherever you can. You don't need to worry about this for setting static variables to literal values. E.g.: `static int someVal = 20;` will always be fine. But `static int someVal = OtherClass.calculateSomeVal();` may or may not be fine, because you don't know whether `calculateSomeVal()` uses a `wait/notify`. instead of initializing `someVal` in the static initializer, create a static accessor that initializes it. Or initialize it inside your app's `init()` method. Or initialize it inside the class constructor. +Use lazy initialization wherever you can. You don't need to worry about this for setting static variables to literal values. E.g.: `static int someVal = 20;` will always be fine. But `static int someVal = OtherClass.calculateSomeVal();` may or may not be fine, because you don't know whether `calculateSomeVal()` uses a `wait/notify`. Instead of initializing `someVal` in the static initializer, create a static accessor that initializes it. Or initialize it inside your app's `init()` method. Or initialize it inside the class constructor. [id="troubleshooting", reftext="Troubleshooting Build Errors"] === Troubleshooting build errors -If your Javascript build fails, you should download the error log and see what the problem is. The most common errors are: +If your JavaScript build fails, you should download the error log and see what the problem is. The most common errors are: // vale-skip: proselint.Annotations: 'XXX' is a placeholder for the actual method name inside a verbatim quote of a TeaVM error log message. 1. "[ERROR] Method XXX.()V is claimed to be synchronous, but it's has invocations of asynchronous methods" + -This error will occur if you've static initializers that use multithreaded code (for example, wait/tell/sleep, etc...). See <> for information about troubleshooting this error. Sometimes TeaVM may give a false-positive here (that is, it *thinks* you're doing some multithreaded stuff, but you're not), then you can force the build to "succeed" by adding the `javascript.stopOnErrors=false` build hint. +This error will occur if you've static initializers that use multithreaded code (for example, wait/tell/sleep, etc.). See <> for information about troubleshooting this error. Sometimes TeaVM may give a false-positive here (that is, it *thinks* you're doing some multithreaded stuff, but you're not), then you can force the build to "succeed" by adding the `javascript.stopOnErrors=false` build hint. // vale-skip: proselint.Annotations: 'XXX' is a placeholder for the actual method name inside a verbatim quote of a TeaVM error log message. 2. "Method XXX wasn't found" + @@ -77,15 +77,15 @@ TeaVM uses its own Java runtime library. It's complete, but you may occasionally [id="zip_war_preview", reftext="Deployment Formats"] === ZIP, WAR, or preview: What's the difference -The *Javascript* build target will result in up to three different bundles being generated: +The *JavaScript* build target will result in up to three different bundles being generated: 1. YourApp-1.0.war 2. YourApp-1.0.zip 3. YourApp-1.0-Preview.html -**YourApp-1.0.war** is a self contained application bundle that can be installed in any JavaEE servlet container. If you haven't customized any <>, then the application will be configured to use a proxy servlet that's embedded into the.war file. +**YourApp-1.0.war** is a self-contained application bundle that can be installed in any JavaEE servlet container. If you haven't customized any <>, then the application will be configured to use a proxy servlet that's embedded into the `.war` file. -As an example, the PropertyCross.war file contains the following files: +As an example, the `PropertyCross.war` file contains the following files: ---- $ jar tvf PropertyCross-1.0.war @@ -135,7 +135,7 @@ Some things to note in this file listing: 4. The `teavm` directory contains all the generated JavaScript for your application. Notice that there are some debugging files generated (*classes.js.map* and *classes.js.teavmdbg*). These aren't loaded by the browser when your app is run, but they can be used by Chrome when you're doing debugging. 5. The *jar* files in the *WEB-INF/lib* directory are dependencies of the proxy servlet. They aren't required for your app to run - unless you're using the proxy. -**YourApp-1.0.zip** is appropriate for deploying the application on any web server. It contains all the same files as the.war file, excluding the WEB-INF directory (that is, it doesn't include any servlets, class files, or Java libraries - it contains purely client-side JavaScript files and HTML). +**YourApp-1.0.zip** is appropriate for deploying the application on any web server. It contains all the same files as the `.war` file, excluding the WEB-INF directory (that is, it doesn't include any servlets, class files, or Java libraries - it contains purely client-side JavaScript files and HTML). As an example, this is a listing of the files in the zip distribution of the PropertyCross demo: @@ -163,30 +163,30 @@ Archive: /path/to/PropertyCross-1.0.zip 4435321 1422743 68% 15 files ---- -You'll notice that it has many of the same files as the.war distribution. it's missing the proxy servlet and dependencies. +You'll notice that it has many of the same files as the `.war` distribution. It's missing the proxy servlet and dependencies. **YourApp-1.0-Preview.html** is a single-page HTML file with all the application's resources embedded into a single page. This is generated for convenience so that you can preview your application on the build server directly. While you could use this file in production, you're probably better to use the ZIP or WAR distribution instead as some mobile devices have file size limitations that may cause problems for the "one large single file" approach. If you do decide to use this file for your production app (that is, copy the file to your own web server), you will need to change the proxy settings, as it's configured to use the proxy on the Codename One build server - which won't be available when the app is hosted on a different server. [[javascript-proxy-settings]] === Setting up a proxy for network requests -The Codename One API includes a network layer (the https://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html[NetworkManager] and https://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html[ConnectionRequest] classes) that allows you to make HTTP requests to arbitrary destinations. When an application is running inside a browser as a Javascript app, it's constrained by the same origin policy. You can make network requests to the same host that served the app originally. +The Codename One API includes a network layer (the https://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html[NetworkManager] and https://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html[ConnectionRequest] classes) that allows you to make HTTP requests to arbitrary destinations. When an application is running inside a browser as a JavaScript app, it's constrained by the same origin policy. You can make network requests to the same host that served the app originally. -For example, If your application is hosted at http://example.com/myapp/index.html, then your app will be able to perform network requests to retrieve other resources under the *example.com* domain, but it won't be able to retrieve resources from *example2.com*, *foo.net*, etc.. +For example, If your application is hosted at http://example.com/myapp/index.html, then your app will be able to perform network requests to retrieve other resources under the *example.com* domain, but it won't be able to retrieve resources from *example2.com*, *foo.net*, etc. NOTE: The HTTP standard does support cross-origin requests in the browser via the `Access-Control-Allow-Origin` HTTP header. Some web services supply this header when serving resources, but not all. The way to be make network requests to arbitrary resources is to do it through a proxy. -A solution exists. The.war JavaScript distribution includes an embedded proxy servlet, and your application is configured, by default, to use this servlet. If you intend to use the.war distribution, then it **should work**. You shouldn't need to do anything to configure the proxy. +A solution exists. The `.war` JavaScript distribution includes an embedded proxy servlet, and your application is configured, by default, to use this servlet. If you intend to use the `.war` distribution, then it **should work**. You shouldn't need to do anything to configure the proxy. -If, but, you're using the.zip distribution or the single-file preview, you will need to set up a Proxy servlet and configure your application to use it for its network requests. +If, but, you're using the `.zip` distribution or the single-file preview, you will need to set up a Proxy servlet and configure your application to use it for its network requests. ==== Step 1: Setting up a proxy -TIP: This section is relevant if you're using the.zip or single-file distributions of your app. You shouldn't need to set up a proxy for the.war distribution since it includes a proxy built-in. +TIP: This section is relevant if you're using the `.zip` or single-file distributions of your app. You shouldn't need to set up a proxy for the `.war` distribution since it includes a proxy built-in. -The easiest way to set up a proxy is to use the Codename One *https://github.com/shannah/cors-proxy[cors-proxy]* project. This is the open-source project from which the proxy in the.war distribution is derived. Download and install the cors-proxy.war file in your JavaEE compatible servlet container. +The easiest way to set up a proxy is to use the Codename One *https://github.com/shannah/cors-proxy[cors-proxy]* project. This is the open-source project from which the proxy in the `.war` distribution is derived. Download and install the `cors-proxy.war` file in your JavaEE compatible servlet container. -If you don't want to install the.war file, but would rather copy the proxy servlet into an existing web project, you can do that also. https://github.com/shannah/cors-proxy/wiki/Embedding-Servlet-into-Existing-Project[See the cors-proxy wiki for more information about this]. +If you don't want to install the `.war` file, but would rather copy the proxy servlet into an existing web project, you can do that also. https://github.com/shannah/cors-proxy/wiki/Embedding-Servlet-into-Existing-Project[See the cors-proxy wiki for more information about this]. ==== Step 2: Configuring your application to use the proxy @@ -221,18 +221,20 @@ The method you choose will depend on the workflow that you prefer. Options #1 an === Using the CORS proxy for same origin requests -By default, the CORS proxy is used for HTTP requests to URLS at a different domain than the one that the app is running in. some circumstances where you may want to *even* use the proxy for same domain requests. You can do this by setting the `javascript.useProxyForSameDomain` display property to `true`. For example, [source,java] +By default, the CORS proxy is used for HTTP requests to URLS at a different domain than the one that the app is running in. Some circumstances where you may want to *even* use the proxy for same domain requests. You can do this by setting the `javascript.useProxyForSameDomain` display property to `true`. For example: + +[source,java] ---- Display.getInstance().setProperty("javascript.useProxyForSameDomain", "true"); ---- *Why would you want to do this?* -The browser shields some HTTP headers (for example, "Set-Cookie") from Javascript so that your app can't access them. Going through the proxy works around this limitation by copying and encoding such headers in a format that the browser will allow, and then decoding them client-side to make them available to your app seamlessly. +The browser shields some HTTP headers (for example, "Set-Cookie") from JavaScript so that your app can't access them. Going through the proxy works around this limitation by copying and encoding such headers in a format that the browser will allow, and then decoding them client-side to make them available to your app seamlessly. ==== Using apache as a proxy -If you're hosting your application on an Apache 2 web server with mod_proxy installed, and you need to make CORS requests to a single domain (or a limited set of domains), you can use Apache to serve as your proxy. One sample configuration (which you would place either in your VirtualHost definition or your.htaccess file is as follows: +If you're hosting your application on an Apache 2 web server with mod_proxy installed, and you need to make CORS requests to a single domain (or a limited set of domains), you can use Apache to serve as your proxy. One sample configuration (which you would place either in your `VirtualHost` definition or your `.htaccess` file is as follows: ---- SSLProxyEngine on @@ -257,7 +259,7 @@ This tells Apache to proxy all requests for '/app' to the domain https://www.mye [id="splash_screen", reftext="Customizing the Splash Screen"] === Customizing the splash screen -Since your application may include many resource files, videos, etc.., the build-server will generate a splash screen for your app to display while it's loading. This basically shows a progress indicator with your app's icon. +Since your application may include many resource files, videos, etc., the build-server will generate a splash screen for your app to display while it's loading. This basically shows a progress indicator with your app's icon. You can customize this splash screen by modifying the HTML source inside the *cn1-splash* `div` tag of your app's index.html file: @@ -274,7 +276,7 @@ You can customize this splash screen by modifying the HTML source inside the *cn [id="debugging", reftext="Debugging in Chrome"] === Debugging -If you run into problems with your app that occur in the Javascript version, you may need to do a little bit of debugging. many debugging tools for Javascript, but the preferred tool for debugging Codename One apps is Chrome's debugger. +If you run into problems with your app that occur in the JavaScript version, you may need to do a little bit of debugging. Many debugging tools for JavaScript, but the preferred tool for debugging Codename One apps is Chrome's debugger. If your application crashes and you don't have a clue where to begin, follow these steps: @@ -287,24 +289,24 @@ If your application crashes and you don't have a clue where to begin, follow the image::img/chrome-debugger.png[Debugging using Chrome tools,scaledwidth=50%] -[id="third_party_libs", reftext="Including Third-Party Javascript Libraries"] +[id="third_party_libs", reftext="Including Third-Party JavaScript Libraries"] === Including Third-Party JavaScript libraries -Codename One allows you to interact directly with Javascript using native interfaces. Native interfaces are placed inside your project's `native/JavaScript` directory using a prescribed naming convention. If you want to, include third-party Javascript libraries in your application you should also place these libraries inside the `native/JavaScript` directory but you must specify which files should be treated as "libraries" and which files are treated as "resources." You can do this by adding a file with extension `.cn1mf.json` file either the root of your `native/JavaScript` directory or the root level of the project's `src` directory. +Codename One allows you to interact directly with JavaScript using native interfaces. Native interfaces are placed inside your project's `native/JavaScript` directory using a prescribed naming convention. If you want to, include third-party JavaScript libraries in your application you should also place these libraries inside the `native/JavaScript` directory but you must specify which files should be treated as "libraries" and which files are treated as "resources." You can do this by adding a file with extension `.cn1mf.json` file either the root of your `native/JavaScript` directory or the root level of the project's `src` directory. ==== Libraries vs resources -A *resource* is a file whose contents can be loaded by your application at runtime using `Display.getInstance().getResourceAsStream()`. In a typical Java environment, resources would be stored on the application's classpath (usually inside a Jar file). On iOS, resources are packaged inside the application bundle. In the Javascript port, resources are stored inside the `APP_ROOT/assets` directory. Historically, JavaScript files have always been treated as resources in Codename One, and many apps include HTML and Javascript files for use inside the https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html[BrowserComponent]. +A *resource* is a file whose contents can be loaded by your application at runtime using `Display.getInstance().getResourceAsStream()`. In a typical Java environment, resources would be stored on the application's classpath (usually inside a Jar file). On iOS, resources are packaged inside the application bundle. In the JavaScript port, resources are stored inside the `APP_ROOT/assets` directory. Historically, JavaScript files have always been treated as resources in Codename One, and many apps include HTML and JavaScript files for use inside the https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html[BrowserComponent]. -With the Javascript port, it isn't quite so clear whether a Javascript file is meant to be a resource or a library that the application itself uses. Most of the time you probably want Javascript files to be used as libraries, but you might also have Javascript files in your app that are meant to be loaded at runtime and displayed inside a Web View - these would be considered resources. +With the JavaScript port, it isn't quite so clear whether a JavaScript file is meant to be a resource or a library that the application itself uses. Most of the time you probably want JavaScript files to be used as libraries, but you might also have JavaScript files in your app that are meant to be loaded at runtime and displayed inside a Web View - these would be considered resources. ==== The JavaScript manifest file -To differentiate libraries from resources, you should provide a *cn1mf.json* file inside your `native/JavaScript` directory that specifies any files or directories that should be treated as libraries. This file can be named anything you like, as long as its name ends with `cn1mf.json`. Any files or directories that you list in this manifest file will be packaged inside your app's `includes` directory instead of the `assets` directory. It add appropriate ` @@ -330,7 +332,7 @@ TIP: A project may contain more than one manifest file. This allows you to inclu ===== How to NOT generate the `script` tag -Sometimes you may want a Javascript file to be treated as a library (that is, packaged in the `includes` directory) but not automatically included in the `index.html` page. Rather than specifying the name of the file in the `libs` list, you can provide a structure with multiple options about the file. For example, +Sometimes you may want a JavaScript file to be treated as a library (that is, packaged in the `includes` directory) but not automatically included in the `index.html` page. Rather than specifying the name of the file in the `libs` list, you can provide a structure with multiple options about the file. For example, ---- { @@ -352,7 +354,7 @@ In the above example, the `mylib2.js` file will be packaged inside the `includes You can also specify directories in the manifest file. In this case, the entire directory will be packaged inside the `includes` directory of your app. -WARNING: If you're including Javascript files in your app that are contained inside a directory hierarchy, you should specify the root directory of the hierarchy in your manifest file and use the sub "includes" property of the directory entry to specify which files should be included with ` ---- -WARNING: Libraries included from a directory hierarchy may not work with the single file preview that the build server generates. For that version, it will embed the contents of each included Javascript file inside the `index.html` file, but the rest of the directory contents will be omitted. If your the library depends on the directory hierarchy and supporting files and you require the single-file preview to work, then you may consider hosting the library on a separate server, and including the library directly from there, rather than embedding it inside your project's "native/JavaScript" directory. +WARNING: Libraries included from a directory hierarchy may not work with the single file preview that the build server generates. For that version, it will embed the contents of each included JavaScript file inside the `index.html` file, but the rest of the directory contents will be omitted. If your the library depends on the directory hierarchy and supporting files and you require the single-file preview to work, then you may consider hosting the library on a separate server, and including the library directly from there, rather than embedding it inside your project's "native/JavaScript" directory. ===== Including remote libraries The examples so far have demonstrated the inclusion of libraries that are part of the app bundle. But, you can also include libraries over the network by specifying the URL to the library directly. This is handy for including common libraries that are hosted by a CDN. -For example, The Google Maps library requires the Google maps API to be included. This is accomplished with the following manifest file contents: +For example, The Google Maps library requires the Google Maps API to be included. This is accomplished with the following manifest file contents: ---- { @@ -413,7 +415,7 @@ NOTE: This example uses the "//" prefix for the URL instead of specifying the pr ===== Including CSS files -CSS files can be included using the same mechanism as is used for Javascript files. If the file name ends with `.css`, then it will be treated as a CSS file (and included with a `` tag instead of a `