Skip to content

Commit ebe0e64

Browse files
shai-almogclaudegithub-actions[bot]
authored
Run classic Android applications as Codename One applications (#5941)
* Run classic Android applications as Codename One applications An Android Studio module's src/main (manifest, res/, assets/, Java and Kotlin) placed in common/src/main/android, or imported with cn1:import-android-project, builds unmodified for every target. - maven/android-res-compiler compiles the manifest and resources at build time into R classes, a binary resource table and a generated AndroidApp that news every activity and view: no XML parsing or reflection on the device. - maven/android-compat is the android.* API written over Codename One, with AndroidX AppCompat, Fragment, Lifecycle, RecyclerView, ConstraintLayout and Material Components. - remap-android relocates android/, androidx/ and material classes so the same shim runs on Android too, maps the JDK classes CLDC lacks to shims, and generates the onClick, fragment, ViewModel and JS-bridge dispatchers. Kotlin is compiled before javac and relocated after it. - Maven goals and Gradle tasks for both steps, the archetype profile, an integration test over scripts/android-compat-samples and its workflow. - scripts/hellocodenameone compiles the gallery sample and runs 13 screenshot tests on every port. - Developer guide chapter with side-by-side captures of the gallery built natively and built with Codename One on the same emulator. Fixes the work needed outside the new modules: Form.initLaf dropping the toolbar's back command, DateFormatSymbols/SimpleDateFormat month handling, Kotlin's static wrapper hashCode/compare, JavaSE clip restore under transforms and hard-stop gradients, Maven local builds ignoring changed dependencies, and a hello test leaking the Android keyboard. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix CI and address review findings on Android compatibility CI: - The Maven plugin declares codenameone-android-compat (provided, optional), so the jobs that install "-pl codenameone-maven-plugin -am" build the runtime hellocodenameone now depends on. - SpotBugs: the Gradle remap action's lazy FileCollections join the documented EI_EXPOSE_REP2 exclusions; File gets a serialVersionUID. - PMD: Form's toolbar menu bar check compares identity on purpose. - LiveDataTest ran on the JUnit thread, which stops counting as the main thread once another test starts the EDT; MainThreadRule runs it there. - The Android interop chapter's snippets are includes: shell in the common snippets, Java in a new docs/demos/android-interop module that compiles them against the runtime and a generated AndroidAppImpl. Review: - A generated main class is deleted once the application writes its own, on unchanged-resource runs too. - Deleting assets, the last one included, clears their index and copies. - import-android-project wires the runtime, compile-android-res and remap-android into an existing project's pom, or stops naming what to add. - Kotlin compiles in process-resources, after the process-sources generators. - Three-letter locale languages (fil) match their resources. - Intent.resolveActivity answers for implicit intents the runtime handles. - LiveData.postValue hands the value to the EDT and shares no field across threads. - SharedPreferences: the last edit of a key wins. - ACTION_SEND forwards its EXTRA_STREAM file, or refuses one it cannot open. - Resuming refreshes the configuration changed in the background. - The File shim is Serializable, as the JDK class is. - Runtime extraction refuses entries outside the classes directory, and the vendored ConstraintLayout solver spells out its float narrowing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix native and JavaScript builds of Kotlin code; isolate runtime tests ParparVM mangled '$' out of class and method names but left '-', which Kotlin 1.4-1.7 put in lambda names (onCreate$lambda-1 and the classes inlined into them). The generated C read "_lambda-1__inlined_sortedBy_1" as a subtraction and did not compile on any native target. Every site that maps '$' now maps '-' too; HyphenatedNamesIntegrationTest translates and runs such names. JavaScript port: - build-javascript-port-hellocodenameone.sh copied the dependencies to a relative path Maven resolved against the module, so the Kotlin runtime never reached the translator. - LocalForage.setItem unwrapped the host call's empty answer, so every FileSystemStorage.mkdir threw. Runtime: - Letter-spaced text is measured a character at a time, as it is drawn; measured whole it was narrower wherever the font kerns, and the browser clipped every Material label. - A Dialog host is put back without a second modal loop, which blocked finish() forever. - Notification action intents are pinned to the application's package (CodeQL implicit PendingIntent). Tests: every runtime test runs on the EDT (MainThreadRule), and activity tests start from a clean display. A fragment test executing transactions on the JUnit thread raced the EDT, Codename One's error dialog became the next test's host form, and finish() hung the plugin test job. Docs: the side-by-side images quote their alt text. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Refresh every edited constructor's inlining plan after dead field elimination DeadFieldElimination collected the methods it edited in a HashSet, but BytecodeMethod.equals compares name and signature only -- it serves override matching -- so of two edited constructors with the same signature in different classes only one was kept and re-planned. The other's call sites kept inlining a store to the field just removed, and the generated C assigned a struct member that no longer existed: the Windows build of the hello suite failed on WindowsWindowManager.Peer.windowId once the Android compatibility gallery added another (int, int) constructor with a write-only field. The set is now by identity. DeadFieldInlinedConstructorIntegrationTest builds and runs two such constructors. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Recapture the Android interop side-by-side images Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Memoize the static accessor fold so its include is never missing optimize() drops CHECKCAST in place, so Kotlin's emptyList() body (GETSTATIC; CHECKCAST; ARETURN) became foldable only after its class was emitted. A later caller folded the call to the field read while the dependency scan had not added the field owner's header, and clang rejected the undeclared get_static_ accessor on the Windows target. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Bind the JavaScript port's static interop stubs JSString.valueOf, JSNumber.valueOf, JSBoolean.valueOf, Int32Array.create, Int16Array.create and Window.encodeURIComponent had Java bodies that return null and no port.js binding, and the JSO bridge never dispatches static members. JSString.valueOf answering null made every LocalForage.setItem(String, String) a delete, so FileSystemStorage.mkdir never created a directory and a write under a new one failed. A node gate derives every stub's binding name and fails when one is unbound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for Android and JavaScript Taken from the CI captures of 9c45a32b. The JavaScript Data screen is left out: it captured the file-system failure the interop binding fix addresses. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Return LocalForage values from the bridge without a cast A stored string, such as the empty directory marker that mkdir now really writes, crosses back as a java.lang.String; the JavaScript VM checks casts, so the cast to JSObject threw ClassCastException out of every isDirectory. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for the Windows port Taken from the cross-built Windows capture of c011d073. The action bar is absent by design: in the desktop native title-bar mode the title and the commands go to the OS window and its menu bar. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for the Linux port Taken from the x64 and arm64 captures of c011d073 (the two are identical). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Unwrap JavaScript strings without casting them to JSString The bridge hands every JS string to Java as a java.lang.String, whatever type was declared, and the JavaScript VM checks casts. Assigning a key of LocalForage.keys() to a JSString threw, keys() swallowed it and answered no keys, so FileSystemStorage.listFiles was always empty on this port. LocalForage.getString and JS.unwrapCharArray cast the same way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the JavaScript AndroidCompatData golden Taken from the CI capture of 4fec0846, the first round in which the JavaScript port lists the files directory. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline hello's translation memory on the Windows AMD runners hello now carries the Android compatibility gallery, the relocated runtime and the Kotlin stdlib, so translating it holds more classes. Measured on two AMD runs (0.93x, 1.02x); the Intel row stayed within tolerance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Recalibrate hello's Windows AMD memory row from three runs The third AMD run measured 1.19x on unchanged code: JDK 25's footprint stays near its heap-policy floor while ParparVM's follows hello's larger live set, and JDK's own rounds swing to 1.6-2 GB. Median and spread of the three runs: 1.025, tolerance 25%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline hello's translation memory on Windows arm64 too Same cause as the AMD row: one arm64 run measured 1.17x (rounds 0.84-1.27, JDK 25 swinging 961-1387 MB). One run cannot estimate its own spread, so the row takes the 25% the AMD row needed, as calibrate does for new rows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for iOS (Metal, Xcode 27) and Mac Catalyst Taken from the CI captures of 35421349. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five Android compatibility runtime review findings - ActivityThread: a result-producing child finishing while its caller has a pending relaunch now re-points the finished record's caller at the recreated instance, so onActivityResult is no longer dropped. - SQLiteOpenHelper: onBeforeDelete receives the obsolete database while it is still open, before it is closed, deleted and recreated, as on Android. The delete goes through the context, the way the database was opened. - SQLiteDatabase: transaction nesting is per thread (ThreadLocal stack). A second thread beginning a transaction while another thread's is open on the single engine connection gets SQLiteDatabaseLockedException instead of silently nesting into it; no locks are added. - Dialog: the Codename One host routes any dispose the wrapper did not ask for through it -- an outside tap cancels, anything else dismisses -- so cancel/dismiss listeners, onStop() and detach run and isShowing() clears. - Looper: the main looper is bound lazily to the EDT (prepareMainLooper, called before an activity starts, and any main-looper lookup on the EDT), so getMainLooper().getThread() is no longer null. Each fix has a test that fails without it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Match intent filters whole, name assets collision-free, compare resources by bytes Intent filters: the resource compiler flattened every activity <intent-filter> to its action strings, and activityForAction() picked the first activity with the action before the platform got a turn, so an ACTION_VIEW filter for a custom scheme captured every browser, dialer and share intent. The compiler now keeps each filter whole (actions, categories, schemes, host/port, path and ssp patterns, MIME types) and generates IntentFilter calls; the runtime's IntentFilter implements Android's match() rules and a new android.os.PatternMatcher reproduces Android's simple glob. activityForIntent() adds CATEGORY_DEFAULT as implicit starts do and picks the most specific match. A filter using a constraint the runtime cannot evaluate (advanced patterns, mimeGroup, resource references) is dropped with W0304 rather than kept without it. Assets: flat names used String.hashCode() of the path, so Aa/file.txt and BB/file.txt shared one output file. They now use a SHA-256 prefix of the whole path, and two assets claiming one (case-folded) name fail the build. Remap: a runtime-jar resource was re-extracted only when its length changed, so a rebuilt resource table of the same length kept the stale copy. Resources now get the same byte comparison as classes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline hello's translation memory on Linux x64 (EPYC 7763) hello now translates the Android compatibility gallery with its relocated runtime and the Kotlin stdlib, so ParparVM's peak RAM ratio rose from 0.84x to 1.02x on the EPYC 7763 runners, as it already did on Windows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Pin the light appearance for the AndroidCompat screenshots The runtime answers night mode from CN.isDarkMode(). The tvOS simulator defaults to dark, so the gallery resolved its values-night card colour (#303030) under the light theme's dark text and AndroidCompatMain captured an unreadable row. Pin light for the capture and restore the host setting afterwards, so the baselines no longer depend on the host's appearance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for watchOS, tvOS and macOS From run 37173812053 (watch, tv) and 37173811895 (macos) at b0f0836a00. macOS has no action bar (the desktop native title bar takes the form title). tvOS AndroidCompatMain is left out: it was captured in dark mode, which the previous commit pins away. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four Android compatibility review findings, document two pushbacks - Bundle.deepCopy() copies nested bundles, lists and arrays instead of sharing them through the shallow copy constructor. - Uri.normalizeScheme() lower-cases the scheme (ASCII fold, not the locale-sensitive toLowerCase()). - SharedPreferences: edit().clear() reports every key it removed to the change listeners. - Resource matching keeps the script of b+ locale qualifiers, so values-b+zh+Hans and values-b+zh+Hant are chosen by the device's script (explicit, or implied by a Chinese region). The locale parser no longer reads a script segment as the language or the region. - Intent.getExtras() stays a copy: Android's is new Bundle(mExtras). - SQLiteStatement.executeInsert() gets no lock: the finding's premise is two threads sharing one database; single-threaded use is exact. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Test that a theme refresh keeps a toolbar form's back command Form.initLaf replaced any menu bar whose class was not the look and feel's, and a Toolbar's ToolbarSideMenu never is, so every form of a global-toolbar application lost its menu bar -- and with it the back command the hardware back key runs -- on refreshTheme(), on a system dark mode change (UIManager.refreshNativeThemeSettings), and on show() under a theme without form transitions. The guard added earlier on this branch fixes that in core; FormToolbarThemeRefreshTest fails without it and the comment now says which ordinary paths it covers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Clip paths within the clip, rotate bitmap pixels, keep spans in subsequences - Canvas.clipPath intersects the path with the current clip when it reaches outside it, instead of replacing the clip. - Bitmap.createBitmap with a rotating, mirroring or skewing matrix samples the source through the inverse matrix (bilinear when filtering); plain positive scales still use the port's scaling. - Handler.postAtFrontOfQueue / sendMessageAtFrontOfQueue go ahead of due work on both the main and background loopers. - WebView.canGoBackOrForward answers false for distances the browser cannot confirm (more than one entry). - SpannableString/SpannableStringBuilder subsequences keep their spans, clipped and offset; SpannableStringBuilder.subSequence no longer recurses into itself. - ViewGroup: documented why the intercepting event only cancels the child. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Seed the AndroidCompat goldens for iOS Metal and the tvOS main screen All 13 iOS Metal references and the tvOS AndroidCompatMain reference come from run 37203007638's ios-ui-tests-metal and tv-ui-tests artifacts. The tvOS capture is now light, because the test pins the light appearance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline hello's RAM ratio on the AMD family 26 Windows x64 runner This is the same cause as the other rows: the Android compatibility gallery makes hello's translation input larger. The value comes from run 37203007339's perf-results.json. The memory tolerance is widened to 0.25, as it already is on the other single-run Windows rows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings, document one pushback - LifecycleRegistry: dispatch over a snapshot, so an observer removing itself no longer makes the next one miss the event. - ActivityThread: onRestoreInstanceState and onPostCreate run after onStart, in the first start of a created activity. - BackStackRecord: a self-replacement on the back stack uncounts the fragment it no longer references; a primary navigation change counts the previous primary fragment the pop uncounts. - SupportFragmentManager: pending fragment results are saved with the platform manager's state and restored into the recreated facade. - View: every view posts through one main-thread handler, so removeCallbacks cancels what post queued. - GroupPeer: a root paint dispatches the tree observer's pre-draw and draw listeners; a cancelling pre-draw schedules another pass. - SavedStateViewModelFactory: comment why the owner is not kept (no process death; relaunch always carries the view model store). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings - ResourceManager keeps a three-digit UN M49 region (es_419), so values-b+es+419 can match; it was cut to "41". - Uri.fromFile keeps an existing file: scheme instead of producing file://file:///... for context directories. - PluralRules gives Persian Hindi's "i = 0 or n = 1" rule (0 and 1 are ONE). - Intent.cloneFilter copies the package and categories. - File.getCanonicalPath resolves . and .. segments, so containment checks cannot be bypassed with root/../outside. - Gradle relocates Kotlin's classes in cn1RemapAndroidKotlin, a task that finalizes compileJava and runs every build; as a compileJava action it never ran when javac was up to date (an ABI-unchanged Kotlin edit, or the build right after a relocation). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three Android compatibility review findings, document two pushbacks - AndroidProjectImporter matches a dependency's whole group:artifact against the covered list; prefix matching reported androidx.core:core-splashscreen as covered by androidx.core:core. kotlin-stdlib-jdk7/jdk8 are listed explicitly so they stay covered. - Parcel writes at the current position and advances it, overwriting a value already there, so a Parcelable can reserve a size slot and seek back to fill it in. - SQLiteDatabase opened OPEN_READONLY refuses writes through the Android API (execSQL, insert, update, delete, setVersion, compiled statements) with SQLiteReadOnlyDatabaseException; reads, pragmas and transactions still run. - SharedPreferences commit and SQLiteOpenHelper first-time open stay unsynchronized: both findings assume concurrent threads, which the single-threaded runtime does not guard. Explained in code comments. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix seven Android compatibility review findings - Broadcast receivers match their whole IntentFilter (data, type, categories), consistent with activity filters, not the action alone. - getLaunchIntentForPackage answers null for any package but this one. - getDrawableForDensity picks and scales image variants for the requested density instead of the device's. - A child finished below the top of the stack delivers its result to its caller, queued until the caller's next resume. - Window focus changes are dispatched through the decor view tree, so a covered activity's views report hasWindowFocus() false. - Single-line TextView and TextUtils.ellipsize honour START and MIDDLE. - The resource compiler leaves out activities declared android:enabled="false". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Chain the Windows x64 family 25 hello rebaseline on #5940's #5940 merged a noise recalibration of windows-x64@amd64-family-25 hello/all (time 1.221, time tolerance 0.45) from the same row this pull request rebaselines. perf_baseline.resolve refuses two rebaselines from one value, which failed test_perf_gate.py and the baseline check in every perf-gate job (Linux and Windows suites, screenshot-capture, check, build). Name #5940's result as this row's "from" and keep its time and time tolerance, so the rebaseline only raises the memory ratio this pull request measured. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings, document one pushback - EditText: mutations through the live Editable (clear, replace, append) now reach TextWatchers; the change hook reports the replaced range. - Bitmap.compress: WebP requests fail instead of writing PNG bytes. - BitmapFactory: outMimeType is read from the data's signature. - ObjectAnimator: a naturally finished auto-cancel animator leaves the static registry (and no longer pins its target). - Looper.quitSafely: messages already due are delivered before exit. - Path.transform: the current point and contour start are mapped too. - RecyclerView child drawing order: documented as not applied, consistent with the runtime ignoring every per-child paint order. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings, document one pushback - Importer: a repeated import keeps the existing .pre-android-import backup instead of overwriting the original main class with the generated one. - PackageManager: resolveActivity/queryIntentActivities fill activityInfo name and package from the component the runtime would start. - ContentResolver: file: URIs keep their authority (file://home/... on sandboxed ports) when opening storage streams. - BufferedReader shim: mark/reset supported across refills, up to the read-ahead limit. - android:launchMode reaches the runtime: singleTop reuses the top instance, singleTask/singleInstance clear onto the existing one and deliver onNewIntent (one task, no affinity). - SharedPreferences: committing an unchanged value notifies no listener. - Resources formatting with the resource locale: pushed back in a code comment; the device String.format is locale independent and no String.format(Locale, ...) exists in JavaAPI or CLDC11. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix two Android compatibility review findings, document one pushback - A plain FLAG_ACTIVITY_CLEAR_TOP launch of a standard-mode activity now finishes the existing instance and creates a new one, as Android does; only single-top and single-task targets are reused through onNewIntent. - Path.addRoundRect honours Direction.CCW, so an inner CCW rounded rectangle cuts a hole in a CW contour under the non-zero rule. - Intent.getExtras() keeps returning a copy: AOSP returns new Bundle(mExtras) too; the comment now says so unambiguously. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Diff the perf-baseline check against the merged base; recalibrate hello RAM on Windows AMD family 26 perf-baseline's check diffed `pull_request.base.sha...HEAD`, but CI checks out the pull request merged into the current base branch while base.sha is not refreshed when that branch moves. Everything master gained since -- here #5940's pr/5940.json -- read as this pull request writing another one's overlay. On a merge commit whose first parent descends from the given base, the first parent is now the base. The family 26 Windows x64 hello RAM row was set from one run (0.879x); the next measured 0.62x with JDK 25's peak swinging 1.2-1.9 GB between rounds. Recalibrated from both runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings, document one pushback - A finishing activity is no longer asked to save its state; destroy() marks it finishing before pause/stop, as Android does. - Uri.getQueryParameters keeps a literal '+', as Android's does; getQueryParameter still reads it as a space (documented Android behaviour, so that half of the finding's premise does not hold). - MessageQueue.removeIdleHandler now cancels pending and future runs. - AbsSpinner refreshes the selected row id after a data change that keeps the selected position. - An android:theme on <include> themes the included tree and overrides the included root's own theme. - The Android importer XML-escapes the values it injects into the copied manifest (a versionName like "1.0 & beta"). - Pushback, documented in ActivityThread: explicit-component and setPackage() packages are not checked, because the runtime's package is the manifest namespace rather than the Gradle applicationId. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five Android compatibility review findings - Matrix.mapPoints walks backwards when an in-array destination overlaps the source further along, so no point is mapped from an already mapped one. - SpannableStringBuilder.replace copies the inserted text's spans, so append(Spanned) and TextUtils.concat keep their styling. - ViewGroup hit-tests and dispatches touches through the inverse of a child's scale/rotation about its pivot (legacy animations stay excluded, as on Android). - Canvas.save/restore keeps a clipPath shape instead of its bounds. - Canvas.drawColor(..., CLEAR) erases the clip of a bitmap canvas with Graphics.clearRect; on a view canvas it stays a no-op. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three Android compatibility review findings - MessageQueue: a background looper's idle handlers run on that looper's thread, once each time its queue runs out of due messages, instead of on the EDT. Looper keeps one queue (getQueue() used to return a new one per call, so removal through a second call did nothing) and gains myQueue(). - Handler: a message sent to a background looper after quit() or quitSafely() is refused and not retained, as on Android. - LayoutInflater: children of a <merge> root (included or top level) join the parent without calling its onFinishInflate(), which ran early and twice. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Recalibrate hello RAM on Windows x64 AMD family 25 from eight runs The row was set from three runs at 1.025x +-25%; the merge with master's hybrid GC measured 0.94x and 0.74x on two consecutive runs of the same code, and earlier runs ranged 0.93x-1.19x. Recalibrated from all eight family-25 runs: 0.938x +-45%. Time is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings, document one pushback - A no-history activity is finished when another activity covers it, so finishing the new one returns to the activity beneath. - The back key dispatches ACTION_UP after ACTION_DOWN, so a handler that claims the down and acts in onKeyUp runs. - Dialog.isShowing() is false after hide(). - Unchecking the checked RadioButton clears the group's checked id. - Fragment result launchers register when the fragment is created, under a request code derived from the fragment's identity, so a result returning to a recreated activity reaches the new fragment. - An item added to a filtered ArrayAdapter shows only when the active constraint keeps it. - AbstractCursor keeps AOSP's answer for an empty cursor (both before first and after last); documented in place. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six Android compatibility review findings - SharedPreferences commit() returns the storage write's result instead of always true. - Bitmap.createBitmap of a mutable source (full-size crop, plain copy) and copy(config, false) snapshot the image instead of aliasing it. - TextUtils.split evaluates its separator as a regular expression through Codename One's RE, keeping trailing empty strings; plain separators keep the literal fast path. - LengthFilter backs the cut off a high surrogate, as Android does. - Writing to Bundle.EMPTY throws UnsupportedOperationException. - The Android project importer records what it copied (path and SHA-256) and a re-import removes recorded files the project no longer has, keeping any file the developer added or edited. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five Android compatibility review findings - SharedPreferences: a commit consumes the editor's batch, so a reused editor neither re-applies old values nor repeats an earlier clear(). - Uri.decode: an unescaped surrogate pair is UTF-8 encoded whole instead of as two replacement characters. - recreate()/config relaunch destroys without finishing: isFinishing() stays false in onDestroy, as on Android. - A reused singleTop/singleTask activity keeps its launch intent; the new one reaches onNewIntent only. - Canvas drawing into a bitmap drops the bitmap's cached pixel array, and follows the image a later setPixel replaced, so reads and writes see the drawing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix two Android compatibility review findings Gradle read the android:onClick names from build/android-res/onclick.txt, but compileAndroidRes hands build/android-res to the runner as its build directory and the runner writes android-res/onclick.txt beneath it, so every Gradle build generated no onClick handlers. Both plugins now take the path from AndroidResourceRunner.onClickNamesFile. A re-import overwrote the generated entry point unconditionally once the .pre-android-import backup existed, losing a developer's customization. It is now kept, as an edited imported file is. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline hello RAM on Linux x64 AMD EPYC 9V74 The first run of this pull request on that runner model measured hello's RAM ratio at 0.96x against master's 0.79x; the larger translation input is the same cause recorded for the other runner models. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline objectAllocation on the Linux x64 AMD EPYC 9V74 runner #5940's hybrid GC made objectAllocation about 4x faster and #5940 rebaselined it on other runner models, but not on the 9V74, so a pull request landing there failed as IMPROVED. Calibrated on top of master. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Give the iOS Metal screenshot jobs room for the larger hello suite The hello suite now compiles the Android compatibility gallery, its runtime and the Kotlin stdlib. build-ios-metal-27 took 55-76 minutes on this branch against 54 on master and timed out at its 75-minute limit mid-compile. Raise both Metal jobs to 95 minutes and their UI-test step to 80. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four Android compatibility review findings - TextView.setText runs the InputFilter chain over the new text, so android:maxLength and custom filters bind programmatic and XML text. - A TextView with watchers keeps an editable buffer, so afterTextChanged is delivered (setText and append), as on Android. - ImageView pushes a cleared color filter / tint (null) to its drawable once the view has set one, instead of leaving the stale value. - ProgressBar draws a configured indeterminate drawable over its content box, sweeping its level like Android does. setOnClickListener(null) still makes the view clickable: that is what Android itself does; the reason is recorded beside the code. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four Android compatibility review findings - MessageQueue.isIdle answers from a background looper's queue: a due message makes it busy, a later one does not. The main looper still answers true; its messages are per-handler EDT callbacks with no single queue to ask, and idle work belongs in addIdleHandler. - Uri.getEncodedQuery returns null for an opaque URI (the ? in a mailto is part of its scheme-specific part) and for a ? inside the fragment. - Bitmap.setPixel, setPixels and eraseColor throw IllegalStateException on an immutable or recycled bitmap, as on Android; an immutable bitmap can be the same object another createBitmap call returned. - AbsListView cancels the press and pending long press when the pointer leaves the list by more than the touch slop, and a release beside the list clicks nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three Android compatibility review findings - CLEAR_TOP finishes the activities it removes: a result they owe (RESULT_CANCELED by default) reaches their caller, as Android does. - A GetContent launcher's gallery result goes through the registration map, so a launcher unregistered while the picker is open hears nothing. - Implicit resolution asks the same question startActivity() does, so a resolveActivity() guard no longer passes a dial without a number or a chooser without an intent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - GridView STRETCH_SPACING_UNIFORM shares the leftover among n + 1 gaps for a single column too, centring it instead of pushing it to the edge. - A layout-direction change re-resolves the start/end padding of every descendant that inherits the direction, and a view joining a parent resolves its relative padding against that parent. - A ViewGroup's visibility change reaches every descendant's onVisibilityChanged, as on AOSP. - ValueAnimator.reverse() on a repeated animator mirrors against the end of the last iteration (or within the current one when infinite), so the value carries over instead of snapping to an endpoint. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - A singleTop activity reused at the top is paused before onNewIntent() and resumed after it, as Android does. - Dialog.cancel() notifies its OnCancelListener once until the next show(). - RippleDrawable restores the child's own color filter after the pressed overlay instead of clearing it. - InsetDrawable.setDrawable() brings the replacement up to the wrapper's visibility, state, level and inset bounds, and invalidates. - ImageView.getImageMatrix() keeps returning the draw matrix, which is what AOSP returns; documented why. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rebaseline objectAllocation RAM on the AMD EPYC 9V45 #5940's hybrid GC rebaselined objectAllocation's time on this runner model but carried the pre-hybrid RAM ratio (0.363) forward. The run on this PR measured 0.24x (rounds 0.22-0.30), failing the Linux x64 perf gate as IMPROVED. Every AMD x64 model measured since #5940 shows the same drop (EPYC 7763 -21%/-25%, 9V74 -7% to -22%), so record the measured ratio as a PR overlay row, the way objectAllocation's time was recorded for the 9V74. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five more Android compatibility review findings - A read-only database refuses PRAGMAs stored in the file (user_version, journal_mode, ...); connection-local ones still run. - A recycled bitmap refuses pixel reads, copy and compress. - Private-file names refuse a backslash, which the File shim treats as a separator. - new Handler() on a thread without a looper throws, as on Android, instead of binding to the main looper. - An unchanged incremental build regenerates the entry point after the application deletes its own main class. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Show the Playground smoke host on the EDT and fail fast on a failed check HarnessSupport.context() called show() from the main thread, which only queues the form switch, so smokeFormShowIsCaptured raced the EDT and failed whenever a slow frame was still painting. A failed check then left the JavaSE port's non-daemon threads running until the CI watchdog killed the JVM 15 minutes later; main now exits non-zero instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix six more Android compatibility review findings - rawQuery and simple queries on an OPEN_READONLY database go through the same read-only check as statements; WITH-led statements read unless they contain a data-changing keyword. - Database names refuse a backslash, like file names. - getStringExtra answers null for a non-String extra. - A detached View has no handler and holds posted work until it is attached. - SystemClock reads the monotonic System.nanoTime(); input events are stamped in the uptime base. - A finishing child's result is queued so the stopped caller is restarted before onActivityResult. - Clarify the push-back comments on SharedPreferences commits, SQLiteOpenHelper initialization and intercepted touch dispatch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - ActivityThread: FLAG_ACTIVITY_REORDER_TO_FRONT moves the existing instance to the top (onNewIntent, activities above it kept) instead of stacking a duplicate. - ActivityThread: an activity recreated for a configuration change keeps FLAG_ACTIVITY_NO_HISTORY. - ComponentActivity: a contract's synchronous result is posted to the main queue, as AndroidX does, rather than delivered inside launch(). - ArrayAdapter: insert() under an active filter places the item after its displayed predecessors, not at the unfiltered index. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five more Android compatibility review findings - Canvas.drawText covers the width measureText reports when textScaleX is not 1: glyphs are stretched where the graphics can transform, and their advances are scaled elsewhere. - SharedPreferences.getString/getStringSet throw ClassCastException for a present value of another type, as on Android and as the other getters. - SharedPreferences.apply updates memory at once and writes once later through the main thread's queue; a commit in between writes everything. - BitmapDrawable honours its target density (the screen's with Resources, 160 without, or setTargetDensity) for intrinsic size, gravity and tiles; a bitmap's density now defaults to the device's, as on Android. - File.renameTo fails, and takes back a copy it created, when the source of a cross-directory move cannot be deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - TextView: BufferType.SPANNABLE text is kept as a SpannableString. - Uri: getBooleanQueryParameter reads "false" in any case as false (equalsIgnoreCase, no locale-sensitive folding). - CookieManager: cookies set with Expires or Max-Age survive removeSessionCookies()/removeSessionCookie(). - SimpleAdapter: the filter keeps rows whose bound values, or a word of them, start with the constraint, and an empty one restores every row. - CancellationSignal: documented why the state stays unsynchronized. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three more Android compatibility review findings - BitmapFactory: outWidth/outHeight are written before the density scaling, where AOSP's decoder writes them, so a bounds probe over a resource reports the image's own (sampled) size. inSampleSize keeps dividing exactly: AOSP fine-scales to width / inSampleSize rather than rounding to a power of two, despite the Options javadoc. - Canvas: drawBitmap refuses a recycled bitmap, as BaseCanvas does. - Matrix: IDENTITY_MATRIX is immutable; every mutator throws. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix Path.addPath(this) loop; document Android-matching Rect/LayerDrawable/MatrixCursor behaviour Path.addPath with itself as the source read operations it was appending and never terminated; snapshot the source first. Rect.union(x, y), LayerDrawable.findDrawableByLayerId and MatrixCursor.RowBuilder.add(name) already match AOSP exactly, so they keep that behaviour with a comment saying why. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five more Android compatibility review findings - TextView: NORMAL setText keeps an immutable snapshot through a real TextUtils.stringOrSpannedString, so later edits of a caller's builder no longer change getText() behind filters, watchers and layout. - View: the background tint mode is stored on the view and applied to replacement backgrounds, like the tint list. - Message.obtain(Message) copies replyTo and gives the copy its own Bundle. - SpannableString/SpannedString/SpannableStringBuilder equality and hash include spans and only match other Spanned values (symmetric with String). - SpannedString no longer extends SpannableString; both share a package-private SpannableStringInternal, so SpannedString is not Spannable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three more Android compatibility review findings - Paint.getTextWidths and breakText include letter spacing in each character's advance, matching measureText and Canvas.drawText. - Matrix.setValues refuses a non-affine third row instead of storing perspective terms that mapping, concatenation, inversion and drawing all ignored. - Bitmap getPixel/setPixel/getPixels/setPixels validate each coordinate against its own dimension, as Android does, instead of indexing into a neighbouring row of the flattened pixel array. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - View.getGlobalVisibleRect/getLocalVisibleRect cut the view's bounds to every ancestor's on-screen box and the display, so a scrolled-out or overhanging child is no longer reported fully visible. - setKeepScreenOn is held per view and aggregated over attached views; one view clearing it (or detaching) no longer re-enables the screen saver while another still asks, and getKeepScreenOn reports the flag. - TextView keeps start/end compound drawables relative and re-resolves them from the new View.onRtlPropertiesChanged hook when the layout direction changes, directly or by inheritance. - ViewTreeObserver keeps window-focus listeners and the root view notifies them from dispatchWindowFocusChanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Composite translucent saveLayer offscreen; clip rotated rects as shapes; honour drawPaint shader saveLayer/saveLayerAlpha with a translucent alpha now draw into an offscreen image the size of the clip and composite it once on restore, so overlapping draws inside the layer no longer darken each other. Where the context already carries a transform, the alpha falls back to per-draw. clipRect under a rotation or skew clips to the transformed quadrilateral through clipPath instead of its bounding box. drawPaint with a shader fills the clip through drawRect, so a LinearGradient is no longer a solid fill. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three more Android compatibility review findings - The import replaces the copied manifest's package with the Gradle namespace: the Android Gradle plugin generates R there whatever the manifest says, and the copied tree has no build script left to say so. - A re-import keeps an imported file (the manifest included) the developer edited since the earlier import, warning instead of overwriting it, as stale removal and the entry point already do. - The generated ViewModelFactory tries every supported constructor in AndroidX's preference order and takes the first the calling factory can supply, so NewInstanceFactory reaches the no-argument constructor of a class that also declares an injected one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five more Android compatibility review findings - SQLiteDatabase: a statement or query from another thread while one thread's transaction is open is refused with SQLiteDatabaseLockedException, as beginTransaction already was; it ran inside that transaction on the shared connection. - Uri.fromFile percent-encodes the path (all but '/'), so '#' and '?' in a file name no longer become a fragment or query. - WebView.loadDataWithBaseURL reports the page by historyUrl, also when navigating back to it. - SpannableStringBuilder copies spans from any Spanned source. - SharedPreferences holds change listeners weakly, as Android does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five more Android compatibility review findings - Carry the manifest's android:noHistory through the resource compiler into ActivityInfo, so it means the same as FLAG_ACTIVITY_NO_HISTORY. - A no-history activity finished by being covered still returns its result (RESULT_CANCELED unless it set one), on both the start and the REORDER_TO_FRONT paths. - A gallery result reaches the instance that replaced the activity when it was recreated while the gallery was open. - sendBroadcast snapshots the intent, so reusing it afterwards does not change the queued broadcast. - AlertDialog.setMessage updates the message view of a built dialog. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - Bitmap.createBitmap(DisplayMetrics, ...) keeps the metrics' density. - BitmapDrawable applies each axis's tile mode on its own: REPEAT tiles, MIRROR flips every other tile, an unset or CLAMP axis stretches the bitmap's edge pixels instead of tiling. - GradientDrawable runs diagonal orientations corner to corner; Canvas fills a diagonal LinearGradient through the port's angled gradient and honours reversed axis-aligned endpoints. - ObjectAnimator.setProperty renames the animator and its values holder after the new property. - DatePicker.setSpinnersShown: pushed back in a code comment; there is no calendar view to swap in, so hiding the wheels would leave it empty. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - Menu XML android:onClick: the resource compiler records menu item handler names (menu: prefix in the onClick names file), the remapper generates OnClickDispatch.dispatchMenu for name(MenuItem) methods returning boolean or void, and inflation installs a listener that dispatches through it -- no reflection, same path for Maven and Gradle. - IntentFilter.match: a null action skips the action test, as on Android. - Uri.Builder: authority, path and query setters clear the opaque part. - Html.fromHtml: numeric entities above U+FFFF decode to surrogate pairs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three more Android compatibility review findings - File.renameTo onto the same (canonical) path succeeds without copying; the copy path truncated and then deleted the file. - BufferedWriter rejects write/flush after close and a second close is a no-op, so post-close data never reaches the underlying writer. - Letter-spaced text draws, measures and breaks a surrogate pair as one character instead of two lone surrogates with spacing between them. - SQLiteDatabase: document why the transaction owner stays a plain field (single-threaded runtime; a stale read degrades to the unchecked case). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - Back key: Activity's default onKeyDown only starts tracking and the default onKeyUp navigates on a tracked, uncanceled up, as on Android. ActivityThread.onBack marks the up tracked when the down asked for it, so an onKeyUp-only override sees the key and a down claimed without tracking still blocks back. - startActivity snapshots the Intent, so a caller mutating it afterwards does not reach getIntent() or onNewIntent(). - Hiding a view clears the focus of it or its focused descendant. - EditText keeps the requested selection range and applies one requested before the native field exists. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four more Android compatibility review findings - Bitmap: every derived bitmap (copy, crop, scale, matrix transform, extractAlpha) keeps the source density, as AOSP does. - BitmapFactory: an unscaled decode reports the source density rather than the target's (AOSP setDensityFromOptions). - ImageView: adjust-view-bounds and the maximum sizes request a layout when they change, so an already laid out view re-measures. - Color: parseColor accepts darkgrey like AOSP; transparent stays rejected because Android rejects it too (comment in code). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix three more Android compatibility review findings - Activity saves and restores the view hierarchy across recreation: View/ViewGroup dispatch save/restore by id, EditText (and TextView with freezesText) keep their text and selection, CompoundButton its checked state; AdapterView and RecyclerView freeze only themselves, as on Android, so recycled rows sharing an id are not all restored from one. - A fragment detached while its started activity runs still gets the result; only a fragment removed for good drops it. - A child removed mid-gesture gets ACTION_CANCEL before it is unlinked. - SQLiteDatabase keeps the transaction owner in an AtomicReference, claimed before BEGIN and released after COMMIT/ROLLBACK, so another thread's statement is refused reliably for the whole transaction. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Declare each Maven plugin and dependency once, which Maven 3.10 requires The ubuntu-24.04 runner image 20261004 moved from Maven 3.9.16 to 3.10.0, and 3.10 fails model building on a plugin or a dependency declared twice in one list; 3.9 only warned and merged them. The Windows cross-build job died before compiling anything, on maven/backend (build-helper twice) and maven/cn1-admob/android (antrun twice). The plugins' executions are merged into their first declaration, which is the effective model 3.9 already built. The javase poms of the archetype and the in-tree apps declared codenameone-core and codenameone-javase at both test and provided scope; 3.9 kept the later, provided one, so the test entries go. The archetype one matters most: every project generated from it would fail to build on Maven 3.10. The initializr and playground templates inside common.zip carry the same pair but their generators already strip one of them before writing a project, so they are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Recalibrate hello RAM on Windows arm64 from eleven runs The row was set from one run at 1.167x; this pull request's eleven runs on the Family 8 Model D49 runner put the median between 0.82x and 1.20x, with rounds clustering near 0.8x and near 1.2x, and two of them failed as IMPROVED. The baseline is now the median of those runs and the tolerance their spread. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Give vm-tests room for the larger hello translation CleanTargetIntegrationTest takes about 2450s on this branch against 1411s on master because hello now includes the Android compatibility runtime and the Kotlin stdlib, and the job hit its 90-minute limit seconds after every test passed. Raise it to 120. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Close buffered streams for good and fix two TextUtils gaps A closed BufferedReader or BufferedInputStream kept serving the data it had already buffered; both now drop the buffer on close and throw "Stream closed" from every later read, as the JDK classes do. TextUtils.split with an empty expression returned the whole text instead of one string per character plus a trailing empty one, and commaEllipsize cut the list mid-item instead of keeping whole items and appending the oneMore/more count suffix as Android does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix four Android compatibility widget review findings - ProgressBar: setMin/setMax clamp the secondary progress too (and the primary even on an indeterminate bar), so a shrunk range no longer leaves it past the end of the track. - TimePicker: a null passed to setCurrentHour/setCurrentMinute leaves the time alone instead of resetting it to midnight. - SimpleAdapter: the default text and integer-image bindings go through setViewText and setViewImage(ImageView, int), so subclass overrides apply. - Spinner: dialog mode lists the adapter's getDropDownView rows in a single-choice ListView instead of flattening items to toString(). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix five Android compatibility review findings - BufferedOutputStream rejects write and flush after close, like the buffered reader, writer and input stream. - AlertDialog keeps a custom title view and shows it in place of the title row (AppCompat's builder delegates to it). - DatePicker tells its listeners when setMinDate/setMaxDate moves the selection to the new bound. - BitmapFactory answers bounds-only requests for PNG, JPEG and GIF from the encoded header instead of decoding every pixel. - Canvas refuses an immutable bitmap target with IllegalStateException. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix dividers, foreground gravity, scroll margins and seek capture Weighted LinearLayout passes now add the divider space back into the total length, so BOTTOM/CENTER gravity no longer pushes children past the edge, and horizontal layouts draw the ending divider they reserve. View honours a non-fill foreground gravity (FrameLayout delegates to it). ScrollView and HorizontalScrollView include the child's margins in the scroll range and every clamp. AbsSeekBar inside a scrolling container waits for horizontal movement past the touch slop before claiming the gesture; LinearLayout and GridLayout report that they never scroll. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Keep spans in AllCaps and hand selector tints to every child InputFilter.AllCaps returned a plain String for Spanned input, dropping every span in the inserted range; it now returns a SpannableString with the source spans copied, as Android does. StateListDrawable forwarded only the tint list to its children; the tint mode stayed on the container, which never paints. It now keeps tint list, tint mode, alpha and color filter and applies them to existing children and to states added later. AbsSpinner.setSelection(INVALID_POSITION) keeps the old selection; that matches Android, whose Spinner/Gallery layout adopts only a next position >= 0, so it is documented in code rather than changed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Redial the JS screenshot sink after the server sheds its idle socket VideoIODecodedFrames intermittently failed on the JavaScript leg with websocket-unavailable and screenshot-missing_actual. Most browser tests ship through port.js's own socket, so the Java-side async sink's socket sits unused from LottieAnimated until VideoIODecodedFrames. The CI test server sheds a websocket idle past CN1_WS_IDLE_TIMEOUT_MS with a bare close (browser close code 1006, logged as ws-sink-unavailable reason=closed:1006 tests earlier in both failing runs), and the sink latched FAILED on any close, refusing every later send. The async state machine moves into Cn1ssAsyncSink, which names no Codename One API: a socket that worked and then drops returns to IDLE and is redialled by the next send, an unacknowledged send is resent once on the new socket, stale callbacks from a replaced socket are ignored by generation, and consecutive failed dials stay bounded so a dead server still fails the run. The fidelity app shares the same copy. scripts/ci/tests/cn1ss-async-sink-test.sh drives it against a fake socket on a plain JVM (and fails if the two copies drift); it runs in scripts-javascript.yml ahead of the build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Navigate Up to the manifest parent and honour two Android contracts - The JS-bridge dispatcher skips a subclass that overrides an @JavascriptInterface method without the annotation, as Android only exposes methods annotated on the runtime class. - startActivityIfNeeded returns false, delivering nothing, when the intent would only reach the calling top activity again. - android:parentActivityName (or the android.support.PARENT_ACTIVITY meta-data) is compiled into AndroidAppImpl; onNavigateUp and onSupportNavigateUp go to that parent, recreate it when it is not on the stack, and do nothing when there is none. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: raise iOS packaging and native-ios limits for the larger hello app The hello app now also translates the Android compatibility gallery, its runtime and the Kotlin stdlib. On 5f80b8356a the cold Release device compile took 42.5 of its 45 minutes (master: 19-25), the UI smoke's -O1 compile alone took 52 minutes (master: 33) so the 65-minute step timed out, and native-ios was still compiling when its 45-minute job limit fired -- a limit master's own push runs already hit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Reconcile list checks by id, close PrintWriter, mirror view drawables - AbsListView: with stable ids, checked rows follow their ids across notifyDataSetChanged() and removed ids are dropped; without them a check past the new end is dropped, so getCheckedItemIds() no longer reads past the adapter. - PrintWriter: close() drops the delegate like the JDK, so later writes are suppressed and reported by checkError() even over a writer whose close is a no-op; a second close does nothing. - View: the background and foreground take the view's resolved layout direction when attached and on every direction change, direct or inherited. - AndroidPomUpdater: the compile-android-res execution is only inserted into the Codename One plugin's own executions block, never a later plugin's; otherwise it is reported for a manual update. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Define Android compatibility release limits and fix retained text and state * Fix Android compatibility lifecycle and UI review findings and guide lint * Preserve Android form state and fix sharing, grid rows, and deleted spans * Preserve Android widget state and defer cosmetic compatibility gaps * Fix Android launch reentrancy, task reset, and localized month parsing * Observe Playground animation startup within a bounded interval * Clear weak references before reclaiming fresh legacy arrays * Fix Android review regressions and copyright CI gate * Fix modal cleanup race and bound Android review scope * Fix Android review regressions and resume reattached GPU rendering * Fix lifecycle review issues and stabilize CI validation * Fix compatibility regressions and isolate GC benchmarks * Fix URI, list, clock, and toast review findings * Guard reentrant Android callbacks and snapshot queued results * Calibrate expanded hello RAM on Windows Model 173 * Record Linux translator RAM improvement on EPYC 9V74 * Wake continuous WebGL surface after first layout * Fix reentrant Android compatibility callbacks and chooser dispatch * Use guide prose conventions for restoration limitation * Keep native foreach layouts within surviving classes Avoid emitting HashMap field accesses when culling leaves only a standalone HashSet. Cover that exact generated-code dependency and preserve map-backed view behavior. Record two five-round Windows Model 207 recursion measurements against its obsolete two-run baseline without changing tolerances. * Handle reentrant Android callbacks and stale asynchronous deliveries * Exclude watchOS from iOS hinge implementation * Guard Android result delivery and matching edge cases * Guard Android canvas targets and reentrant UI state --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 07e3d75 commit ebe0e64

1,355 files changed

Lines changed: 191362 additions & 517 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Android compatibility
2+
3+
# Classic Android applications built as Codename One applications: the resource
4+
# compiler (maven/android-res-compiler), the android.* runtime
5+
# (maven/android-compat) and the build steps that compile, relocate and package
6+
# them (build-engine, the Maven and Gradle plugins, the archetype profile).
7+
#
8+
# unit tests the compiler's encoding and qualifier rules,
9+
# the runtime's table reader, the remapper and
10+
# the project importer
11+
# android-compat-test.sh every sample under scripts/android-compat-samples
12+
# imported unmodified, built by Maven and Gradle,
13+
# checked for unrelocated android.* references,
14+
# and staged for iOS, Android, JavaScript and the
15+
# desktop with identical uploads from both tools
16+
on:
17+
pull_request:
18+
branches: [ master ]
19+
paths:
20+
- 'maven/android-compat/**'
21+
- 'maven/android-res-compiler/**'
22+
- 'maven/build-engine/**'
23+
- 'maven/codenameone-maven-plugin/**'
24+
- 'maven/codenameone-gradle-plugin/**'
25+
- 'maven/project-model/**'
26+
- 'maven/cn1app-archetype/**'
27+
- 'maven/pom.xml'
28+
- 'maven/integration-tests/android-compat-test.sh'
29+
- 'maven/integration-tests/android-gradle-incremental-test.sh'
30+
- 'maven/integration-tests/inc/**'
31+
- 'scripts/android-compat-samples/**'
32+
- '.github/workflows/android-compat.yml'
33+
push:
34+
branches: [ master ]
35+
paths:
36+
- 'maven/android-compat/**'
37+
- 'maven/android-res-compiler/**'
38+
- 'maven/build-engine/**'
39+
- 'maven/codenameone-maven-plugin/**'
40+
- 'maven/codenameone-gradle-plugin/**'
41+
- 'maven/project-model/**'
42+
- 'maven/cn1app-archetype/**'
43+
- 'maven/pom.xml'
44+
- 'maven/integration-tests/android-compat-test.sh'
45+
- 'maven/integration-tests/android-gradle-incremental-test.sh'
46+
- 'maven/integration-tests/inc/**'
47+
- 'scripts/android-compat-samples/**'
48+
- '.github/workflows/android-compat.yml'
49+
50+
concurrency:
51+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
52+
cancel-in-progress: true
53+
54+
jobs:
55+
android-compat:
56+
runs-on: ubuntu-24.04
57+
timeout-minutes: 60
58+
env:
59+
CN1_GRADLE_REQUIRED: '1'
60+
steps:
61+
- uses: actions/checkout@v6
62+
# Installed FIRST so the JDK 8 below is the one JAVA_HOME names.
63+
- name: Set up JDK 17 for Gradle
64+
uses: actions/setup-java@v5
65+
with:
66+
java-version: '17'
67+
distribution: 'temurin'
68+
- run: echo "GRADLE_JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV
69+
- name: Set up JDK 8
70+
uses: actions/setup-java@v5
71+
with:
72+
java-version: '8'
73+
distribution: 'temurin'
74+
cache: 'maven'
75+
# xvfb for the CSS compiler, which opens an AWT frame.
76+
- name: Install xvfb
77+
run: bash scripts/ci/apt-get-install.sh xvfb
78+
- name: Build Codename One Maven artifacts (includes archetypes and both plugins)
79+
env:
80+
RETRY_ONLY_MATCHING: transient
81+
RETRY_ATTEMPTS: '5'
82+
RETRY_DELAY_SECONDS: '60'
83+
run: |
84+
cd maven
85+
bash ../scripts/ci/retry.sh xvfb-run -a mvn install -Plocal-dev-javase -DskipTests -Darchetype.test.skip=true -Dmaven.javadoc.skip=true
86+
- name: Unit tests
87+
working-directory: maven
88+
run: xvfb-run -a mvn -B -Plocal-dev-javase -pl android-res-compiler,android-compat,build-engine test
89+
- name: Refresh archetype catalog
90+
run: |
91+
cd maven
92+
xvfb-run -a mvn archetype:update-local-catalog -Plocal-dev-javase
93+
xvfb-run -a mvn archetype:crawl -Plocal-dev-javase
94+
- name: Android samples, imported and built by Maven and Gradle
95+
working-directory: maven/integration-tests
96+
run: xvfb-run -a bash android-compat-test.sh
97+
- name: Upload the test projects' logs
98+
if: failure()
99+
uses: actions/upload-artifact@v7
100+
with:
101+
name: android-compat-logs
102+
path: maven/integration-tests/build/android-compat/**/*.log
103+
if-no-files-found: ignore

‎.github/workflows/ios-packaging.yml‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ jobs:
3030
# sequence. On a cold runner all tests passed after 119m44s, leaving only
3131
# 16s of the old two-hour budget for artifact upload and cache saves. Keep
3232
# the individual step limits below and allow 30m of finalization headroom.
33-
timeout-minutes: 150
33+
# The hello app now also translates the Android compatibility gallery, its runtime and the Kotlin stdlib; 150 no longer covers the three builds.
34+
timeout-minutes: 210
3435
# Exercises both CocoaPods and SPM in a single Xcode project. Catches
3536
# regressions in either dependency manager (each pathway runs end to end)
3637
# and additionally validates that they coexist correctly. Also turns on
@@ -163,7 +164,8 @@ jobs:
163164
./scripts/run-ios-device-release-build.sh \
164165
"${{ steps.build_ios_app.outputs.workspace }}" \
165166
"${{ steps.build_ios_app.outputs.scheme }}"
166-
timeout-minutes: 45
167+
# A cold Release compile of the hello app (now with the Android compatibility gallery, its runtime and the Kotlin stdlib) took 42.5 of 45 minutes.
168+
timeout-minutes: 70
167169

168170
# The binary half of scripts-ios.yml's private-API gate. App Store Connect scans the
169171
# linked executable, so this reads what it would read: every import of the Release
@@ -219,7 +221,8 @@ jobs:
219221
"${{ steps.build_ios_app.outputs.scheme }}"
220222
# run-ios-ui-tests.sh has a 12-minute no-progress watchdog and a
221223
# bounded 35-minute suite cap; include native build/cleanup headroom.
222-
timeout-minutes: 65
224+
# The cold -O1 compile alone reached 52 minutes once the hello app gained the Android compatibility gallery, its runtime and the Kotlin stdlib.
225+
timeout-minutes: 100
223226

224227
- name: Run native iOS notification tests
225228
env:
@@ -247,8 +250,8 @@ jobs:
247250
# So the higher of the two, not either alone: the watch build is added work on top of the
248251
# rebuild, not a re-description of it. The real fix is to inject before the UI-smoke step
249252
# so this build is genuinely incremental; until then the budget has to cover the build it
250-
# actually does.
251-
timeout-minutes: 45
253+
# actually does. The hello app's growth (the Android compatibility gallery, its runtime and the Kotlin stdlib) added roughly half again to that build.
254+
timeout-minutes: 60
252255

253256
- name: Upload packaging artifacts
254257
if: ${{ !cancelled() }}

‎.github/workflows/parparvm-tests.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ jobs:
177177
vm-tests:
178178
runs-on: ubuntu-24.04
179179
# The full suite takes about an hour before the bounded GC benchmark arms.
180-
# Leave time for both; each benchmark workload retains its own timeout.
180+
# Android compatibility and Kotlin translation add time; leave room for
181+
# both phases while each GC workload retains its own timeout.
181182
timeout-minutes: 120
182183
# The two database engines the backend speaks a wire protocol to.
183184
#

‎.github/workflows/pr.yml‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ on:
4040
- 'scripts/check-native-cpp-linkage.py'
4141
# Same: the builder define-toggle lint runs from this workflow and nowhere else.
4242
- 'scripts/check-builder-define-toggles.py'
43+
- 'scripts/check-cast-semantics.sh'
44+
- 'scripts/cast-semantics-baseline.txt'
4345
# The build hint gates are run from this workflow and nowhere else, and one
4446
# of them holds an empty baseline. Ignoring the whole directory meant a
4547
# change that breaks a gate, or that adds a line to the baseline, could
@@ -108,6 +110,8 @@ on:
108110
- 'scripts/check-native-cpp-linkage.py'
109111
# Same: the builder define-toggle lint runs from this workflow and nowhere else.
110112
- 'scripts/check-builder-define-toggles.py'
113+
- 'scripts/check-cast-semantics.sh'
114+
- 'scripts/cast-semantics-baseline.txt'
111115
# The build hint gates are run from this workflow and nowhere else, and one
112116
# of them holds an empty baseline. Ignoring the whole directory meant a
113117
# change that breaks a gate, or that adds a line to the baseline, could
@@ -387,7 +391,7 @@ jobs:
387391
-Dcn1.binaries="${CN1_BINARIES}" \
388392
-Pcompile-android \
389393
-DbackendAnalysis \
390-
-pl android,ios,project-model,build-engine,codenameone-maven-plugin,codenameone-gradle-plugin,backend,backend-test,backend-parparvm,javac -am verify
394+
-pl android,ios,project-model,build-engine,codenameone-maven-plugin,codenameone-gradle-plugin,backend,backend-test,backend-parparvm,javac,android-res-compiler,android-compat -am verify
391395
- name: Type-check the SQLCipher-backed database package
392396
if: ${{ matrix.java-version == 8 }}
393397
env:
@@ -602,7 +606,7 @@ jobs:
602606
- name: Generate static analysis HTML summaries
603607
if: ${{ !cancelled() && matrix.java-version == 8 }}
604608
env:
605-
QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target
609+
QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target
606610
QUALITY_REPORT_SERVER_URL: ${{ github.server_url }}
607611
QUALITY_REPORT_REPOSITORY: ${{ github.repository }}
608612
QUALITY_REPORT_REF: ${{ github.event.pull_request.head.sha || github.sha }}
@@ -622,6 +626,8 @@ jobs:
622626
"bytecode-translator:vm/ByteCodeTranslator/target" \
623627
"maven-plugin:maven/codenameone-maven-plugin/target" \
624628
"build-engine:maven/build-engine/target" \
629+
"android-res-compiler:maven/android-res-compiler/target" \
630+
"android-compat:maven/android-compat/target" \
625631
"project-model:maven/project-model/target" \
626632
"gradle-plugin:maven/codenameone-gradle-plugin/target" \
627633
"backend:maven/backend/target" \
@@ -752,11 +758,11 @@ jobs:
752758
- name: Generate quality report summary
753759
if: ${{ !cancelled() && matrix.java-version == 8 }}
754760
env:
755-
QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target
761+
QUALITY_REPORT_TARGET_DIRS: maven/core-unittests/target:maven/android/target:maven/ios/target:vm/ByteCodeTranslator/target:maven/codenameone-maven-plugin/target:maven/build-engine/target:maven/project-model/target:maven/codenameone-gradle-plugin/target:maven/build-hint-catalog/target:maven/build-hint-tools/target:maven/backend/target:maven/backend-test/target:maven/backend-parparvm/target:maven/javac/target:maven/android-res-compiler/target:maven/android-compat/target
756762
# Every project listed here must produce a SpotBugs report and that
757763
# report must be empty. A missing report fails the gate too, so a
758764
# SpotBugs run that silently stops running is not mistaken for clean.
759-
QUALITY_REPORT_REQUIRED_SPOTBUGS: core-unittests:android:ios:ByteCodeTranslator:codenameone-maven-plugin:build-engine:project-model:codenameone-gradle-plugin:build-hint-catalog:build-hint-tools:backend:backend-test:javac
765+
QUALITY_REPORT_REQUIRED_SPOTBUGS: core-unittests:android:ios:ByteCodeTranslator:codenameone-maven-plugin:build-engine:project-model:codenameone-gradle-plugin:build-hint-catalog:build-hint-tools:backend:backend-test:javac:android-res-compiler:android-compat
760766
# PMD and Checkstyle run on the core and on the backend runtime, and each
761767
# must produce its report: without one, the forbidden-rule gate below it
762768
# has nothing to read and passes.

‎.github/workflows/scripts-ios-native.yml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ jobs:
2929
# Allow setup plus the separately bounded 30-minute generation and 45-minute
3030
# XCTest build/run steps. A 45-minute job cap canceled a progressing native
3131
# build after only 34 minutes of the XCTest step in run 37728629000.
32+
# The hello app also translates the Android compatibility gallery and Kotlin stdlib.
3233
timeout-minutes: 90
3334
steps:
3435
- uses: actions/checkout@v6
@@ -134,7 +135,8 @@ jobs:
134135
# scheme carries an embedded watch app with a ParparVM translation of its own, and
135136
# xcodebuild builds that before it can run a single test -- 16 of the 25 minutes went on
136137
# compiling it. The packaging job already allows 65 for the same reason.
137-
timeout-minutes: 45
138+
# Still compiling at 36 minutes once the hello app gained the Android compatibility gallery, its runtime and the Kotlin stdlib.
139+
timeout-minutes: 70
138140

139141
- name: Upload native iOS artifacts
140142
if: ${{ !cancelled() }}

‎.github/workflows/scripts-ios.yml‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ jobs:
159159
pull-requests: write
160160
issues: write
161161
runs-on: macos-15
162-
timeout-minutes: 75
162+
# The hello suite now also compiles the Android compatibility gallery, its runtime and the Kotlin stdlib; 75 left no margin.
163+
timeout-minutes: 95
163164
env:
164165
GITHUB_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
165166
# The device runner reports logical test failures through CN1SS log
@@ -415,7 +416,7 @@ jobs:
415416
"${{ steps.build-ios-app.outputs.scheme }}"
416417
# Match the GL envelope; run-ios-ui-tests.sh still fails a stalled
417418
# DeviceRunner after 12 minutes without CN1SS progress.
418-
timeout-minutes: 65
419+
timeout-minutes: 80
419420

420421
- name: Upload iOS Metal port status
421422
if: ${{ !cancelled() }}
@@ -545,7 +546,8 @@ jobs:
545546
pull-requests: write
546547
issues: write
547548
runs-on: xcode-27
548-
timeout-minutes: 75
549+
# The hello suite now also compiles the Android compatibility gallery, its runtime and the Kotlin stdlib; 75 left no margin.
550+
timeout-minutes: 95
549551
env:
550552
GITHUB_TOKEN: ${{ secrets.CN1SS_GH_TOKEN }}
551553
# The device runner reports logical test failures through CN1SS log
@@ -831,7 +833,7 @@ jobs:
831833
"${{ steps.build-ios-app.outputs.scheme }}"
832834
# Match the GL envelope; run-ios-ui-tests.sh still fails a stalled
833835
# DeviceRunner after 12 minutes without CN1SS progress.
834-
timeout-minutes: 65
836+
timeout-minutes: 80
835837

836838
# NO port-status upload here, deliberately. The published port report is
837839
# keyed on the port id `ios-metal`, and there is only one iOS port -- this

‎.github/workflows/scripts-javascript.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ on:
2323
- 'scripts/run-javascript-lifecycle-tests.mjs'
2424
- 'scripts/test-javascript-surface-replay.mjs'
2525
- 'scripts/test-javascript-weak-refs.mjs'
26+
- 'scripts/test-javascript-native-stub-bindings.mjs'
27+
- 'scripts/ci/tests/cn1ss-async-sink-test.sh'
28+
- 'scripts/ci/tests/Cn1ssAsyncSinkTest.java'
29+
- 'scripts/fidelity-app/common/src/main/java/com/codenameone/fidelity/Cn1ssAsyncSink.java'
2630
- 'scripts/test-javascript-vault-bridge.mjs'
2731
- 'scripts/test-javascript-composited-rendering.mjs'
2832
- 'scripts/javascript/composited-app/**'
@@ -67,6 +71,10 @@ on:
6771
- 'scripts/run-javascript-lifecycle-tests.mjs'
6872
- 'scripts/test-javascript-surface-replay.mjs'
6973
- 'scripts/test-javascript-weak-refs.mjs'
74+
- 'scripts/test-javascript-native-stub-bindings.mjs'
75+
- 'scripts/ci/tests/cn1ss-async-sink-test.sh'
76+
- 'scripts/ci/tests/Cn1ssAsyncSinkTest.java'
77+
- 'scripts/fidelity-app/common/src/main/java/com/codenameone/fidelity/Cn1ssAsyncSink.java'
7078
- 'scripts/run-javascript-lifecycle-tests.sh'
7179
- 'scripts/verify-javascript-lens-parity.mjs'
7280
- 'scripts/build-javascript-port-hellocodenameone.sh'
@@ -177,6 +185,19 @@ jobs:
177185
- name: Weak reference guards
178186
run: node scripts/test-javascript-weak-refs.mjs
179187

188+
# A static interop factory (JSString.valueOf, Int32Array.create, ...) runs
189+
# its Java body -- `return null;` -- unless port.js binds it; an unbound
190+
# JSString.valueOf made every directory marker a delete.
191+
- name: Interop stub bindings
192+
run: node scripts/test-javascript-native-stub-bindings.mjs
193+
194+
# Plain JVM, no build, so it also runs ahead of the ParparVM build. The
195+
# sink the Java-side captures ship through must redial after the test
196+
# server sheds its idle socket (close 1006): latching "failed" on that
197+
# close lost VideoIODecodedFrames with websocket-unavailable.
198+
- name: CN1SS async sink reconnect
199+
run: bash scripts/ci/tests/cn1ss-async-sink-test.sh
200+
180201
- name: Cache codenameone-tools
181202
uses: actions/cache@v5
182203
with:

0 commit comments

Comments
 (0)