diff --git a/docs/TODO.md b/docs/TODO.md index d6238aca5a..9c357b9241 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -77,7 +77,6 @@ ## Advanced Topics / Under the Hood Updates - [ ] Replace the Marshmallow-era target SDK guidance in `Advanced-Topics-Under-The-Hood.asciidoc` so it documents the current Gradle 8/target SDK defaults instead of claiming the build still targets API 21 by default.【F:docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc†L681-L687】【F:maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java†L637-L663】【F:maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java†L900-L923】 -- [ ] Update the offline build prerequisites section to match today’s toolchain requirements (managed Gradle 8+, Java 17 via `JAVA17_HOME`) instead of the legacy Gradle 2.11/JDK 8 walkthrough.【F:docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc†L500-L579】【F:maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java†L80-L507】 - [ ] Refresh the Android permissions mapping to cover new runtime prompts (e.g., `POST_NOTIFICATIONS`, background location) and remove stale entries like `ACCESS_MOCK_LOCATION` so it reflects what the builder and Android implementation request now.【F:docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc†L632-L660】【F:maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java†L123-L299】【F:Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java†L8731-L8734】 ## Miscellaneous Features Chapter Updates diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index f09dc49bff..50024fc93c 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -480,152 +480,6 @@ rim.nativeBrowser true/false defaults to false. Enables the native blackberry br |Defaults to 48x48. The size of the icon in the format of width x height (without the spacing). |=== -=== Offline Build - -IMPORTANT: Offline build is an enterprise feature - -At this time Codename One supports iOS & Android targets for offline builds. We require an Enterprise grade subscription as explained in the sidebar. - -NOTE: If you signup for Enterprise and cancel you can still do the offline build. You won't be able to update the builder though - -.Why only Enterprise? -**** -There are several reasons, the technical one is that offline builds are no panacea. Things fail. The support effort for offline builds is huge, as evidence despite the fact that all of our code is open source very few people bothered trying to compile it because of the complexities. - -We don’t think building offline is convenient and we always recommended avoiding it. When we build our own apps we use the cloud just like everyone else because it’s surprisingly faster and more convenient…​ - -However, some government and regulated industries have issues with SaaS delivered solutions and thus must use offline build. These organizations also require enterprise grade support for most cases and so it makes sense to bundle as an enterprise only solution. -**** - - -==== Prerequisites for iOS Builds - -You need the following installed tools/versions for Codename One's offline build process: - -- Mac ideally with El Capitan, newer should work -- Xcode 7+ (but not 8+ at this time) -- Oracle's JDK 8 -- Cocoapods - in the terminal type `sudo gem install cocoapods --pre`. -- xcodeproj - in the terminal type `sudo gem install xcodeproj` - -==== Prerequisites for Android Builds - -Android builds need the following: - -- Android Studio -- Oracle's JDK 8 -- Gradle version 2.11 - -==== Installation - -To build offline you need to install the offline builder code which is a stripped down version of the build servers. When you install a version of the offline builder it maps to the time in which you downloaded it... - -That means that features like versioned builds won't work. You can download/keep multiple offline builders and toggle between them which is similar in scope. - -E.g. if you installed an offline builder then installed a newer version and the newer version has a bug you can revert to the old version. Notice that the older version might not have features that exist in a newer version. - -TIP: Since installation requires an enterprise account, you might need to re-login in the Codename One Settings UI - -To install an offline builder open the Codename One Settings UI by right clicking the project and selecting #Codename One# -> #Codname One Settings#. - -.Open Codename One settings -image::img/developer-guide/newsettings-ui.png[Open Codename One settings,scaledwidth=20%] - -TIP: Even though the settings are a part of a project, the offline build settings are global and apply to all the projects... - -Once the Codename One settings UI launches select the #Offline Builds# entry: - -.Offline build entry -image::img/developer-guide/offline-builds-section.png[Offline build entry,scaledwidth=20%] - -This should launch the settings UI which would be blank the first time around: - -.Offline builds setting UI -image::img/developer-guide/offline-builds-settings.png[Offline builds setting UI,scaledwidth=40%] - -When you are in this form you can press the download button to download the current version from the build server. If there is no update nothing will happen. If there is the latest version will download and tag with a version number/date. - -You can see/change the selected version in this UI. This allows building against an older version. You can also delete older builds to save space. - -==== Building - -Offline building is almost like building with the cloud. In the right click menu you can select one of the offline build targets as such: - -.The offline build targets -image::img/developer-guide/offline-build-targets.png[The offline build targets,scaledwidth=40%] - -Once selected build generates a project under the `build/and` or `build/iphone` respectively. - -Open these directories in Android Studio or xcode to run/build in the native IDE to the device or native emulator/simulator. - -WARNING: Build deletes previous offline builds, if you want to keep the sources of a build you need to move it to a different directory! - -To get this to work with Android Studio you will need one more step. You will need to configure Android studio to use your local version of gradle 2.11 by following these steps: - -- Open the Android Studio preferences - -.Android Studio Preferences -image::img/developer-guide/android-studio-preferences.png[Android Studio Preferences,scaledwidth=30%] - -- Select #Build, Execution, Deployment# -> #Build Tools# -> #Gradle# - -- Select the #Use Local gradle distribution# - -- Press the #...# and pick your local gradle 2.11 install - -.Local gradle config -image::img/developer-guide/offline-gradle-config.png[Local gradle config,scaledwidth=50%] - - -==== FAQ - -===== Should I use the Offline Builder? - -Probably not. - -Cloud build is far more convenient, simple. Doesn't require any installs (other than the plugin) and is much faster. - -We built this tool for developers who work in situations that prohibit cloud build. E.g. government, banking etc. where regulation is restrictive. - -===== Can I Move/Backup my Builders? - -No. - -We protect all the builders to avoid abuse. If you backup and restore on a new system the builders might stop working even if you are a paying enterprise customer. - -===== Can I install the builders for all our developers? - -Our licensing terms require a parallel developer seat for the Codename One developers in your company. If you have 5 Codename One developers they must all have an enterprise developer account to comply. - -E.g. You can't have one enterprise account and 4 basic accounts. - -The reason behind this is simple, in the past we saw a lot of funneling from developers who built such a licensing structure. - -===== What Happens if I Cancel? - -If you cancel your enterprise subscription all your existing installed offline builders should work as before but you won't be able to update them or get support for this. - -===== When are Versions Released? - -We will try to keep this in the same release pace as library updates i.e. once a week typically on a Friday. - -===== Are Version Numbers Sequential? - -They grow but we sometimes skip versions. Versions map to our cloud deployment versioning scheme and we might skip versions in some cases. - -===== Why is this Feature Limited to Enterprise Subscribers? - -This is a complex tool to support & maintain. SaaS has a well defined business model where we can reduce prices and maintenance costs. - -Offline builds are more like a shrinkwrap business model in which case our pricing needs to align itself to shrinkwrap pricing models for long term sustainability. - -The main use case this product tries to address is government and highly regulated industries who are in effect enterprise users. - -===== How Different is the Code From Cloud Builds? - -We use the same code as we do in the cloud build process with minor modifications in the process. Since the cloud servers are setup by us they work differently but should align reasonably well. - - === Android Permissions One of the annoying tasks when programming native Android applications is tuning all the required permissions @@ -634,36 +488,23 @@ to match your codes requirements, Codename One aims to simplify this. The build However, sometimes developers might find the permissions that come up a bit confusing and might not understand why a specific permission came up. This maps Android permissions to the methods/classes in Codename One that would trigger them. Notice that this list isn't exhaustive as the API is rather large: -`android.permission.WRITE_EXTERNAL_STORAGE` - this permission appears by default for Codename One -applications, since the `FileSystemStorage` API (which is used extensively) might have some dependencies on it. You can explicitly disable it using the build hint `android.blockExternalStoragePermission=true`, notice that this is something we don't test and it might fail on devices. - -`android.permission.INTERNET` - this is a hardcoded permission in Codename One, the ability to connect to the network is coded into all Codename One applications. - -`android.hardware.camera` & `android.permission.RECORD_AUDIO` - are triggered by com.codename1.Capture +`android.permission.WRITE_EXTERNAL_STORAGE` - included by default (up to Android 13) so file APIs continue to work unless you block it with `android.blockExternalStoragePermission=true`. -`android.permission.RECORD_AUDIO` - is triggered by usage of `MediaManager.createMediaRecorder()` & `Display.createMediaRecorder()` +`android.permission.INTERNET` - always requested because the networking stack depends on it even for offline features like local web views. -`android.permission.READ_PHONE_STATE` - is triggered by `com.codename1.ads` package, `com.codename1.components.Ads`, -`com.codename1.components.ShareButton`, `com.codename1.media`, `com.codename1.push`, `Display.getUdid()` & -`Display.getMsisdn()`. This permission is required for media in order to suspend audio playback when you get a phone call. +`android.permission.CAMERA` & `android.permission.RECORD_AUDIO` - required when capturing photos, video, or audio. The builder injects them automatically and the Android port prompts the user the first time the API is used. -`android.hardware.location`, `android.hardware.location.gps`, `android.permission.ACCESS_FINE_LOCATION`, -`android.permission.ACCESS_MOCK_LOCATION` & `android.permission.ACCESS_COARSE_LOCATION` - -map to `com.codename1.maps` & `com.codename1.location`. +`android.permission.READ_PHONE_STATE` - used by telephony-aware APIs like `Display.getMsisdn()` and by media integration so audio pauses correctly on calls. -`package.permission.C2D_MESSAGE`, `com.google.android.c2dm.permission.RECEIVE`, `android.permission.RECEIVE_BOOT_COMPLETED` - -are requested by the `com.codename1.push` package +`android.permission.ACCESS_FINE_LOCATION` & `android.permission.ACCESS_COARSE_LOCATION` - requested when using `com.codename1.location` or embedded maps so location fixes can be delivered. -`android.permission.READ_CONTACTS` - triggers by the package `com.codename1.contacts` & `Display.getAllContacts()`. +`android.permission.ACCESS_BACKGROUND_LOCATION` - added when background geofencing or fetch tasks are enabled; the Android port verifies the permission separately on Android 10+. -`android.permission.VIBRATE` - is triggered by `Display.vibrate()` and `Display.notifyStatusBar()` +`android.permission.POST_NOTIFICATIONS` - prompted on Android 13+ for both push registration and local notifications. -`android.permission.SEND_SMS` - is triggered by `Display.sendSMS()` +`package.permission.C2D_MESSAGE`, `com.google.android.c2dm.permission.RECEIVE`, and `android.permission.RECEIVE_BOOT_COMPLETED` - bundled with the push subsystem so Firebase Cloud Messaging can wake the app after device restarts. -`android.permission.WAKE_LOCK` - is triggered by `Display.lockScreen()` & `Display.setScreenSaverEnabled()` - -`android.permission.WRITE_CONTACTS` - is triggered by `Display.createContact()`, `Display.deleteContact()`, -`ContactsManager.createContact()` & `ContactsManager.deleteContact()` +`android.permission.READ_CONTACTS` - requested when accessing the device address book through `Display.getAllContacts()` and related APIs. ==== Permissions Under Marshmallow (Android 6+) @@ -675,13 +516,7 @@ This is really great as it allows apps to be installed with a single click and n ===== Enabling Permissions -Codenmae One compiles Android targets with SDK level 23 but not with target level 23! - -This means that by default the new permission mode is still off and you won't see any of the effects mentioned below. - -WARNING: This will probably change to the default in the future but at the moment the target SDK defaults to 21 - -To activate this functionality you will need to set the target SDK to level 23 by using the `android.targetSDKVersion=23` build hint. +Codename One's Gradle 8 based Android builder detects the highest Android SDK you have installed and uses that value (with a minimum of API 33) for both the compile and target SDK versions, so the modern runtime permission flow is enabled by default. If you override the target version via the `android.targetSDKVersion` build hint the builder will honour it, but lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. ===== Permission Prompts @@ -707,12 +542,12 @@ Display.getInstance().invokeAndBlock(() -> { f.show(); ---- -When we try to install this app without changing anything on an Android 6 device we see this UI: +If you explicitly lower the target SDK (e.g. `android.targetSDKVersion=21`) and install this app on an Android 6 device you will still see the legacy install prompt with all permissions listed up front: .Install UI when using the old permissions system image::img/developer-guide/marshmallow-permissions-level21.png[Install UI when using the old permissions system,scaledwidth=20%] -When we set `android.targetSDKVersion=23` in the build hints and try to install again the UI looks like this: +When you keep the default target (API 33+) the installer defers to the runtime permission flow and the install UI looks like this instead: .Install UI when using the new permissions system image::img/developer-guide/marshmallow-permissions-level23.png[Install UI when using the new permissions system,scaledwidth=20%] @@ -3795,4 +3630,3 @@ You will notice 3 big things that aren't covered in this unified framework: - Versioned builds - there is a lot of complexity in the versioned build system. This might be something we address in the future but for now I wanted to keep the framework simple. -- Offline builds - Offline builds work through manual download and aren't subjected to this framework diff --git a/docs/developer-guide/Events.asciidoc b/docs/developer-guide/Events.asciidoc index e3c9184674..2cdea4f27d 100644 --- a/docs/developer-guide/Events.asciidoc +++ b/docs/developer-guide/Events.asciidoc @@ -65,6 +65,8 @@ When an action event is fired it is given a type, however this type might change You can get the event type from https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.html#getEventType--[getEventType()], this also gives you a rather exhaustive list of the possible event types for the action event. +Modern gesture components also publish custom action types. For example, https://www.codenameone.com/javadoc/com/codename1/ui/SwipeableContainer.html[SwipeableContainer] dispatches https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.Type.html#Swipe-[ActionEvent.Type.Swipe] when the top component is fully opened, allowing code to react to swipe gestures without monitoring low-level drags. Listening for these higher level events keeps gesture handling portable across touch and desktop targets. + ===== Source Of Event `ActionEvent` has a source object, what that source is depends heavily on the event type. For most component based events this is the component but there are some nuances. @@ -149,6 +151,10 @@ r.addResponseListener((e) -> { These seem very similar but they have one important distinction. The latter code is invoked on the EDT, so if `data` is big it might slow down processing significantly. The `ConnectionRequest` is invoked on the network thread and so can process any amount of data without slowing down the UI significantly. +===== MessageEvent And The Cross-Platform Message Bus + +Bridging to native code often means passing messages between Codename One Java and platform specific code. The cross-platform message bus provides a high level abstraction for that by letting you post messages to the native layer and subscribe for messages that originate there. Use https://www.codenameone.com/javadoc/com/codename1/ui/Display.html#postMessage-com.codename1.ui.events.MessageEvent-[Display.postMessage()] to send an https://www.codenameone.com/javadoc/com/codename1/ui/events/MessageEvent.html[MessageEvent] to the platform, and https://www.codenameone.com/javadoc/com/codename1/ui/Display.html#addMessageListener-com.codename1.ui.events.ActionListener-[Display.addMessageListener()] to receive events that arrive from native code, JavaScript bridges, or background services. Message events include helpers like `isPromptForAudioRecorder()`, `isPromptForAudioPlayer()`, and `getPromptPromise()` that allow you to hook into permission prompts emitted by the JavaScript port so custom UI can respond to platform requests while keeping the event dispatch on the EDT. This API complements `NetworkEvent` by covering native-to-Java messaging without requiring direct low level callbacks. + ==== DataChangeListener The https://www.codenameone.com/javadoc/com/codename1/ui/events/DataChangedListener.html[DataChangedListener] is used in several places to indicate that the underlying model data has changed: @@ -231,6 +237,10 @@ 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 very important event for general user code. It is recommended that developers don't bind a style listener. +==== Component State Change Events + +Component instances now publish lifecycle hooks that fire when they become initialized on a form and when they are removed. You can subscribe with https://www.codenameone.com/javadoc/com/codename1/ui/Component.html#addStateChangeListener-com.codename1.ui.events.ActionListener-[Component.addStateChangeListener()] to receive https://www.codenameone.com/javadoc/com/codename1/ui/events/ComponentStateChangeEvent.html[ComponentStateChangeEvent] instances that indicate whether the component is transitioning to the initialized state. This is especially useful for running setup or teardown logic alongside focus, scroll, and selection listeners. + ==== Event Dispatcher When creating your own components and objects you sometimes want to broadcast your own events, for that @@ -314,13 +324,20 @@ public void pointerPressed(int[] x, int[] y) public void pointerPressed(int x, int y) public void pointerReleased(int[] x, int[] y) public void pointerReleased(int x, int y) +public void pointerHover(int[] x, int[] y) +public void pointerHoverPressed(int[] x, int[] y) +public void pointerHoverReleased(int[] x, int[] y) public void longPointerPress(int x, int y) public void keyPressed(int keyCode) public void keyReleased(int keyCode) public void keyRepeated(int keyCode) ---- -Notice that most pointer events have a version that accepts an array as an argument, this allows for multi-touch event handling by sending all the currently touched coordinates. +Notice that most pointer events have a version that accepts an array as an argument, this allows for multi-touch event handling by sending all the currently touched coordinates. Desktop and pen-enabled devices can also trigger hover events without a press. To respond to those you can override the `pointerHover*` callbacks on `Form` or `Component`, which are invoked before a button receives focus or a drag begins on those platforms. + +While you can override `longPointerPress`, there is usually no need. The dedicated https://www.codenameone.com/javadoc/com/codename1/ui/Component.html#addLongPressListener-com.codename1.ui.events.ActionListener-[Component.addLongPressListener()] helper wires long press detection into an action listener so you can keep gesture logic in the high-level API. + +Drag and drop lifecycles also expose a completion hook. When `Component.addDragFinishedListener()` is registered it receives https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.Type.html#DragFinished-[ActionEvent.Type.DragFinished] once the framework has completed its cleanup, allowing you to reset state or trigger follow-up actions that should only occur after the drag image is hidden. ==== Drag Event Sanitation diff --git a/docs/developer-guide/Index.asciidoc b/docs/developer-guide/Index.asciidoc index d3131d0479..e182e71484 100644 --- a/docs/developer-guide/Index.asciidoc +++ b/docs/developer-guide/Index.asciidoc @@ -35,8 +35,6 @@ When we develop an app in Codename One we use the builtin simulator when running IMPORTANT: Codename One doesn't send source code to the build cloud, only compiled bytecode! -Notice that Codename One also provides an option to build offline which means corporations that have policies forbidding such cloud architectures can still use Codename One with some additional overhead/complexity of setting up the native build tools. Since Codename One is open source some developers use the source code to compile applications offline but that's outside the scope of this book. - ==== Why Build Servers? The build servers allow building native iOS Apps without a Mac and native Windows apps without a Windows machine. They remove the need to install/update complex toolchains and simplify the process of building a native app to a right click.