<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shai Almog</title>
    <description>The latest articles on DEV Community by Shai Almog (@codenameone).</description>
    <link>https://dev.to/codenameone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F417973%2Fa7b22dd9-5565-48f5-bfab-7e5035b3888f.png</url>
      <title>DEV Community: Shai Almog</title>
      <link>https://dev.to/codenameone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codenameone"/>
    <language>en</language>
    <item>
      <title>App Intents: One Java Declaration for Siri, Spotlight, and Shortcuts</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Thu, 03 Sep 2026 17:05:38 +0000</pubDate>
      <link>https://dev.to/codenameone/app-intents-one-java-declaration-for-siri-spotlight-and-shortcuts-fdi</link>
      <guid>https://dev.to/codenameone/app-intents-one-java-declaration-for-siri-spotlight-and-shortcuts-fdi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04m43n1b4vl36kmvujd2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04m43n1b4vl36kmvujd2.jpg" alt="A Java application action connected to Siri through generated App Intents code" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@AppIntent&lt;/code&gt; can expose a public static Java method to Siri, Spotlight, and Shortcuts. The same declaration can also drive an Android launcher shortcut or an internal application command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5559" rel="noopener noreferrer"&gt;PR #5559&lt;/a&gt; adds &lt;code&gt;com.codename1.intents&lt;/code&gt; and the build-time annotations behind that integration. The build generates only the native declarations each target supports.&lt;/p&gt;

&lt;p&gt;For encrypted SQLite and the rest of this week's work, see the &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A handler is a public static method
&lt;/h2&gt;

&lt;p&gt;An app intent can run in a process the system started only to answer it. There may be no application object and no visible form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="no"&gt;WORKOUT_TOTAL_LOCK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nd"&gt;@AppIntent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"log_workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Log a workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Records a completed workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Log a workout in ${applicationName}"&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;headless&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeoutSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt; &lt;span class="nf"&gt;logWorkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@IntentParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kind"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"What kind of workout?"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ride"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"swim"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@IntentParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"minutes"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"How many minutes?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;WORKOUT_TOTAL_LOCK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Preferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"totalMinutes"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Preferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"totalMinutes"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDialog&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logged "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" minutes."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method is static because the build generates a direct call. Runtime annotation lookup is unavailable in translated iOS code, and dead-code elimination removes methods with no Java caller. A reflection-based dispatcher could compile successfully and disappear from the shipped application. Intent handlers can run concurrently, so the preference update protects its read-modify-write sequence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0phdmEgYW5ub3RhdGlvbnM8YnIvPkFwcEludGVudCBhbmQgSW50ZW50RW50aXR5XSAtLT4gQltNYXZlbiBieXRlY29kZSBwcm9jZXNzb3JdCiAgICBCIC0tPiBDW1JlZmxlY3Rpb24tZnJlZSBKYXZhIGRpc3BhdGNoIHRhYmxlXQogICAgQiAtLT4gRFtpT1MgQXBwIEludGVudHMgYW5kIFNwb3RsaWdodCBkZWNsYXJhdGlvbnNdCiAgICBCIC0tPiBFW0FuZHJvaWQgbGF1bmNoZXIgYW5kIGR5bmFtaWMgc2hvcnRjdXRzXQogICAgQyAtLT4gRltJbnRlbnRzLmludm9rZSBvbiBldmVyeSBwb3J0XQogICAgRCAtLT4gQwogICAgRSAtLT4gQwogICAgR1tTaW11bGF0b3IgQXBwIEludGVudHMgd2luZG93XSAtLT4gQw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0phdmEgYW5ub3RhdGlvbnM8YnIvPkFwcEludGVudCBhbmQgSW50ZW50RW50aXR5XSAtLT4gQltNYXZlbiBieXRlY29kZSBwcm9jZXNzb3JdCiAgICBCIC0tPiBDW1JlZmxlY3Rpb24tZnJlZSBKYXZhIGRpc3BhdGNoIHRhYmxlXQogICAgQiAtLT4gRFtpT1MgQXBwIEludGVudHMgYW5kIFNwb3RsaWdodCBkZWNsYXJhdGlvbnNdCiAgICBCIC0tPiBFW0FuZHJvaWQgbGF1bmNoZXIgYW5kIGR5bmFtaWMgc2hvcnRjdXRzXQogICAgQyAtLT4gRltJbnRlbnRzLmludm9rZSBvbiBldmVyeSBwb3J0XQogICAgRCAtLT4gQwogICAgRSAtLT4gQwogICAgR1tTaW11bGF0b3IgQXBwIEludGVudHMgd2luZG93XSAtLT4gQw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="931" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Maven plugin validates declarations and generates native metadata during the build. A malformed phrase, missing entity query, or invalid route fails the build instead of producing a shortcut that silently vanishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intents and routes answer different questions
&lt;/h2&gt;

&lt;p&gt;A route maps a URL to a screen. An intent accepts typed parameters, can ask the user to choose an entity, and returns a result. A route has no return channel.&lt;/p&gt;

&lt;p&gt;They meet when the intent should open a screen. &lt;code&gt;opensRoute&lt;/code&gt; passes bound parameters through the existing route table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@AppIntent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"show_workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Show a workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Show my ${workout} in ${applicationName}"&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;opensRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/workouts/{workout}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt; &lt;span class="nf"&gt;showWorkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@IntentParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Which workout?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Workout&lt;/span&gt; &lt;span class="n"&gt;workout&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Route&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/workouts/:id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="nf"&gt;workoutForm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RouteParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;buildWorkoutForm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{workout}&lt;/code&gt; placeholder expands to the selected entity's stable ID before the router matches &lt;code&gt;/workouts/:id&lt;/code&gt;. The screen keeps one address. A deep link and a system intent reach it through the same router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entities let the platform ask which object you meant
&lt;/h2&gt;

&lt;p&gt;An entity parameter represents an application noun such as a workout, playlist, or invoice. The platform can search those entities and ask the user to choose before the handler runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@IntentEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Workout&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@EntityId&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityTitle&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EntityQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BY_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Workout&lt;/span&gt; &lt;span class="nf"&gt;byId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WorkoutStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EntityQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEARCH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Workout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;matching&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WorkoutStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;BY_ID&lt;/code&gt; query is required because an entity crosses the native boundary as its identifier. The identifier must survive releases and data reordering. A list position or content hash can later resolve to the wrong object without producing an error.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Intents.index(...)&lt;/code&gt; publishes entities to device search. Remove them when the underlying content disappears, or stale results remain visible after the application data is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless means no window
&lt;/h2&gt;

&lt;p&gt;A headless handler can use &lt;code&gt;Storage&lt;/code&gt;, &lt;code&gt;Preferences&lt;/code&gt;, &lt;code&gt;Database&lt;/code&gt;, networking, logging, surfaces publishing, and indexing. It cannot use &lt;code&gt;Form&lt;/code&gt;, &lt;code&gt;Dialog&lt;/code&gt;, the camera, capture, or anything else that needs a window.&lt;/p&gt;

&lt;p&gt;Platform-dispatched handlers do not run on the event dispatch thread. A foreground handler that needs to update the UI must use &lt;code&gt;Display.callSerially(...)&lt;/code&gt;. &lt;code&gt;Intents.invoke(...)&lt;/code&gt; is synchronous and runs on the calling thread, which makes it useful as an internal command layer but also means a long handler can freeze the UI if invoked from the event thread.&lt;/p&gt;

&lt;p&gt;Each invocation has a deadline. Cancellation is cooperative through &lt;code&gt;IntentContext.isCancelled()&lt;/code&gt;. Commit durable changes as the work proceeds because a result returned after the platform deadline can be discarded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android shortcuts are not Siri
&lt;/h2&gt;

&lt;p&gt;Android has no equivalent to Siri's typed invocation contract and spoken result channel. The API does not label launcher shortcuts as voice parity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Declared intent&lt;/td&gt;
&lt;td&gt;App Intent and App Shortcut&lt;/td&gt;
&lt;td&gt;Launcher shortcut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice phrases&lt;/td&gt;
&lt;td&gt;Siri&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entity disambiguation&lt;/td&gt;
&lt;td&gt;System picker&lt;/td&gt;
&lt;td&gt;Application picker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device search&lt;/td&gt;
&lt;td&gt;Core Spotlight&lt;/td&gt;
&lt;td&gt;Long-lived launcher shortcuts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Donation&lt;/td&gt;
&lt;td&gt;Learned suggestions&lt;/td&gt;
&lt;td&gt;Dynamic shortcuts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headless execution&lt;/td&gt;
&lt;td&gt;Background application launch&lt;/td&gt;
&lt;td&gt;Service without an activity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Branch on &lt;code&gt;Intents.isVoiceInvocationSupported()&lt;/code&gt; before telling a user to speak to the application. &lt;code&gt;areIntentsSupported()&lt;/code&gt; answers a broader question.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Intents.invoke(...)&lt;/code&gt; works on every port because it calls the generated Java dispatcher. A desktop or web application can use the same intent as an internal command even when the operating system provides no external intent surface.&lt;/p&gt;

&lt;p&gt;Widget actions use the separate surfaces API. A tap arrives through &lt;code&gt;Surfaces.setActionHandler(...)&lt;/code&gt;; that handler can call &lt;code&gt;Intents.invoke(...)&lt;/code&gt; when a widget should reuse an intent. Declaring &lt;code&gt;@AppIntent&lt;/code&gt; alone does not generate a widget action.&lt;/p&gt;

&lt;h2&gt;
  
  
  System integrations stay out when unused
&lt;/h2&gt;

&lt;p&gt;An application that never references &lt;code&gt;com.codename1.intents&lt;/code&gt; gets no native intent plumbing, shortcut resources, frameworks, manifest entries, or deployment-target change.&lt;/p&gt;

&lt;p&gt;Spotlight indexing and donation use older Objective-C APIs and do not raise the iOS deployment target. Declaring an &lt;code&gt;@AppIntent&lt;/code&gt; generates the newer Swift declarations. &lt;code&gt;ios.intents.appIntents=false&lt;/code&gt; keeps indexing and donation while suppressing those declarations. &lt;code&gt;ios.intents.minDeploymentTarget&lt;/code&gt; lets the project choose whether an intent contributes a floor.&lt;/p&gt;

&lt;p&gt;The simulator's &lt;strong&gt;Simulate &amp;gt; App Intents&lt;/strong&gt; window lists the generated declarations, builds parameter forms, fills entity pickers from the real query methods, and invokes the same dispatch table used by the native ports.&lt;/p&gt;

&lt;p&gt;A workout handler remains one piece of Java application logic. Siri can invoke it by voice, Spotlight can find its entities, Android can publish it as a shortcut, and the application can call it directly through &lt;code&gt;Intents.invoke()&lt;/code&gt;. Each platform integration remains native to that platform.&lt;/p&gt;

&lt;p&gt;Return to &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;the parent post&lt;/a&gt; for the database, watch, web, smart-home, camera, and security work that shipped with it.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>CodeScanner.scan(): Barcode Scanning Without Rebuilding the Camera Pipeline</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 02 Sep 2026 17:09:59 +0000</pubDate>
      <link>https://dev.to/codenameone/codescannerscan-barcode-scanning-without-rebuilding-the-camera-pipeline-30pm</link>
      <guid>https://dev.to/codenameone/codescannerscan-barcode-scanning-without-rebuilding-the-camera-pipeline-30pm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbfj9379wilunc3tsjnt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbfj9379wilunc3tsjnt.jpg" alt="A phone camera recognizing a barcode, face, pose, document, and text through one vision pipeline" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new vision analyzers could read barcodes, faces, poses, text, documents, and segmentation masks. Scanning one QR code still meant opening a camera, configuring a session, listening for frames, converting each frame, feeding a pipeline, moving the result to the event thread, and restoring the previous form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The low-level layer remains necessary for custom camera products. It should not be a prerequisite for reading one code or counting faces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5575" rel="noopener noreferrer"&gt;PR #5575&lt;/a&gt; adds the missing layers above the analyzers. For the other work that shipped this week, see the &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One call owns the scanner screen
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CodeScanner.scan()&lt;/code&gt; opens a scanner form, runs the barcode analyzer, returns the first accepted code, and restores the form the application was showing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;CodeScanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ToastBar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showErrorMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This device cannot scan codes"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;CodeScanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// the user pressed back&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;urlField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;except&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cancellation is a &lt;code&gt;null&lt;/code&gt; result. A camera or decoder failure reaches &lt;code&gt;except(...)&lt;/code&gt;. That maps the old scanner library's three callbacks onto one &lt;code&gt;AsyncResource&lt;/code&gt; without treating the back button as an error.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CodeScannerOptions&lt;/code&gt; changes the screen text and restricts the accepted formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CodeScanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CodeScannerOptions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Boarding pass"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hold the pass flat inside the frame"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BarcodeFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PDF417&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BarcodeFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AZTEC&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;checkIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restricting formats prevents a nearby product barcode from completing a QR-only flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  One component owns the live pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;VisionCameraView&lt;/code&gt; covers the case where the preview belongs inside an application form. It opens the camera when shown, keeps only the newest frame while analysis is busy, delivers results on the event dispatch thread, and releases the camera when the user leaves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0NhbWVyYSBmcmFtZXNdIC0tPiBCW1Zpc2lvbkNhbWVyYVZpZXddCiAgICBCIC0tPiBDW0tlZXAgbmV3ZXN0IGZyYW1lXQogICAgQyAtLT4gRFtDYWxsZXItc2VsZWN0ZWQgYW5hbHl6ZXJdCiAgICBEIC0tPiBFW1R5cGVkIHJlc3VsdCBvbiBFRFRdCiAgICBFIC0tPiBGW0FwcGxpY2F0aW9uIFVJXQogICAgR1tGb3JtIGhpZGRlbl0gLS0-IEhbUmVsZWFzZSBjYW1lcmFdCiAgICBJW2Nsb3NlXSAtLT4gSltSZWxlYXNlIGFuYWx5emVyXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0NhbWVyYSBmcmFtZXNdIC0tPiBCW1Zpc2lvbkNhbWVyYVZpZXddCiAgICBCIC0tPiBDW0tlZXAgbmV3ZXN0IGZyYW1lXQogICAgQyAtLT4gRFtDYWxsZXItc2VsZWN0ZWQgYW5hbHl6ZXJdCiAgICBEIC0tPiBFW1R5cGVkIHJlc3VsdCBvbiBFRFRdCiAgICBFIC0tPiBGW0FwcGxpY2F0aW9uIFVJXQogICAgR1tGb3JtIGhpZGRlbl0gLS0-IEhbUmVsZWFzZSBjYW1lcmFdCiAgICBJW2Nsb3NlXSAtLT4gSltSZWxlYXNlIGFuYWx5emVyXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1425" height="278"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getCurrent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;FaceDetector&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FaceDetector&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;VisionCameraView&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Face&lt;/span&gt;&lt;span class="o"&gt;[]&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VisionCameraView&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFacing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CameraFacing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FRONT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VisionPipelineListener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Face&lt;/span&gt;&lt;span class="o"&gt;[]&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Face&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;faces&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;VisionImage&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;countLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" face(s)"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Faces"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SOUTH&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;countLabel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToolbar&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setBackCommand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Back"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showBack&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The analyzer remains caller-constructed. That is how the build decides which native model to package. A face-detection application should not carry barcode, pose, and segmentation dependencies merely because a high-level component knows those analyzers exist.&lt;/p&gt;

&lt;p&gt;Leaving the form temporarily stops the camera but keeps the analyzer ready in case the form is shown again. Call &lt;code&gt;close()&lt;/code&gt; from the navigation path that discards the screen permanently. It releases both resources and cannot be reversed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results use types and component coordinates
&lt;/h2&gt;

&lt;p&gt;Backend strings such as &lt;code&gt;"QR_CODE"&lt;/code&gt; and &lt;code&gt;"leftEye"&lt;/code&gt; are now represented by &lt;code&gt;BarcodeFormat&lt;/code&gt;, &lt;code&gt;FaceLandmarks&lt;/code&gt;, and &lt;code&gt;PoseLandmarks&lt;/code&gt;. Geometry helpers turn normalized analyzer coordinates into pixels an application can draw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Rectangle&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;faces&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;getBounds&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBounds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getWidth&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeight&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subImage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getX&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getY&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getWidth&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeight&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VisionImage.fromFile(...)&lt;/code&gt; and &lt;code&gt;VisionImage.fromImage(...)&lt;/code&gt; bridge picked images into the analyzer path. Encoded images keep their original bytes instead of being decoded and encoded again.&lt;/p&gt;

&lt;p&gt;Selfie segmentation also gets the operation that makes its result immediately usable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SelfieSegmenter&lt;/span&gt; &lt;span class="n"&gt;segmenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SelfieSegmenter&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;EncodedImage&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EncodedImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpegBytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;segmenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VisionImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encoded&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpegBytes&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cutOut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6f&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIcon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;segmenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;except&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;segmenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pixels below 60 percent foreground confidence become transparent.&lt;/p&gt;

&lt;h2&gt;
  
  
  High-level classes must still select native dependencies
&lt;/h2&gt;

&lt;p&gt;The build scans application classes, not every class in core. An application referencing only &lt;code&gt;CodeScanner&lt;/code&gt; never directly names &lt;code&gt;BarcodeScanner&lt;/code&gt;. Without an explicit mapping, the build could prune the barcode adapter and camera natives while still compiling successfully.&lt;/p&gt;

&lt;p&gt;The new high-level classes select the dependencies they need. &lt;code&gt;HighLevelVisionDependencyTest&lt;/code&gt; walks the vision sources and fails when a future convenience class is added without a corresponding build mapping. This tests the failure mode that would otherwise appear only as an inert feature on a device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vision can be scripted before hardware arrives
&lt;/h2&gt;

&lt;p&gt;In the simulator, &lt;strong&gt;Simulate &amp;gt; Vision&lt;/strong&gt; can mark a feature supported or unsupported and select a result, no-result, backend-error, or unsupported outcome. Scripted results include plausible geometry.&lt;/p&gt;

&lt;p&gt;That lets an application test cancellation, debounce behavior, overlays, and error copy without a camera or a trained model. It also makes the same application code run in desktop UI tests.&lt;/p&gt;

&lt;p&gt;The preview remains a native view. iOS and Android do not give native peers the same stacking order, so portable components should sit around the preview rather than assume they can paint a reticle on top of it. Only one camera session can be open at a time. The non-default preview scale modes are currently honored by the simulator but ignored by the iOS and Android camera previews.&lt;/p&gt;

&lt;p&gt;The low-level APIs remain available for products that need direct frame control. The change is that barcode scanning, face counting, and common camera analysis no longer start by rebuilding the plumbing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/app-intents-siri-spotlight-shortcuts/" rel="noopener noreferrer"&gt;next post shows how to expose a Java intent to Siri, Spotlight, and Shortcuts&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Tapjacking Protection: Rejecting Android Touches Behind an Overlay</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 01 Sep 2026 17:19:37 +0000</pubDate>
      <link>https://dev.to/codenameone/tapjacking-protection-rejecting-android-touches-behind-an-overlay-4h0b</link>
      <guid>https://dev.to/codenameone/tapjacking-protection-rejecting-android-touches-behind-an-overlay-4h0b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmeo0boi8wmcpo3w1rz0n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmeo0boi8wmcpo3w1rz0n.jpg" alt="A protected Android confirmation screen rejecting a touch from behind a malicious overlay" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are continuing our security hardening work with protection for the input path. A confirmation screen can be correct and still receive a tap the user did not understand. On Android, another application can draw over the screen and make a transfer button look like part of a different interaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5553" rel="noopener noreferrer"&gt;PR #5553&lt;/a&gt; adds tapjacking and screen-overlay protection to &lt;code&gt;DeviceIntegrity&lt;/code&gt;. It can report the condition, drop the full gesture, and ask Android 12 or newer to prevent overlay windows on a sensitive screen.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt; also covers encrypted SQLite, which protects stored bytes. Tapjacking protection controls input while the application is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android reports the overlay on the touch
&lt;/h2&gt;

&lt;p&gt;Android marks a &lt;code&gt;MotionEvent&lt;/code&gt; as fully obscured when another window covers the point that was touched. It marks the event as partially obscured when another window covers any part of the application window.&lt;/p&gt;

&lt;p&gt;Those signals have different false-positive rates. A fully obscured touch is the tapjacking case. Partial obscuring can come from ordinary system UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0FuZHJvaWQgTW90aW9uRXZlbnRdIC0tPiBCe0Z1bGx5IG9ic2N1cmVkP30KICAgIEIgLS0-fHllc3wgQ3tQb2xpY3l9CiAgICBCIC0tPnxub3wgRHtQYXJ0aWFsbHkgb2JzY3VyZWQ_fQogICAgRCAtLT58bm98IEVbRGVsaXZlciBnZXN0dXJlXQogICAgRCAtLT58eWVzfCBGe1NUUklDVD99CiAgICBGIC0tPnx5ZXN8IEdbRHJvcCB0aHJvdWdoIFVQIG9yIENBTkNFTF0KICAgIEYgLS0-fG5vfCBFCiAgICBDIC0tPnxSRVBPUlR8IEhbTm90aWZ5IGFuZCBkZWxpdmVyXQogICAgQyAtLT58QkxPQ0sgb3IgU1RSSUNUfCBHCiAgICBDIC0tPnxPRkZ8IEU%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0FuZHJvaWQgTW90aW9uRXZlbnRdIC0tPiBCe0Z1bGx5IG9ic2N1cmVkP30KICAgIEIgLS0-fHllc3wgQ3tQb2xpY3l9CiAgICBCIC0tPnxub3wgRHtQYXJ0aWFsbHkgb2JzY3VyZWQ_fQogICAgRCAtLT58bm98IEVbRGVsaXZlciBnZXN0dXJlXQogICAgRCAtLT58eWVzfCBGe1NUUklDVD99CiAgICBGIC0tPnx5ZXN8IEdbRHJvcCB0aHJvdWdoIFVQIG9yIENBTkNFTF0KICAgIEYgLS0-fG5vfCBFCiAgICBDIC0tPnxSRVBPUlR8IEhbTm90aWZ5IGFuZCBkZWxpdmVyXQogICAgQyAtLT58QkxPQ0sgb3IgU1RSSUNUfCBHCiAgICBDIC0tPnxPRkZ8IEU%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="752" height="898"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Detection is therefore touch-driven. &lt;code&gt;isScreenObscured()&lt;/code&gt; describes the latest observed touch. It is not a live query for every window on the device. An overlay that appears while nobody touches the application produces no event to inspect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four policies make the cost explicit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;TapjackingPolicy&lt;/code&gt; has four modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Reports&lt;/th&gt;
&lt;th&gt;Drops fully obscured gestures&lt;/th&gt;
&lt;th&gt;Drops partially obscured gestures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OFF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REPORT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BLOCK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STRICT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;BLOCK&lt;/code&gt; is the normal setting for a sensitive application. &lt;code&gt;REPORT&lt;/code&gt; is useful before enforcement when you need to measure what devices and installed tools produce. &lt;code&gt;STRICT&lt;/code&gt; can discard intended taps caused by benign system UI, so it belongs only on flows where missing a tap is preferable to accepting a framed one.&lt;/p&gt;

&lt;p&gt;The runtime setup can live in &lt;code&gt;init()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTapjackingProtection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TapjackingPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BLOCK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTapjackingListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TRUE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Dialog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Security warning"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Another app is drawing over this screen. "&lt;/span&gt;
                        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"Close it before continuing."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The listener receives state transitions rather than one callback per touch. Read the state from the event. The callback is delivered on the event dispatch thread, while a later clean touch can update the global state before a busy event thread handles the earlier warning.&lt;/p&gt;

&lt;h2&gt;
  
  
  A blocked gesture is all or nothing
&lt;/h2&gt;

&lt;p&gt;Dropping only the press would let the matching release reach a component that never received a press. The port latches the decision from &lt;code&gt;ACTION_DOWN&lt;/code&gt; through &lt;code&gt;UP&lt;/code&gt; or &lt;code&gt;CANCEL&lt;/code&gt;. Application components receive none of the gesture.&lt;/p&gt;

&lt;p&gt;The Android port performs the check explicitly before the event reaches Codename One. The normal Android &lt;code&gt;setFilterTouchesWhenObscured()&lt;/code&gt; path is not enough because the Codename One view dispatches directly into its own touch handler. The standard filter remains enabled on fallback paths as a second layer.&lt;/p&gt;

&lt;p&gt;This also matters for native peers. A &lt;code&gt;BrowserComponent&lt;/code&gt; or native text field can have a platform-specific event path. Preventing the overlay window is stronger than filtering touches after they arrive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android 12 can hide overlays
&lt;/h2&gt;

&lt;p&gt;On a sensitive screen, combine the touch policy with overlay prevention and screen capture protection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isHideOverlayWindowsSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHideOverlayWindows&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSecureScreen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;isHideOverlayWindowsSupported()&lt;/code&gt; requires Android 12 or newer and the &lt;code&gt;android.permission.HIDE_OVERLAY_WINDOWS&lt;/code&gt; manifest permission. The zero-code guard adds the permission and chooses the normal policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;android.tapjackingGuard&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;android.tapjackingGuard.mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;block&lt;/span&gt;
&lt;span class="py"&gt;android.tapjackingGuard.hideOverlays&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clear &lt;code&gt;setHideOverlayWindows(false)&lt;/code&gt; and &lt;code&gt;setSecureScreen(false)&lt;/code&gt; when the application leaves the sensitive flow if the rest of the product allows those features.&lt;/p&gt;

&lt;h2&gt;
  
  
  iOS does not allow the same cross-application overlay
&lt;/h2&gt;

&lt;p&gt;iOS does not let one application draw a window over another, so the policy has no iOS work to do. Screen recording and mirroring are different threats and use &lt;code&gt;ios.disableScreenshots=true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The simulator exposes &lt;strong&gt;Simulate &amp;gt; App Shield &amp;gt; Screen Overlay (Tapjacking)&lt;/strong&gt;. It drives the same listener and signal path as the Android port, which lets the warning, policy transitions, and analytics path run on the desktop. Real-device testing is still needed for partial-obscuring behavior because system UI differs by Android vendor.&lt;/p&gt;

&lt;p&gt;Tapjacking is not reported as a standing device-compromise reason. An overlay is transient and can be benign. Folding it into &lt;code&gt;isDeviceCompromised()&lt;/code&gt; would make a notification shade look like a rooted device. Instead it raises its own App Shield signal and remains available as a local input policy.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BLOCK&lt;/code&gt; handles the high-confidence case by default. &lt;code&gt;STRICT&lt;/code&gt; remains an explicit choice because partially obscured touches can come from benign system UI. Unsupported platforms do not report protection they cannot provide.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/camera-vision-scanners/" rel="noopener noreferrer"&gt;next post restores a short path for common camera and vision cases&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>One Java API for HomeKit, Matter, and Google Home</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 31 Aug 2026 19:30:15 +0000</pubDate>
      <link>https://dev.to/codenameone/one-java-api-for-homekit-matter-and-google-home-2g85</link>
      <guid>https://dev.to/codenameone/one-java-api-for-homekit-matter-and-google-home-2g85</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuc53dnpd96vn6ojkane2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuc53dnpd96vn6ojkane2.jpg" alt="Lights, locks, and thermostats mapped from HomeKit, Matter, and Google Home into one Java API" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A light can expose brightness through a HomeKit characteristic or a Matter cluster. Application code should ask for brightness, not carry both platform identifiers and two sets of value rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5554" rel="noopener noreferrer"&gt;PR #5554&lt;/a&gt; adds &lt;code&gt;com.codename1.home&lt;/code&gt;, a portable model for listing accessories, reading and writing traits, watching changes, running scenes, and commissioning Matter devices.&lt;/p&gt;

&lt;p&gt;The API also reports cases where the platforms cannot give the same answer. For the other work that shipped this week, see the &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traits describe the device, not the platform
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;HomeStructure&lt;/code&gt; contains rooms and accessories. Each accessory contains services, and each service exposes traits such as &lt;code&gt;ON_OFF&lt;/code&gt;, &lt;code&gt;BRIGHTNESS&lt;/code&gt;, or &lt;code&gt;TARGET_TEMPERATURE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0FwcGxpY2F0aW9uPGJyLz5UcmFpdC5CUklHSFRORVNTXSAtLT4gQltjb20uY29kZW5hbWUxLmhvbWVdCiAgICBCIC0tPiBDW0hvbWVLaXQ8YnIvPkhNQ2hhcmFjdGVyaXN0aWNUeXBlQnJpZ2h0bmVzc10KICAgIEIgLS0-IERbTWF0dGVyPGJyLz5MZXZlbCBDb250cm9sIEN1cnJlbnRMZXZlbF0KICAgIEIgLS0-IEVbU2ltdWxhdG9yPGJyLz5zY3JpcHRlZCBhY2Nlc3NvcnkgZ3JhcGhdCiAgICBDIC0tPiBGW0Nhbm9uaWNhbCBUcmFpdFZhbHVlXQogICAgRCAtLT4gRgogICAgRSAtLT4gRg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0FwcGxpY2F0aW9uPGJyLz5UcmFpdC5CUklHSFRORVNTXSAtLT4gQltjb20uY29kZW5hbWUxLmhvbWVdCiAgICBCIC0tPiBDW0hvbWVLaXQ8YnIvPkhNQ2hhcmFjdGVyaXN0aWNUeXBlQnJpZ2h0bmVzc10KICAgIEIgLS0-IERbTWF0dGVyPGJyLz5MZXZlbCBDb250cm9sIEN1cnJlbnRMZXZlbF0KICAgIEIgLS0-IEVbU2ltdWxhdG9yPGJyLz5zY3JpcHRlZCBhY2Nlc3NvcnkgZ3JhcGhdCiAgICBDIC0tPiBGW0Nhbm9uaWNhbCBUcmFpdFZhbHVlXQogICAgRCAtLT4gRgogICAgRSAtLT4gRg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1088" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The port owns conversions that are easy to get wrong. Matter brightness uses a 0 to 254 level. Covering position runs in the opposite direction on some backends. Matter has no single thermostat setpoint in automatic mode. Air quality has a different number of levels in HomeKit and Matter.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TraitValue&lt;/code&gt; exposes the canonical value and retains the platform ordinal where a conversion loses detail. Proportional values become percentages. Temperature getters require the expected unit, preventing a Celsius value from being read as Fahrenheit by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Availability has more than two states
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SmartHome.getInstance()&lt;/code&gt; never returns &lt;code&gt;null&lt;/code&gt;. Unsupported ports return a fallback that reports &lt;code&gt;NOT_SUPPORTED&lt;/code&gt;. Supported ports use more specific states so an empty accessory graph is not mistaken for a configured home with no devices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SmartHome&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SmartHome&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;onResult&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;structures&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt; &lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAvailability&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROVIDER_NOT_INSTALLED&lt;/span&gt;
            &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROVIDER_UPDATE_REQUIRED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openProviderSetup&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PERMISSION_REQUIRED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestAuthorization&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PERMISSION_DENIED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openHomeSettings&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NOT_CONFIGURED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openEcosystemApp&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COMMISSIONING_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;showAddDeviceOnlyUI&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read availability from the completion path even when &lt;code&gt;refresh()&lt;/code&gt; fails. On iOS, the initial value is &lt;code&gt;NOT_STARTED&lt;/code&gt; because connecting to HomeKit is what reveals the authorization and home state.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COMMISSIONING_ONLY&lt;/code&gt; is the normal Android result without Google Home developer setup. Play services can add a Matter accessory to the user's Google Home. Reading or controlling the accessory graph requires Google Home APIs, a Google Cloud project, and a Home Developer Console registration containing the application's signing-key SHA-1. Codename One cannot create those credentials for an application.&lt;/p&gt;

&lt;p&gt;The Google Home accessory graph is not part of this release. Reporting full availability would make the same enum value mean different things on Android and iOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reads and writes can partly succeed
&lt;/h2&gt;

&lt;p&gt;A batch read returns one &lt;code&gt;TraitReading&lt;/code&gt; per requested value. A reading can contain a value, contain an error, or contain neither. The third state is valid when a sensor has not measured yet or a light in white mode has no meaningful hue.&lt;/p&gt;

&lt;p&gt;A batch write follows the same rule. Turning off every light can succeed for three accessories and fail for one unreachable bulb. The result carries each row instead of collapsing the operation into one boolean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TraitReadRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TraitReadRequest&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thermostat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thermostatService&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Trait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CURRENT_TEMPERATURE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thermostat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thermostatService&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Trait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CURRENT_HUMIDITY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;onResult&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;readings&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TraitReading&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isFailed&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;showUnavailable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTrait&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasValue&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;showNoReadingYet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTrait&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTrait&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build controls from each service's &lt;code&gt;TraitConstraint&lt;/code&gt;. A dimmer with a ten-percent floor reports that floor. Writing below it is refused rather than silently clamped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changes are state updates, not an event log
&lt;/h2&gt;

&lt;p&gt;HomeKit can push trait changes while the application is in the foreground. Other backends require polling. &lt;code&gt;TraitSubscription.isPushDelivery()&lt;/code&gt; tells the application which model it received.&lt;/p&gt;

&lt;p&gt;Where push delivery is unavailable, call &lt;code&gt;drainChanges()&lt;/code&gt; when the application returns to the foreground. Changes are coalesced by accessory, service, and trait. Dragging a dimmer can therefore produce one update with the final value instead of forty intermediate events.&lt;/p&gt;

&lt;p&gt;Nothing in this release wakes a stopped application for an accessory change. The home hub owns background automation. The phone application owns a foreground view of current state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commissioning uses the operating system's setup flow
&lt;/h2&gt;

&lt;p&gt;Both mobile ports hand Matter commissioning to a system-owned flow. The user may need to power on the accessory, hold a physical button, scan a label, and join Wi-Fi. The application receives no determinate progress to display.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SmartHome&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SmartHome&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Commissioner&lt;/span&gt; &lt;span class="n"&gt;commissioner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCommissioner&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;SetupPayload&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SetupPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scannedCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;commissioner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commission&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CommissioningRequest&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSetupPayload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSuggestedName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Kettle"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onResult&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAvailability&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;HomeAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COMMISSIONING_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;showAddedToHome&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccessoryName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;onResult&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;structures&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refreshError&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refreshError&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refreshError&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;showUpdatedHome&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structures&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccessoryId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success can mean the device joined the user's home without becoming addressable by the application. That is the normal result on an Android build with commissioning alone. On graph-capable builds, refresh after every successful flow. The default iOS Matter sheet does not return an accessory ID even though the new device appears in HomeKit, so the application must inspect the refreshed graph. &lt;code&gt;wasCommissionedToThisApp()&lt;/code&gt; is useful when a backend returns a directly addressable ID, but it is not a substitute for that refresh.&lt;/p&gt;

&lt;p&gt;The iOS build adds HomeKit entitlements only when application code touches accessories. Commissioning lives in its own package because it adds a generated app-extension target. An application that never references &lt;code&gt;com.codename1.home&lt;/code&gt; gets no framework, entitlement, Play services dependency, or extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  The simulated house contains the awkward cases
&lt;/h2&gt;

&lt;p&gt;The simulator, desktop ports, and JavaScript expose a local house with a two-gang switch, bridged lights, an unreachable socket, a thermostat in automatic mode, and a dimmer with a nonzero floor.&lt;/p&gt;

&lt;p&gt;Those cases make the application handle missing values, partial failures, constraints, and delayed callbacks before it meets real hardware. The simulator does not push changes and never completes an operation inline, which prevents desktop-only timing assumptions from becoming device bugs.&lt;/p&gt;

&lt;p&gt;Automations, triggers, background events, topology writes, camera streams, alarm panels, and Matter events remain outside this release. By default Codename One also commissions through the Apple Home or Google Home ecosystem rather than becoming a Matter controller with its own fabric.&lt;/p&gt;

&lt;p&gt;Application code can work with brightness, temperature, locks, and scenes without carrying HomeKit characteristic strings or Matter cluster IDs. Capability queries still expose the platform differences the product must handle.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/tapjacking-protection/" rel="noopener noreferrer"&gt;next post adds tapjacking protection to the security work&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>JavaScript Find in Page: DOM Text Above the Codename One Canvas</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 30 Aug 2026 17:26:42 +0000</pubDate>
      <link>https://dev.to/codenameone/javascript-find-in-page-dom-text-above-the-codename-one-canvas-41pj</link>
      <guid>https://dev.to/codenameone/javascript-find-in-page-dom-text-above-the-codename-one-canvas-41pj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hgroczq5iwiz8wbic65.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hgroczq5iwiz8wbic65.jpg" alt="Searchable browser text layered above a Codename One canvas and accessibility tree" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browser search cannot find pixels. That was the JavaScript port's text model: Codename One drew each glyph onto a canvas, so a visible label was invisible to find-in-page, text selection, and ordinary browser text machinery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5552" rel="noopener noreferrer"&gt;PR #5552&lt;/a&gt; keeps the canvas renderer and promotes eligible visible text into a DOM layer. Codename One still measures and places every run. The browser handles the part it is better at: text rasterization, selection, search, accessibility, input metadata, and device-pixel resolution.&lt;/p&gt;

&lt;p&gt;For encrypted SQLite and the rest of this week's work, see the &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A DOM component tree was the wrong trade
&lt;/h2&gt;

&lt;p&gt;Replacing each Codename One component with a DOM element would hand layout and paint ordering to the browser. It would also create two UI implementations to keep consistent.&lt;/p&gt;

&lt;p&gt;We kept application components on the canvas and projected only the text and accessibility data the browser needs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRCCiAgICBBW0NvZGVuYW1lIE9uZSBsYXlvdXQgYW5kIHBhaW50XSAtLT4gQltDYW52YXM8YnIvPnNoYXBlcyBpbWFnZXMgdHJhbnNmb3JtZWQgdGV4dF0KICAgIEEgLS0-IENbVGV4dCBsYXllcjxici8-ZWxpZ2libGUgdmlzaWJsZSB0ZXh0IHJ1bnNdCiAgICBBIC0tPiBEW0FjY2Vzc2liaWxpdHkgdHJlZTxici8-aW5jcmVtZW50YWwgQVJJQSBwcm9qZWN0aW9uXQogICAgRVtCcm93c2VyIGlucHV0XSAtLT4gRltQb3NpdGlvbmVkIGlucHV0IG9yIHRleHRhcmVhXQogICAgQiAtLT4gR1tDb21wb3NpdGVkIHBhZ2VdCiAgICBDIC0tPiBHCiAgICBEIC0tPiBHCiAgICBGIC0tPiBH%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRCCiAgICBBW0NvZGVuYW1lIE9uZSBsYXlvdXQgYW5kIHBhaW50XSAtLT4gQltDYW52YXM8YnIvPnNoYXBlcyBpbWFnZXMgdHJhbnNmb3JtZWQgdGV4dF0KICAgIEEgLS0-IENbVGV4dCBsYXllcjxici8-ZWxpZ2libGUgdmlzaWJsZSB0ZXh0IHJ1bnNdCiAgICBBIC0tPiBEW0FjY2Vzc2liaWxpdHkgdHJlZTxici8-aW5jcmVtZW50YWwgQVJJQSBwcm9qZWN0aW9uXQogICAgRVtCcm93c2VyIGlucHV0XSAtLT4gRltQb3NpdGlvbmVkIGlucHV0IG9yIHRleHRhcmVhXQogICAgQiAtLT4gR1tDb21wb3NpdGVkIHBhZ2VdCiAgICBDIC0tPiBHCiAgICBEIC0tPiBHCiAgICBGIC0tPiBH%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1162" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page now has a shape similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"presentation"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cn1-text-layer"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position:absolute; white-space:pre"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Account balance&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cn1-accessibility-tree"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Account balance"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text run arrives already broken and positioned. &lt;code&gt;white-space: pre&lt;/code&gt; prevents the browser from wrapping it. Text measurement stays on the worker's &lt;code&gt;OffscreenCanvas&lt;/code&gt;, so promoting a run does not alter layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebuilding the overlay broke the browser features
&lt;/h2&gt;

&lt;p&gt;The accessibility projection previously cleared its container and recreated every element on each invalidation. Scrolling changes component bounds repeatedly, which meant scrolling also destroyed DOM focus and any active text selection.&lt;/p&gt;

&lt;p&gt;The overlay now diffs the semantic tree. Stable nodes keep their element identity and listeners while their bounds or content change. The browser can maintain focus and selection because the application is no longer replacing the element under it.&lt;/p&gt;

&lt;p&gt;The real-browser verification tags semantic elements with a JavaScript property, triggers invalidation, then reads the property again. Rebuilding the tree would lose the tag and fail the test. That makes incremental behavior part of the port contract rather than a performance assumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  High-DPI displays use their native resolution
&lt;/h2&gt;

&lt;p&gt;The old host code pinned &lt;code&gt;devicePixelRatio&lt;/code&gt; to one. A 375 by 667 CSS box therefore had a 375 by 667 backing canvas even on a display with a pixel ratio of two. The browser enlarged that bitmap to the physical pixels.&lt;/p&gt;

&lt;p&gt;The new path uses a 750 by 1334 backing surface behind the same 375 by 667 CSS box at a device-pixel ratio of two. Coordinates are converted at the DOM boundary, while Codename One continues to address device pixels internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSS layout:      375 x 667
devicePixelRatio:        2
backing surface: 750 x 1334
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The port also refreshes the ratio when browser zoom changes or a window moves between displays. Screenshot tests can still pin a specific ratio through the existing query parameter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inputs now tell the browser what they mean
&lt;/h2&gt;

&lt;p&gt;Text editing already used a positioned native input. The new work fixes and extends the metadata the browser reads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inputmode&lt;/code&gt; selects the appropriate on-screen keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;autocomplete&lt;/code&gt; connects password managers and autofill.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;autocapitalize&lt;/code&gt; and &lt;code&gt;spellcheck&lt;/code&gt; follow the Codename One constraints.&lt;/li&gt;
&lt;li&gt;The component name becomes the field name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cn1$autocomplete&lt;/code&gt; can override the autocomplete token.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A password field bug also disappeared. &lt;code&gt;PASSWORD&lt;/code&gt; is a bit flag, but the old code compared the entire constraint value. &lt;code&gt;PASSWORD | EMAILADDR&lt;/code&gt; missed the password case and fell through to a clear-text input.&lt;/p&gt;

&lt;p&gt;Several browser APIs had silently broken when the port moved into a worker. A &lt;code&gt;@JSBody&lt;/code&gt; executes in that worker, so direct access to &lt;code&gt;history&lt;/code&gt;, &lt;code&gt;matchMedia&lt;/code&gt;, or other window-owned objects fails. Host bindings now restore browser history, dark-mode detection, reduced-motion and forced-color queries, and cursor support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some text stays on the canvas
&lt;/h2&gt;

&lt;p&gt;The DOM layer is targeted. Text stays on the canvas when the DOM cannot reproduce the paint operation without changing its meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offscreen targets, transition buffers, &lt;code&gt;ComponentImage&lt;/code&gt;, and screenshot rendering&lt;/li&gt;
&lt;li&gt;Cell renderers whose single component instance is stamped at several positions&lt;/li&gt;
&lt;li&gt;Text outside the displayed form when a dialog paints another form as its backdrop&lt;/li&gt;
&lt;li&gt;Shape clips, transformed text, and bitmap fonts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drag selection is also disabled for now. The text layer takes no pointer events so the canvas keeps owning hit testing. Find-in-page and keyboard selection work without that change. Mouse drag selection needs pointer routing that can distinguish text selection from component gestures.&lt;/p&gt;

&lt;p&gt;Vertical placement can differ from browser font metrics by roughly one pixel. A sheet covering text inside the same form can also leave that text represented in the DOM until same-form occlusion is modeled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search works without handing layout to the browser
&lt;/h2&gt;

&lt;p&gt;The JavaScript port still ships the Codename One UI. A browser cannot reflow a label, substitute a component, or change a layout after an OS update. The application owns those decisions.&lt;/p&gt;

&lt;p&gt;The browser now sees enough structure to do what users expect from a web page. Search finds text. Assistive technology receives stable semantic nodes. Password managers recognize fields. High-DPI screens render at their actual pixel density.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/smart-home-homekit-matter/" rel="noopener noreferrer"&gt;next post maps HomeKit, Matter, and Google Home without flattening their differences&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Watch Apps: One Codebase, Two Real Applications</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 29 Aug 2026 17:06:28 +0000</pubDate>
      <link>https://dev.to/codenameone/watch-apps-one-codebase-two-real-applications-215f</link>
      <guid>https://dev.to/codenameone/watch-apps-one-codebase-two-real-applications-215f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoadmfvohgg8b0w2895w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoadmfvohgg8b0w2895w.jpg" alt="A phone and watch running separate Codename One applications connected by an asynchronous data channel" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A watch app is not a second form in the phone process. It is another application on another device, with its own storage, startup sequence, and periods when the other side is unreachable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5487" rel="noopener noreferrer"&gt;PR #5487&lt;/a&gt; now builds an Apple Watch companion from &lt;code&gt;codename1.watchMain&lt;/code&gt;. On Wear OS, the same entry point becomes the Android product when &lt;code&gt;codename1.watchStandalone=true&lt;/code&gt;; a companion Wear APK beside the phone application is not generated yet. The release also adds one phone-to-watch API that maps to &lt;code&gt;WCSession&lt;/code&gt; on Apple platforms and the Wearable Data Layer on Android.&lt;/p&gt;

&lt;p&gt;For encrypted SQLite and the rest of this week's work, see the &lt;a href="https://www.codenameone.com/blog/sqlite-portable-encrypted/" rel="noopener noreferrer"&gt;weekly release overview&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One entry point builds the watch application
&lt;/h2&gt;

&lt;p&gt;The watch application starts from a fully qualified class name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.watchMain&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;com.example.MyWatchApp&lt;/span&gt;

&lt;span class="c"&gt;# For a standalone Wear OS product:
&lt;/span&gt;&lt;span class="py"&gt;codename1.watchStandalone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Apple platforms, &lt;code&gt;watchMain&lt;/code&gt; adds a companion target to the phone build. On Android, &lt;code&gt;watchStandalone=true&lt;/code&gt; replaces the phone product with the Wear OS application rooted at &lt;code&gt;watchMain&lt;/code&gt;. Without that flag, the Android build remains the phone application. The build logs that no companion Wear artifact was produced instead of quietly implying otherwise.&lt;/p&gt;

&lt;p&gt;The Apple build derives the watch bundle identifier, deployment target, signing team, and display name from settings the project already has.&lt;/p&gt;

&lt;p&gt;The phone and watch share source files, resources, CSS, and themes. They do not share runtime state. Each has its own &lt;code&gt;Storage&lt;/code&gt;, &lt;code&gt;Preferences&lt;/code&gt;, and SQLite files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW1NoYXJlZCBKYXZhIHNvdXJjZTxici8-cmVzb3VyY2VzIGFuZCBDU1NdIC0tPiBCW1Bob25lIGFwcGxpY2F0aW9uXQogICAgQSAtLT4gQ1tXYXRjaCBhcHBsaWNhdGlvbjxici8-d2F0Y2hNYWluXQogICAgQiA8LS0-fHNlbmRNZXNzYWdlPGJyLz5saXZlIHJlcXVlc3QgYW5kIHJlcGx5fCBDCiAgICBCIDwtLT58cHV0RGF0YTxici8-bGF0ZXN0IHJlcGxpY2F0ZWQgc3RhdGV8IEMKICAgIEIgPC0tPnx0cmFuc2ZlckZpbGU8YnIvPmJhY2tncm91bmQgcGF5bG9hZHwgQw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW1NoYXJlZCBKYXZhIHNvdXJjZTxici8-cmVzb3VyY2VzIGFuZCBDU1NdIC0tPiBCW1Bob25lIGFwcGxpY2F0aW9uXQogICAgQSAtLT4gQ1tXYXRjaCBhcHBsaWNhdGlvbjxici8-d2F0Y2hNYWluXQogICAgQiA8LS0-fHNlbmRNZXNzYWdlPGJyLz5saXZlIHJlcXVlc3QgYW5kIHJlcGx5fCBDCiAgICBCIDwtLT58cHV0RGF0YTxici8-bGF0ZXN0IHJlcGxpY2F0ZWQgc3RhdGV8IEMKICAgIEIgPC0tPnx0cmFuc2ZlckZpbGU8YnIvPmJhY2tncm91bmQgcGF5bG9hZHwgQw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="843" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wear OS reuses the Android port. watchOS uses a separate Core Graphics renderer because it has no UIKit view hierarchy, OpenGL ES, or Metal. The watch runtime sits inside a SwiftUI shell and runs its own ParparVM translation rooted at the watch entry point.&lt;/p&gt;

&lt;h2&gt;
  
  
  A message and a value solve different problems
&lt;/h2&gt;

&lt;p&gt;The platforms offer several transports because a watch spends much of its life asleep.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;putData()&lt;/code&gt; for state that should converge when the watch next wakes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WearableConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WearableMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/steps"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stepCount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"goalReached"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stepCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the listener during &lt;code&gt;init()&lt;/code&gt;. A payload can be the reason the platform started the process, so listeners attached from a later form may miss the replay window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WearableConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addDataListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WearableDataListener&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;dataChanged&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WearableMessage&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/steps"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPath&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;stepsLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;dataRemoved&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/steps"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;stepsLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each data path holds the latest value. Two rapid writes can arrive as one update. That is correct for a step count and wrong for a queue of events.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;sendMessage()&lt;/code&gt; when both applications must be awake and the sender needs an answer now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WearableConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WearableMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/workout/start"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WearableReplyHandler&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;replyReceived&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WearableMessage&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;showWorkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;replyFailed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;showReplicatedWorkoutState&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failure is a normal branch. The phone may be asleep, out of range, or running an older version that does not know the message path. Do not use &lt;code&gt;isReachable()&lt;/code&gt; as a preflight for a request with a fallback. Reachability can change after it is checked, and its first value during a cold start may still be unknown. Let &lt;code&gt;replyFailed()&lt;/code&gt; select the replicated state instead. &lt;code&gt;transferFile()&lt;/code&gt; covers files and large payloads that can arrive later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The simulator runs two processes
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Watch &amp;gt; Launch Watch App&lt;/strong&gt; command starts the watch beside the phone. The applications run in separate processes and connect through the desktop bridge, so &lt;code&gt;sendMessage()&lt;/code&gt; and &lt;code&gt;putData()&lt;/code&gt; take the same asynchronous route the application code expects on a device.&lt;/p&gt;

&lt;p&gt;The simulator includes Apple Watch 41 mm and 45 mm skins, plus round and square Wear skins. Test the round skin even if the first target is Apple Watch. It catches layouts that depend on rectangular corners.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CN.isWatch()&lt;/code&gt; selects the form-factor-specific UI. The &lt;code&gt;watch&lt;/code&gt; theme override changes styling without forking the rest of the theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BoxLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isWatch&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi Watch"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToolbar&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setVisible&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SpanLabel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome to the phone application"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Complications reuse the surfaces model
&lt;/h2&gt;

&lt;p&gt;A complication is a small system-rendered surface driven by a timeline. That is the same model Codename One uses for widgets, Live Activities, and Dynamic Island content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WidgetKind&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WidgetKind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"steps"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Steps"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSupportedSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WidgetSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WATCH_CIRCULAR&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSupportedSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WidgetSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WATCH_RECTANGULAR&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The watch sizes belong to &lt;code&gt;WidgetSize&lt;/code&gt; instead of a second complication API. Application content can therefore share the same surface descriptors and timeline logic.&lt;/p&gt;

&lt;p&gt;The system targets that render those watch families are not generated yet. watchOS still needs its WidgetKit extension target, and Wear OS still needs complication or tile services. The API establishes the common model without claiming those final platform adapters have shipped.&lt;/p&gt;

&lt;p&gt;Android has one more current limit. Standalone Wear applications build today. A companion configuration does not yet produce a second Wear APK beside the phone APK. Apple Watch supports both companion and standalone targets, although standalone App Store submission still needs a manual archive step in Xcode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share code without pretending the watch is a phone
&lt;/h2&gt;

&lt;p&gt;The watch and phone are separate products. They can still share application rules, visual assets, and surface descriptions. &lt;code&gt;WearableConnection&lt;/code&gt; keeps the connection between them visible in ordinary Java code.&lt;/p&gt;

&lt;p&gt;Write once, run anywhere does not require pretending every screen has the same lifecycle. A phone message can fail. A replicated value can arrive after a relaunch. A complication can render while neither application is active. The shared code handles those cases without hiding them.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/javascript-dom-text-search/" rel="noopener noreferrer"&gt;next post keeps the Codename One renderer while restoring browser-native text behavior&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>SQLite Across Every Port: One Contract, One Encrypted File Format</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 28 Aug 2026 19:01:48 +0000</pubDate>
      <link>https://dev.to/codenameone/sqlite-across-every-port-one-contract-one-encrypted-file-format-2fmo</link>
      <guid>https://dev.to/codenameone/sqlite-across-every-port-one-contract-one-encrypted-file-format-2fmo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgefl8pagioo32mhiasjh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgefl8pagioo32mhiasjh.jpg" alt="A locked SQLite database connected to mobile, web, desktop, and watch applications" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original &lt;code&gt;com.codename1.db&lt;/code&gt; implementation delegated SQLite calls to the database supplied by each operating system. We knew this was less portable than the rest of Codename One, but fixing it meant taking ownership of SQLite on every target. That looked like a deep rabbit hole, so we exposed escape hatches instead. The &lt;a href="https://www.codenameone.com/blog/spatial-pluggable-sqlite/" rel="noopener noreferrer"&gt;pluggable SpatiaLite work we described in 2018&lt;/a&gt; is one example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On Windows and Linux, &lt;code&gt;Database.openOrCreate()&lt;/code&gt; returned &lt;code&gt;null&lt;/code&gt;. JavaScript depended on WebSQL, which Chrome removed and Firefox never implemented. An iOS cursor could report success on an empty result set, then read unset memory. The simulator counted rows from one while the other ports counted from zero. Encryption had no sensible place to live because there was no single database contract underneath it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5526" rel="noopener noreferrer"&gt;PR #5526&lt;/a&gt; follows that rabbit hole to the end. It replaces the platform-dependent implementations with one specified and tested SQLite contract. It also resolves the long-standing &lt;a href="https://github.com/codenameone/CodenameOne/issues/3848" rel="noopener noreferrer"&gt;encrypted database request&lt;/a&gt;. The same encrypted file can move between ports and open in a standard SQLCipher 4 client.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
SQLite now has one tested contract, including native Windows and Linux implementations, SQLite compiled to WebAssembly for JavaScript, and encryption with three key models.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.codenameone.com/blog/watch-apps-phone-channel/" rel="noopener noreferrer"&gt;watch apps deep dive&lt;/a&gt; treats the phone and watch as separate applications with separate lifecycles. &lt;code&gt;WearableConnection&lt;/code&gt; gives them one asynchronous API for messages, replicated state, and files.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/javascript-dom-text-search/" rel="noopener noreferrer"&gt;JavaScript text&lt;/a&gt; now appears as real DOM text above the canvas. Browser search, selection, accessibility, autofill, and native-resolution rendering work without handing layout to the browser.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/smart-home-homekit-matter/" rel="noopener noreferrer"&gt;Smart home support&lt;/a&gt; maps HomeKit, Matter, and Google Home concepts onto &lt;code&gt;com.codename1.home&lt;/code&gt;, including a simulated house for desktop development.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/tapjacking-protection/" rel="noopener noreferrer"&gt;Tapjacking protection&lt;/a&gt; detects and can reject Android gestures that begin behind another app's overlay.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/camera-vision-scanners/" rel="noopener noreferrer"&gt;Camera and vision&lt;/a&gt; regain the one-call ergonomics that were lost when the old scanner libraries were replaced by lower-level on-device analyzers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/app-intents-siri-spotlight-shortcuts/" rel="noopener noreferrer"&gt;App Intents&lt;/a&gt; expose a Java handler to Siri, Spotlight, Shortcuts, Android launcher shortcuts, and an internal command layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Each port had different SQLite behavior
&lt;/h2&gt;

&lt;p&gt;The old API looked uniform because every port implemented the same Java methods. That said nothing about the result.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;execute(sql)&lt;/code&gt; ran every statement on iOS, but silently stopped after the first statement in the simulator. &lt;code&gt;getBlob()&lt;/code&gt; returned &lt;code&gt;nil&lt;/code&gt; on iOS. The JavaScript cursor's &lt;code&gt;position(n)&lt;/code&gt; always landed on row zero. The simulator leaked a prepared statement per query. &lt;code&gt;ThreadSafeDatabase.close()&lt;/code&gt; returned before the close happened, so an immediate delete could race it.&lt;/p&gt;

&lt;p&gt;These were not edge cases around a working abstraction. They meant a query tested in the simulator could have different transaction, cursor, or binding behavior on a device.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;DatabaseConformanceSuite&lt;/code&gt; defines the contract once. Seven device tests exercise lifecycle, statements, cursors, transactions, encryption, and legacy behavior on every port-status target. The current reports show those tests passing on Android, both iOS renderers, JavaScript, native Linux, native Windows, macOS, tvOS, and watchOS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0FwcGxpY2F0aW9uIGNvZGU8YnIvPmNvbS5jb2RlbmFtZTEuZGJdIC0tPiBCW1BvcnRhYmxlIGRhdGFiYXNlIGNvbnRyYWN0XQogICAgQiAtLT4gQ1tBbmRyb2lkPGJyLz5wbGF0Zm9ybSBTUUxpdGUgb3IgU1FMQ2lwaGVyXQogICAgQiAtLT4gRFtpT1MgYW5kIEFwcGxlIHRhcmdldHM8YnIvPnN5c3RlbSBTUUxpdGUgb3IgY2lwaGVyIGVuZ2luZV0KICAgIEIgLS0-IEVbSmF2YVNjcmlwdDxici8-U1FMaXRlIGluIFdlYkFzc2VtYmx5XQogICAgQiAtLT4gRltXaW5kb3dzIGFuZCBMaW51eDxici8-bmF0aXZlIFNRTGl0ZV0KICAgIEIgLS0-IEdbU2ltdWxhdG9yPGJyLz5TUUxpdGUgSkRCQ10KICAgIEhbT25lIGNvbmZvcm1hbmNlIHN1aXRlXSAtLT4gQwogICAgSCAtLT4gRAogICAgSCAtLT4gRQogICAgSCAtLT4gRgogICAgSCAtLT4gRw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW0FwcGxpY2F0aW9uIGNvZGU8YnIvPmNvbS5jb2RlbmFtZTEuZGJdIC0tPiBCW1BvcnRhYmxlIGRhdGFiYXNlIGNvbnRyYWN0XQogICAgQiAtLT4gQ1tBbmRyb2lkPGJyLz5wbGF0Zm9ybSBTUUxpdGUgb3IgU1FMQ2lwaGVyXQogICAgQiAtLT4gRFtpT1MgYW5kIEFwcGxlIHRhcmdldHM8YnIvPnN5c3RlbSBTUUxpdGUgb3IgY2lwaGVyIGVuZ2luZV0KICAgIEIgLS0-IEVbSmF2YVNjcmlwdDxici8-U1FMaXRlIGluIFdlYkFzc2VtYmx5XQogICAgQiAtLT4gRltXaW5kb3dzIGFuZCBMaW51eDxici8-bmF0aXZlIFNRTGl0ZV0KICAgIEIgLS0-IEdbU2ltdWxhdG9yPGJyLz5TUUxpdGUgSkRCQ10KICAgIEhbT25lIGNvbmZvcm1hbmNlIHN1aXRlXSAtLT4gQwogICAgSCAtLT4gRAogICAgSCAtLT4gRQogICAgSCAtLT4gRgogICAgSCAtLT4gRw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1342" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cursor navigation now derives from two primitives, &lt;code&gt;rewind()&lt;/code&gt; and &lt;code&gt;stepForward()&lt;/code&gt;, instead of being reimplemented on every port. Transactions return to autocommit after either commit or rollback. Blobs and typed parameters behave consistently. Windows and Linux finally open a database instead of returning &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fixing these inconsistencies changes behavior that some existing applications may rely on. A cursor index or transaction quirk can become part of application code even when the behavior was accidental. The &lt;code&gt;db.legacy&lt;/code&gt; build hint keeps the previous implementations available during migration.&lt;/p&gt;

&lt;p&gt;Older Ant projects default to legacy mode. They may be stable applications that only need another maintenance release, and forcing a database migration on them would be reckless. Newer Maven projects default to the corrected contract so new code does not inherit the old problems. Either project type can set &lt;code&gt;db.legacy=true&lt;/code&gt; for the old behavior or &lt;code&gt;db.legacy=false&lt;/code&gt; for the corrected contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  DatabaseConfig opens the same encrypted format everywhere
&lt;/h2&gt;

&lt;p&gt;Encryption is selected by passing a &lt;code&gt;DatabaseConfig&lt;/code&gt; when the database opens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEncryptionSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;DatabaseConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;managed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openOrCreate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"secure.db"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Read and write through db.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;wipe&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three key models:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key model&lt;/th&gt;
&lt;th&gt;Where the key comes from&lt;/th&gt;
&lt;th&gt;Good fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DatabaseConfig.passphrase(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A secret supplied by the user or server&lt;/td&gt;
&lt;td&gt;Data that must move to another device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DatabaseConfig.managed()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A random key stored in Android Keystore or the iOS keychain&lt;/td&gt;
&lt;td&gt;Local data with nobody to prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DatabaseConfig.rawKey(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thirty-two random bytes supplied by the application&lt;/td&gt;
&lt;td&gt;A key managed by an existing backend or protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A passphrase compiled into the application is recoverable from the shipped binary. It is not a useful secret. A managed key avoids that mistake, but it also changes the recovery story: if the key-store entry is lost, the database is unreadable. Use a user-held or server-held passphrase when the data must survive a device loss.&lt;/p&gt;

&lt;p&gt;Existing plaintext databases can be converted without replacing the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEncrypted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"customer.db"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"customer.db"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;managed&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The on-disk parameters are fixed to the SQLCipher 4 format: AES-256-CBC, PBKDF2-HMAC-SHA512 with 256,000 iterations, 4,096-byte pages, and per-page HMAC-SHA512. CI writes a database with the Codename One engine and reads it with the stock &lt;code&gt;sqlcipher&lt;/code&gt; client, then reverses the direction. That cross-engine test catches a class of failure where every port can read its own incompatible file.&lt;/p&gt;

&lt;p&gt;Encryption protects data at rest. It does not protect an open database from a debugger, a memory dump, or code already running on a compromised device. That boundary is why this work sits beside App Shield and App Hardening instead of replacing either one.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript gets SQLite instead of a removed browser API
&lt;/h2&gt;

&lt;p&gt;The JavaScript port used WebSQL because browsers once exposed it as a convenient SQL-shaped store. Chrome removed WebSQL in version 119. Firefox never shipped it. A portable API cannot depend on a feature that no longer exists in one browser and never existed in another.&lt;/p&gt;

&lt;p&gt;JavaScript now runs SQLite compiled to WebAssembly and stores the database in the browser's storage pool. It passes the same statement, cursor, transaction, encryption, and compatibility tests as the native ports.&lt;/p&gt;

&lt;p&gt;The new engine cannot read an old WebSQL store. &lt;code&gt;openOrCreate()&lt;/code&gt; refuses to create an empty database when it detects that old data, because silently replacing a user's database with an empty state is worse than a visible migration. An application with WebSQL-era users must export from a build that can still read the old store, or deliberately opt into a new empty database with &lt;code&gt;cn1.db.ignoreLegacyWebSql=true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The build also remains pay-for-what-you-use. An application that never references &lt;code&gt;com.codename1.db&lt;/code&gt; gets no database engine. A plain JavaScript database adds roughly 1.5 MB of WebAssembly. Referencing &lt;code&gt;DatabaseConfig&lt;/code&gt; adds the cipher implementation where the platform needs it.&lt;/p&gt;

&lt;p&gt;On Android, encryption raises the minimum SDK to 23 and requires AndroidX because those are SQLCipher's requirements. The unencrypted database path keeps the older floor.&lt;/p&gt;

&lt;p&gt;That covers the database work. Six more changes shipped this week, each with its own detailed post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The watch and phone now run as separate applications
&lt;/h2&gt;

&lt;p&gt;A watch is not a small second window owned by the phone process. It has its own executable, storage, startup sequence, and failure modes. The &lt;a href="https://www.codenameone.com/blog/watch-apps-phone-channel/" rel="noopener noreferrer"&gt;watch applications post&lt;/a&gt; covers the complete model introduced by &lt;a href="https://github.com/codenameone/CodenameOne/pull/5487" rel="noopener noreferrer"&gt;PR #5487&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One &lt;code&gt;codename1.watchMain&lt;/code&gt; setting adds the Apple Watch companion entry point. On Android, &lt;code&gt;codename1.watchStandalone=true&lt;/code&gt; builds that entry point as the Wear OS product instead of the phone application; a companion Wear artifact beside the phone APK is not generated yet. The simulator can launch the phone and watch as separate processes and connect them on the desktop. The pair shares source, resources, CSS, and the surfaces model. It does not share &lt;code&gt;Storage&lt;/code&gt;, &lt;code&gt;Preferences&lt;/code&gt;, or SQLite.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WearableConnection&lt;/code&gt; models the three transports the platforms actually provide. &lt;code&gt;sendMessage()&lt;/code&gt; asks a live peer for an immediate reply. &lt;code&gt;putData()&lt;/code&gt; replicates the latest state and survives sleep or relaunch. &lt;code&gt;transferFile()&lt;/code&gt; moves a larger payload in the background. The API uses &lt;code&gt;WCSession&lt;/code&gt; on Apple platforms and the Wearable Data Layer on Android.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WearableConnection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WearableMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/steps"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stepCount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"goalReached"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stepCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complications join the existing surfaces vocabulary through watch-specific &lt;code&gt;WidgetSize&lt;/code&gt; families. This reuses the same content and timeline model already used for widgets and Live Activities. The generated watchOS complication target and Wear OS complication service are not part of this release yet. Android also supports standalone Wear apps today, but does not yet generate a companion Wear artifact beside a phone APK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser search now works in the JavaScript port
&lt;/h2&gt;

&lt;p&gt;The JavaScript port has always rendered through a canvas. That gives Codename One control over layout and keeps the UI consistent, but it also turned visible text into pixels. Browser search could not find it. Users could not select it. The browser could not rasterize it at native resolution or expose it as ordinary text.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/javascript-dom-text-search/" rel="noopener noreferrer"&gt;JavaScript text post&lt;/a&gt; explains how &lt;a href="https://github.com/codenameone/CodenameOne/pull/5552" rel="noopener noreferrer"&gt;PR #5552&lt;/a&gt; keeps the canvas renderer and adds two targeted DOM layers above it. The text layer holds visible text runs at the positions Codename One already calculated. The accessibility layer holds the ARIA projection and updates it incrementally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"presentation"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cn1-text-layer"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Selectable, searchable text&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cn1-accessibility-tree"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser never gets to reflow a line or decide where a component belongs. It gets text that is already measured, broken, and positioned. Find-in-page, text selection, accessibility, native input metadata, password-manager hints, and high-DPI rendering can then use browser machinery without changing application layout.&lt;/p&gt;

&lt;p&gt;Drag selection is still off because the text layer cannot take pointer events without changing canvas hit testing. Shape-clipped and transformed text stays on the canvas. Same-form occlusion, such as a sheet over text in the underlying form, also needs more work.&lt;/p&gt;

&lt;h2&gt;
  
  
  One API for HomeKit, Matter, and Google Home
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/smart-home-homekit-matter/" rel="noopener noreferrer"&gt;smart-home post&lt;/a&gt; introduces &lt;code&gt;com.codename1.home&lt;/code&gt; from &lt;a href="https://github.com/codenameone/CodenameOne/pull/5554" rel="noopener noreferrer"&gt;PR #5554&lt;/a&gt;, a common model for HomeKit, Matter, and Google Home. An accessory contains services. Services expose canonical traits such as &lt;code&gt;ON_OFF&lt;/code&gt;, &lt;code&gt;BRIGHTNESS&lt;/code&gt;, and &lt;code&gt;TARGET_TEMPERATURE&lt;/code&gt;. Platform identifiers and unit conventions stay in the port.&lt;/p&gt;

&lt;p&gt;That sounds like a normal abstraction until the platforms disagree. Matter and HomeKit express covering position in opposite directions. Matter has no single thermostat setpoint in automatic mode. Android can commission a Matter accessory into Google Home with little setup, but reading the accessory graph requires Google Home developer registration that Codename One cannot create for you.&lt;/p&gt;

&lt;p&gt;The API reports that default Android state as &lt;code&gt;COMMISSIONING_ONLY&lt;/code&gt;, not &lt;code&gt;AVAILABLE&lt;/code&gt;. On iOS it can read and write HomeKit traits, run scenes, and commission Matter accessories. The simulator, desktop ports, and JavaScript run a deliberately awkward synthetic house so missing values, partial failures, and polling behavior can be tested without hardware.&lt;/p&gt;

&lt;p&gt;Automations, background accessory events, camera streams, alarm panels, and the Google Home accessory graph on Android are outside this release. Each gap has a capability query or a specific availability state instead of turning into an empty list that looks like a real answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tapjacking protection continues our security hardening work
&lt;/h2&gt;

&lt;p&gt;We are continuing the push to make Codename One the secure default for application developers. The &lt;a href="https://www.codenameone.com/blog/tapjacking-protection/" rel="noopener noreferrer"&gt;tapjacking protection post&lt;/a&gt; covers the input side of that work. Tapjacking happens when another Android application draws over a sensitive screen and changes what the user believes a tap will do. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5553" rel="noopener noreferrer"&gt;PR #5553&lt;/a&gt; adds a &lt;code&gt;TapjackingPolicy&lt;/code&gt; to &lt;code&gt;DeviceIntegrity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTapjackingProtection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TapjackingPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BLOCK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTapjackingListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TRUE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSource&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;showOverlayWarning&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isHideOverlayWindowsSupported&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;DeviceIntegrity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHideOverlayWindows&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;REPORT&lt;/code&gt; observes without changing input. &lt;code&gt;BLOCK&lt;/code&gt; drops a gesture that begins fully obscured. &lt;code&gt;STRICT&lt;/code&gt; also drops partially obscured gestures, which can include ordinary system UI and therefore carries a real false-positive cost.&lt;/p&gt;

&lt;p&gt;Detection is touch-driven because Android supplies the obscured state on &lt;code&gt;MotionEvent&lt;/code&gt;. An overlay that appears without a touch is not detected by polling. On Android 12 and newer, &lt;code&gt;setHideOverlayWindows(true)&lt;/code&gt; prevents overlay windows instead of reacting to their touches. iOS does not let one application draw over another, so the policy is an Android-only control rather than a fake cross-platform checkbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan a barcode without rebuilding the camera pipeline
&lt;/h2&gt;

&lt;p&gt;The on-device vision work added barcode recognition, face and pose detection, text recognition, segmentation, document scanning, and image labeling. It also exposed every piece of the camera-to-analyzer pipeline. That was useful for custom camera products and needlessly low-level for common cases.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/camera-vision-scanners/" rel="noopener noreferrer"&gt;camera and vision post&lt;/a&gt; covers the higher-level APIs in &lt;a href="https://github.com/codenameone/CodenameOne/pull/5575" rel="noopener noreferrer"&gt;PR #5575&lt;/a&gt;. &lt;code&gt;CodeScanner.scan()&lt;/code&gt; owns a complete scanner screen and returns one asynchronous result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CodeScanner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;urlField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;except&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VisionCameraView&lt;/code&gt; packages the same pipeline as a component for a form you own. Typed &lt;code&gt;BarcodeFormat&lt;/code&gt;, &lt;code&gt;FaceLandmarks&lt;/code&gt;, and &lt;code&gt;PoseLandmarks&lt;/code&gt; constants replace string literals. &lt;code&gt;VisionRect.toBounds(...)&lt;/code&gt; and &lt;code&gt;VisionPoint.toPoint(...)&lt;/code&gt; convert normalized analyzer geometry into component coordinates. &lt;code&gt;SegmentationMask.cutOut(...)&lt;/code&gt; turns selfie segmentation into a usable image operation.&lt;/p&gt;

&lt;p&gt;The preview remains a native peer. Components cannot be painted over it uniformly on every target, so controls and reticles should sit around it. One camera session can be open at a time. The simulator now scripts vision results, which lets the flow, overlay geometry, cancellation, and error paths run before the application reaches a device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expose an intent to Siri, Spotlight, and Shortcuts
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/app-intents-siri-spotlight-shortcuts/" rel="noopener noreferrer"&gt;App Intents post&lt;/a&gt; shows how to expose a Java method to Siri, Spotlight, Shortcuts, or an Android launcher shortcut. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5559" rel="noopener noreferrer"&gt;PR #5559&lt;/a&gt; puts the declaration in Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@AppIntent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"log_workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Log a workout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Log a workout in ${applicationName}"&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt; &lt;span class="nf"&gt;logWorkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@IntentParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"minutes"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;WorkoutStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;IntentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;spoken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logged "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" minutes."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Maven plugin reads the compiled bytecode and generates a reflection-free dispatch table plus the native declarations. The direct static call matters on iOS, where runtime annotation lookup is unavailable and dead-code elimination can remove a handler that has no Java caller.&lt;/p&gt;

&lt;p&gt;Entities let the platform ask the user which application object they meant. &lt;code&gt;Intents.index(...)&lt;/code&gt; publishes those objects to device search. &lt;code&gt;opensRoute&lt;/code&gt; connects an intent to the existing route table when the result should foreground a screen.&lt;/p&gt;

&lt;p&gt;Android is not presented as Siri parity. It gets launcher shortcuts, donation, indexing, and headless execution. Voice invocation, system disambiguation, and spoken assistant results are iOS capabilities. &lt;code&gt;Intents.invoke(...)&lt;/code&gt; still works on every port as an internal command layer, even when the operating system exposes no intent surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security now covers storage, code, backend trust, and input
&lt;/h2&gt;

&lt;p&gt;This week closes several gaps that used to be explained away as platform differences. SQLite now has one documented contract and one portable encrypted format. The watch and phone are separate applications with a defined channel. Smart-home traits and app intents project one application model onto native system services without pretending those services are identical. The web port keeps our renderer while restoring browser behavior users expect.&lt;/p&gt;

&lt;p&gt;App Shield moves trust decisions to the backend. App Hardening raises the cost of reading and modifying the shipped binary. Encrypted SQLite protects stored data. Tapjacking protection rejects a class of misleading input on Android. None of these controls makes a compromised device trustworthy. Together they cover more of the path from stored bytes to the tap that authorizes an operation.&lt;/p&gt;

&lt;p&gt;We want Codename One to lead cross-platform development on security. That requires more than adding isolated security switches. The safe path must be available from ordinary application code, included only when used, and tested on every target we claim to support. This week's encrypted database and tapjacking work move two more boundaries into that default path.&lt;/p&gt;

&lt;p&gt;Start with the &lt;a href="https://www.codenameone.com/developer-guide/#sql-encryption" rel="noopener noreferrer"&gt;database guide&lt;/a&gt; if you have data at rest to migrate. Existing Ant applications should set &lt;code&gt;db.legacy&lt;/code&gt; explicitly before moving to Maven. Applications with WebSQL-era browser data need an export plan before taking the new JavaScript engine.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>App Hardening: One Obfuscation Pipeline Across Every Port</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 22 Aug 2026 13:30:18 +0000</pubDate>
      <link>https://dev.to/codenameone/app-hardening-one-obfuscation-pipeline-across-every-port-ani</link>
      <guid>https://dev.to/codenameone/app-hardening-one-obfuscation-pipeline-across-every-port-ani</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3u2zhkyfaxczlvbcu9pi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3u2zhkyfaxczlvbcu9pi.jpg" alt="Source code passing through a hardened build pipeline before splitting into mobile, web, and desktop binaries" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Obfuscating only the Android artifact is a poor security model for a cross-platform application. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5527" rel="noopener noreferrer"&gt;Codename One App Hardening&lt;/a&gt; transforms the merged application before it splits into Android, iOS, JavaScript, Windows, Linux, and desktop builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We set the target at DexGuard-class resistance: rename useful symbols, remove plaintext application strings where the target permits it, distort selected control flow, and keep crash reports readable. This is not a claim that reverse engineering becomes impossible. It is a commitment to make the same security decision cover the whole application instead of leaving every port to a different tool and configuration.&lt;/p&gt;

&lt;p&gt;This post continues &lt;a href="https://www.codenameone.com/blog/third-generation-gui-builder/" rel="noopener noreferrer"&gt;this week's release overview and GUI Builder rewrite&lt;/a&gt; and &lt;a href="https://www.codenameone.com/blog/app-shield-server-attestation/" rel="noopener noreferrer"&gt;last week's App Shield release&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One transform before six platform builds
&lt;/h2&gt;

&lt;p&gt;App Hardening runs on the merged application JAR inside the cloud build service. That placement matters. The engine sees application classes and bundled libraries before ParparVM translates bytecode to C, before R8 handles Android, and before the JavaScript or native desktop backends consume the program.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW01lcmdlZCBhcHBsaWNhdGlvbiBKQVJdIC0tPiBCW0RlbXV4IGFwcGxpY2F0aW9uIGFuZCBsaWJyYXJpZXNdCiAgICBCIC0tPiBDW1JlbmFtZSBjbGFzc2VzIGFuZCBtZW1iZXJzPGJyLz5leGNlcHQgQW5kcm9pZCwgd2hlcmUgUjggcmVtYWlucyB0aGUgcmVuYW1lcl0KICAgIEMgLS0-IERbRW5jcnlwdCBlbGlnaWJsZSBzdHJpbmcgY29uc3RhbnRzXQogICAgRCAtLT4gRVtBcHBseSBjb250cm9sLWZsb3cgdHJhbnNmb3Jtczxici8-b25seSBvbiBzYWZlIHRhcmdldHNdCiAgICBFIC0tPiBGW1ZlcmlmeSB0cmFuc2Zvcm1lZCBieXRlY29kZV0KICAgIEYgLS0-IEd7UGxhdGZvcm0gYnVpbGRlcnN9CiAgICBHIC0tPiBIW0FuZHJvaWQgYW5kIFI4XQogICAgRyAtLT4gSVtpT1MgYW5kIFBhcnBhclZNXQogICAgRyAtLT4gSltKYXZhU2NyaXB0XQogICAgRyAtLT4gS1tXaW5kb3dzLCBMaW51eCwgYW5kIEphdmFTRV0KICAgIEYgLS0-IExbTWFwcGluZyBhbmQgYnVpbGQgcmVwb3J0XQogICAgTCAtLT4gTVtDcmFzaCBQcm90ZWN0aW9uIHJldHJhY2Vd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBBW01lcmdlZCBhcHBsaWNhdGlvbiBKQVJdIC0tPiBCW0RlbXV4IGFwcGxpY2F0aW9uIGFuZCBsaWJyYXJpZXNdCiAgICBCIC0tPiBDW1JlbmFtZSBjbGFzc2VzIGFuZCBtZW1iZXJzPGJyLz5leGNlcHQgQW5kcm9pZCwgd2hlcmUgUjggcmVtYWlucyB0aGUgcmVuYW1lcl0KICAgIEMgLS0-IERbRW5jcnlwdCBlbGlnaWJsZSBzdHJpbmcgY29uc3RhbnRzXQogICAgRCAtLT4gRVtBcHBseSBjb250cm9sLWZsb3cgdHJhbnNmb3Jtczxici8-b25seSBvbiBzYWZlIHRhcmdldHNdCiAgICBFIC0tPiBGW1ZlcmlmeSB0cmFuc2Zvcm1lZCBieXRlY29kZV0KICAgIEYgLS0-IEd7UGxhdGZvcm0gYnVpbGRlcnN9CiAgICBHIC0tPiBIW0FuZHJvaWQgYW5kIFI4XQogICAgRyAtLT4gSVtpT1MgYW5kIFBhcnBhclZNXQogICAgRyAtLT4gSltKYXZhU2NyaXB0XQogICAgRyAtLT4gS1tXaW5kb3dzLCBMaW51eCwgYW5kIEphdmFTRV0KICAgIEYgLS0-IExbTWFwcGluZyBhbmQgYnVpbGQgcmVwb3J0XQogICAgTCAtLT4gTVtDcmFzaCBQcm90ZWN0aW9uIHJldHJhY2Vd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1211" height="1110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5527" rel="noopener noreferrer"&gt;PR #5527&lt;/a&gt; carries the open-source engine, retrace support, client API, build preflight, crash payload changes, and documentation. &lt;a href="https://github.com/codenameone/BuildDaemon/pull/173" rel="noopener noreferrer"&gt;BuildDaemon PR #173&lt;/a&gt; wires the engine into the cloud builders and enforces the Enterprise entitlement on the server.&lt;/p&gt;

&lt;p&gt;The server gate is intentional. A non-Enterprise build that requests hardening fails with an explanation. It never returns an ordinary binary that looks protected because the client asked for protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn it on with one level
&lt;/h2&gt;

&lt;p&gt;For most projects the only setting is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.harden.level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The levels are cumulative:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Renaming&lt;/th&gt;
&lt;th&gt;String encryption&lt;/th&gt;
&lt;th&gt;Control flow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;off&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;standard&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Constant strings&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aggressive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;All eligible strings&lt;/td&gt;
&lt;td&gt;One opaque-predicate guard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;paranoid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;All eligible strings&lt;/td&gt;
&lt;td&gt;Two guards per eligible method&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can override the individual transforms with &lt;code&gt;harden.rename&lt;/code&gt;, &lt;code&gt;harden.strings&lt;/code&gt;, and &lt;code&gt;harden.controlFlow&lt;/code&gt;. A per-platform &lt;code&gt;harden.&amp;lt;platform&amp;gt;.enabled=false&lt;/code&gt; hint opts one target out. An unknown level fails the build instead of becoming &lt;code&gt;off&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Classes resolved by name need a keep rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.harden.keep&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;-keep class com.example.payment.NativeGateway { *; }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine already keeps the application entry point, generated bootstraps, and native-interface peers. Codename One does not use runtime reflection to resolve ordinary application classes, which removes a large source of keep-rule guesswork. A third-party library that loads package-relative resources can still need an explicit rule because its package name changes while the resource path does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The port matrix is deliberately uneven
&lt;/h2&gt;

&lt;p&gt;Applying every transform everywhere would produce a larger binary without adding protection, or break a backend optimizer. App Hardening uses one policy but adapts the mechanics:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transform&lt;/th&gt;
&lt;th&gt;Applied to&lt;/th&gt;
&lt;th&gt;Why not everywhere&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class, method, and field renaming&lt;/td&gt;
&lt;td&gt;iOS, JavaScript, Windows, Linux, JavaSE&lt;/td&gt;
&lt;td&gt;Android keeps R8 as its only renamer to avoid chained renaming during the build.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String encryption&lt;/td&gt;
&lt;td&gt;iOS, Android, Windows, Linux, JavaSE&lt;/td&gt;
&lt;td&gt;JavaScript strings can be live references passed through the native bridge.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control-flow obfuscation&lt;/td&gt;
&lt;td&gt;Android and JavaSE&lt;/td&gt;
&lt;td&gt;It conflicts with ParparVM optimization and inflates JavaScript output. Constructors are excluded.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The renaming dictionary uses a &lt;code&gt;zq&lt;/code&gt; prefix rather than the familiar &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt;. Short names can appear inside ParparVM's generated native identifiers and confuse dead-code elimination. The longer prefix keeps the names opaque without disabling that optimizer.&lt;/p&gt;

&lt;p&gt;A mapping looks like this in the retrace tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.MyForm -&amp;gt; zqaaaa:
    10:10:void onSave():42:42 -&amp;gt; zqa
com.example.util.Helper -&amp;gt; zqaaab:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mapping gets a build-specific ID and stays on the server for Crash Protection. Hardened stack traces carry that ID, the raw stack, trace format, and hardening level. The server retraces the report before filing the GitHub issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  String encryption has sharp edges
&lt;/h2&gt;

&lt;p&gt;Renaming hides labels. String encryption removes eligible application literals from the shipped binary and synthesizes a decoder inside each class with a per-class key. There is no single framework decoder for an attacker to hook.&lt;/p&gt;

&lt;p&gt;The engine handles both ordinary &lt;code&gt;LDC&lt;/code&gt; literals and &lt;code&gt;static final String&lt;/code&gt; values stored in the class-file &lt;code&gt;ConstantValue&lt;/code&gt; attribute. It also reports strings it cannot transform.&lt;/p&gt;

&lt;p&gt;Three boundaries matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Annotation values remain in annotation metadata and stay readable.&lt;/li&gt;
&lt;li&gt;JDK 9 style &lt;code&gt;invokedynamic&lt;/code&gt; string-concatenation recipes can keep literal fragments outside ordinary &lt;code&gt;LDC&lt;/code&gt; instructions. The build report counts those sites. Compiling with &lt;code&gt;-XDstringConcat=inline&lt;/code&gt; moves them back into instructions the engine can transform.&lt;/li&gt;
&lt;li&gt;An encrypted literal is value-equal to the same unencrypted framework string but may not be the same object. Compare strings with &lt;code&gt;.equals()&lt;/code&gt;, not &lt;code&gt;==&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this turns a client-side constant into a secret. If the app can decrypt a value, an attacker controlling the process can eventually observe it. Credentials and signing secrets still belong on a server or in platform-backed secure storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Control flow is a cost, not a badge
&lt;/h2&gt;

&lt;p&gt;Aggressive and paranoid levels add opaque predicates to eligible methods. A decompiler must keep branches that the application can resolve at runtime but static analysis cannot fold away.&lt;/p&gt;

&lt;p&gt;The engine skips constructors and checks method growth before adding bytecode. It verifies the transformed classes with ASM's &lt;code&gt;CheckClassAdapter&lt;/code&gt; before any platform builder sees them. The build report identifies methods or literals that were skipped instead of claiming a transform covered bytes it did not touch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;paranoid&lt;/code&gt; doubles the eligible control-flow guards. It also increases size and analysis complexity inside your own build. Start with &lt;code&gt;standard&lt;/code&gt;, test a release build, then raise the level for code where the added cost has a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crash reports remain part of the contract
&lt;/h2&gt;

&lt;p&gt;Obfuscation without retrace trades one security problem for an operational one. A production crash that says &lt;code&gt;zqaaaa.zqa()&lt;/code&gt; does not help the team responsible for fixing it.&lt;/p&gt;

&lt;p&gt;App Hardening and Crash Protection share the mapping lifecycle. Mapping upload is required by default. If upload fails, the build fails rather than ship an artifact whose future crashes cannot be decoded.&lt;/p&gt;

&lt;p&gt;Application code can inspect the stamped result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hardening&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isHardened&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hardening level: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Hardening&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLevel&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mapping: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Hardening&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMappingId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simulator and local source builds report &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;off&lt;/code&gt; because they are never hardened. A local target fails preflight unless you explicitly set &lt;code&gt;harden.allowUnhardenedLocalBuild=true&lt;/code&gt;. That escape hatch permits the build; it does not pretend to protect it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening, Shield, and encrypted data stop different attacks
&lt;/h2&gt;

&lt;p&gt;Last week's &lt;a href="https://www.codenameone.com/blog/app-shield-server-attestation/" rel="noopener noreferrer"&gt;App Shield&lt;/a&gt; release gives a backend a server-verified attestation token. Patching a local boolean is no longer enough to impersonate a trusted app when the server enforces that token.&lt;/p&gt;

&lt;p&gt;App Hardening works earlier in the attack. It raises the cost of finding the code and constants an attacker wants to patch. App Shield makes a successful local patch insufficient for protected server calls. The open &lt;a href="https://github.com/codenameone/CodenameOne/pull/5526" rel="noopener noreferrer"&gt;portable encrypted database PR&lt;/a&gt; adds the data-at-rest layer with interoperable encrypted SQLite files and keystore-managed keys. That database work is not merged yet, so it is direction rather than part of this release.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0FwcCBIYXJkZW5pbmc8YnIvPmJpbmFyeSBpbnNwZWN0aW9uIGFuZCB0YW1wZXJpbmddIC0tPiBCW0FwcCBTaGllbGQ8YnIvPmFwcC10by1zZXJ2ZXIgdHJ1c3RdCiAgICBCIC0tPiBDW0JhY2tlbmQgYXV0aG9yaXphdGlvbjxici8-YnVzaW5lc3Mgb3BlcmF0aW9uXQogICAgRFtFbmNyeXB0ZWQgZGF0YWJhc2U8YnIvPmRhdGEgYXQgcmVzdCwgUFIgb3Blbl0gLS0-IEE%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0FwcCBIYXJkZW5pbmc8YnIvPmJpbmFyeSBpbnNwZWN0aW9uIGFuZCB0YW1wZXJpbmddIC0tPiBCW0FwcCBTaGllbGQ8YnIvPmFwcC10by1zZXJ2ZXIgdHJ1c3RdCiAgICBCIC0tPiBDW0JhY2tlbmQgYXV0aG9yaXphdGlvbjxici8-YnVzaW5lc3Mgb3BlcmF0aW9uXQogICAgRFtFbmNyeXB0ZWQgZGF0YWJhc2U8YnIvPmRhdGEgYXQgcmVzdCwgUFIgb3Blbl0gLS0-IEE%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1046" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The security lead we are building comes from covering these boundaries together. Release builds already obfuscate by default. Enterprise teams can now add one cross-platform hardening policy, server-enforced attestation, Crash Protection retrace, and, once PR #5526 finishes review, portable database encryption. Each layer has a named failure mode and a testable output.&lt;/p&gt;

&lt;p&gt;App Hardening still cannot stop a determined attacker who controls the device. It does not replace authorization, rate limits, secure key custody, or review of the operation your backend performs. It makes static analysis and casual tampering more expensive across every artifact you ship, which is the job an obfuscation layer can defend.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;standard&lt;/code&gt;, submit a release cloud build, and check the hardening report before moving to a stronger profile. The &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/App-Hardening.asciidoc" rel="noopener noreferrer"&gt;App Hardening guide&lt;/a&gt; documents every hint, exclusion, and local-build boundary.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>The Third-Generation GUI Builder: One Workspace for Every Form</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:39:34 +0000</pubDate>
      <link>https://dev.to/codenameone/the-third-generation-gui-builder-one-workspace-for-every-form-4mdh</link>
      <guid>https://dev.to/codenameone/the-third-generation-gui-builder-one-workspace-for-every-form-4mdh</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9pq4m50y5nnduhh2edu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9pq4m50y5nnduhh2edu.jpg" alt="A visual editor connecting project forms, CSS, Java source, and a live Codename One canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have rebuilt the Codename One GUI Builder again. This is its third generation. The interesting part is not another drag-and-drop surface. It is what we kept, what Maven broke, and why a visual editor must understand the whole project instead of opening one generated form at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a smaller rewrite than the second generation Steve Hannah built. His guided layout work remains the foundation. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5523" rel="noopener noreferrer"&gt;PR #5523&lt;/a&gt; replaces the surrounding shell with a Maven-first Codename One application that moves between forms, CSS, and Java without leaving the workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  This week in one page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
The third-generation GUI Builder is now a project workspace launched with &lt;code&gt;mvn cn1:guibuilder&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codenameone.com/blog/app-hardening-cross-platform/" rel="noopener noreferrer"&gt;App Hardening&lt;/a&gt; applies renaming, string encryption, and selected control-flow transforms before the platform builds split. The follow-up publishes Saturday.&lt;/li&gt;
&lt;li&gt;
The iOS on-device debugger no longer crashes when a reused local slot is mistaken for an object.&lt;/li&gt;
&lt;li&gt;
Port status and Linux video now report skipped tests instead of counting them as passes. The same work fixed three GStreamer defects.&lt;/li&gt;
&lt;li&gt;
Google Sign-In moved to version 7.1 so Apple Silicon simulators get a real arm64 simulator slice.&lt;/li&gt;
&lt;li&gt;
ParparVM memory reclamation now returns surplus BiBOP pages to the operating system after a small-object peak.&lt;/li&gt;
&lt;li&gt;
Missing and legacy URLs now lead to recovery pages instead of a bare 404.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three builders, three different projects
&lt;/h2&gt;

&lt;p&gt;The first GUI Builder grew out of tooling we built at Sun Microsystems. It stored forms inside the resource file and generated a central state machine. That model made sense when Codename One still targeted feature phones with roughly 2 MB of RAM.&lt;/p&gt;

&lt;p&gt;The second generation separated a form into readable &lt;code&gt;.gui&lt;/code&gt; XML and companion Java. Steve built auto layout on top of &lt;code&gt;LayeredLayout&lt;/code&gt;, including smart insets, sibling references, matching sizes, baseline alignment, and multi-selection. It was a much better model for a modern application, but the editor still opened from a selected form.&lt;/p&gt;

&lt;p&gt;That last assumption became painful after the Maven migration. A Maven project has forms under &lt;code&gt;src/main/guibuilder&lt;/code&gt;, Java under &lt;code&gt;src/main/java&lt;/code&gt;, and styling in &lt;code&gt;src/main/css/theme.css&lt;/code&gt;. Launching a separate editor for every generated form turned navigation into the slow part of a visual tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0dlbmVyYXRpb24gMTxici8-cmVzb3VyY2UgZmlsZSBhbmQgc3RhdGUgbWFjaGluZV0gLS0-IEJbR2VuZXJhdGlvbiAyPGJyLz5vbmUgLmd1aSBmb3JtIGFuZCBndWlkZWQgbGF5b3V0XQogICAgQiAtLT4gQ1tHZW5lcmF0aW9uIDM8YnIvPm9uZSBNYXZlbiBwcm9qZWN0IHdvcmtzcGFjZV0KICAgIEMgLS0-IERbRm9ybXNdCiAgICBDIC0tPiBFW0xpdmUgQ1NTXQogICAgQyAtLT4gRltQcm90ZWN0ZWQgSmF2YSByZWdpb25zXQogICAgQyAtLT4gR1tSZXNwb25zaXZlIGNhbnZhc10%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBW0dlbmVyYXRpb24gMTxici8-cmVzb3VyY2UgZmlsZSBhbmQgc3RhdGUgbWFjaGluZV0gLS0-IEJbR2VuZXJhdGlvbiAyPGJyLz5vbmUgLmd1aSBmb3JtIGFuZCBndWlkZWQgbGF5b3V0XQogICAgQiAtLT4gQ1tHZW5lcmF0aW9uIDM8YnIvPm9uZSBNYXZlbiBwcm9qZWN0IHdvcmtzcGFjZV0KICAgIEMgLS0-IERbRm9ybXNdCiAgICBDIC0tPiBFW0xpdmUgQ1NTXQogICAgQyAtLT4gRltQcm90ZWN0ZWQgSmF2YSByZWdpb25zXQogICAgQyAtLT4gR1tSZXNwb25zaXZlIGNhbnZhc10%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1170" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The third generation changes the unit of work from a file to a project. It scans every &lt;code&gt;.gui&lt;/code&gt; file, keeps them in the left panel, renders the selected form in the center, and shows its properties, layout, and events on the right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit4t9w23e0i1ma77r72r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit4t9w23e0i1ma77r72r.png" alt="The third-generation GUI Builder with project forms, component palette, live canvas, and inspector" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One Maven goal opens the project
&lt;/h2&gt;

&lt;p&gt;Create a form and open the editor from the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn cn1:create-gui-form &lt;span class="nt"&gt;-DclassName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example.ProfileForm
mvn cn1:guibuilder &lt;span class="nt"&gt;-DclassName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example.ProfileForm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first goal creates the pair that belongs in version control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;common/src/main/guibuilder/com/example/ProfileForm.gui
common/src/main/java/com/example/ProfileForm.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second goal resolves &lt;code&gt;com.codenameone:codenameone-guibuilder&lt;/code&gt; through Maven and passes a project binding to the editor. IntelliJ IDEA, NetBeans, Eclipse, and Visual Studio Code now ship shortcuts that invoke the same goal. The editor itself remains a Java 8 artifact, so it runs on the JDK that already builds the application.&lt;/p&gt;

&lt;p&gt;Switching forms no longer starts another process. The Forms tab is backed by a recursive scan of &lt;code&gt;src/main/guibuilder&lt;/code&gt;, so a project with twenty forms behaves like a project with twenty forms, not twenty unrelated editor sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guided layout survived the rewrite
&lt;/h2&gt;

&lt;p&gt;We did not replace Steve's layout model with absolute coordinates. A component dropped in auto layout mode still becomes a &lt;code&gt;LayeredLayout&lt;/code&gt; child whose insets can refer to the parent or another named component.&lt;/p&gt;

&lt;p&gt;Here is a trimmed form from the builder's demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"GuidedLayoutForm"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Form"&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"LayeredLayout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"heroTitle"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Label"&lt;/span&gt;
        &lt;span class="na"&gt;layeredinsets=&lt;/span&gt;&lt;span class="s"&gt;"24px auto auto 24px"&lt;/span&gt;
        &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Guided Layout"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"SpanLabel"&lt;/span&gt;
        &lt;span class="na"&gt;layeredinsets=&lt;/span&gt;&lt;span class="s"&gt;"12px 24px auto 0px"&lt;/span&gt;
        &lt;span class="na"&gt;guidedreferences=&lt;/span&gt;&lt;span class="s"&gt;"heroTitle|-|-|heroTitle"&lt;/span&gt;
        &lt;span class="na"&gt;guidedhorizontalsize=&lt;/span&gt;&lt;span class="s"&gt;"fill"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;component&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"secondary"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Button"&lt;/span&gt;
        &lt;span class="na"&gt;guidedreferences=&lt;/span&gt;&lt;span class="s"&gt;"primary|primary|-|primary"&lt;/span&gt;
        &lt;span class="na"&gt;guidedhorizontalsize=&lt;/span&gt;&lt;span class="s"&gt;"match"&lt;/span&gt;
        &lt;span class="na"&gt;guidedmatchwidth=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those names matter. A guide stored as an object pointer would disappear after a save and reload. A guide stored by component name survives on disk, which means the model must reject duplicate names and update references when you rename, delete, or paste a component.&lt;/p&gt;

&lt;p&gt;The canvas adds the relationship while you drag. It can align edges, centers, and text baselines. Resizing can keep a preferred size, fill the parent, stay fixed, or match another component. The same canvas can switch from phone portrait to desktop width, which makes a bad relationship visible before it reaches a device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvx8ufwzcnv4iev0jtfb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvx8ufwzcnv4iev0jtfb.gif" alt="Moving the primary action beside the description updates its guided-layout relationship" width="480" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The move above is not stored as a new set of absolute coordinates. It changes the relationship between &lt;code&gt;primary&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt;, then lets &lt;code&gt;LayeredLayout&lt;/code&gt; resolve the result for the current canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6wxmvn27249gsth6ydf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6wxmvn27249gsth6ydf.gif" alt="The same guided layout switching between phone portrait and desktop canvases" width="480" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS belongs beside the canvas
&lt;/h2&gt;

&lt;p&gt;The second-generation builder and the old resource editor were separate tools. A user comment on our 2016 GUI Builder post asked for the editor to read the project CSS and render it directly. The third generation finally treats that as the normal workflow.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;CSS&lt;/strong&gt; and the project stylesheet opens beside the live form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2r5dvs00k44dcnqhdkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2r5dvs00k44dcnqhdkm.png" alt="Editing the project CSS beside the live form preview" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pane edits the real &lt;code&gt;src/main/css/theme.css&lt;/code&gt;. After the edit debounce, the CSS compiler installs the new theme and rebuilds the preview. A selector change is visible where it matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2459b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#315fce&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2mm&lt;/span&gt; &lt;span class="m"&gt;4mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no second theme model to keep in sync. The stylesheet that colors the canvas is the stylesheet Maven compiles for the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generated Java is visible, but not disposable
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Code&lt;/strong&gt; button opens the companion Java source in the same workspace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wubhx9ye4yqn0qllprk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wubhx9ye4yqn0qllprk.png" alt="The companion Java source with generated and user-owned regions" width="680" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generated code and user code have different ownership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// &amp;lt;gui-builder-generated&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;signIn&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;buildUI&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;signIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sign in"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;signIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"signIn"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signIn&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;/gui-builder-generated&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// &amp;lt;gui-builder-user-code&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSignIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ActionEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;/gui-builder-user-code&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The embedded editor protects the generated region instead of waiting for Save to overwrite it. Event handlers stay in the user region and survive regeneration. This removes an old timing problem where one IDE generated source on save while another waited for the next build.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.gui&lt;/code&gt; file remains plain XML. You can review it in a pull request and recover it without a proprietary database. The current editor does not watch a hand-edited &lt;code&gt;.gui&lt;/code&gt; file while it is open, so use &lt;strong&gt;Refresh&lt;/strong&gt; after an external edit.&lt;/p&gt;

&lt;h2&gt;
  
  
  One hardening policy before the platform split
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5527" rel="noopener noreferrer"&gt;PR #5527&lt;/a&gt; adds an open-source hardening engine that runs on the merged application before it becomes Android, iOS, JavaScript, Windows, Linux, or JavaSE output. The goal is DexGuard-class resistance without protecting one port and leaving the others exposed.&lt;/p&gt;

&lt;p&gt;The policy is intentionally port-aware. Android keeps R8 as its sole renaming tool. JavaScript skips string encryption because its native bridge can hold live string references. ParparVM skips control-flow transforms that would fight its optimizer. The common pipeline still gives one build hint, one report, and a build-specific mapping connected to Crash Protection.&lt;/p&gt;

&lt;p&gt;Saturday's &lt;a href="https://www.codenameone.com/blog/app-hardening-cross-platform/" rel="noopener noreferrer"&gt;App Hardening deep dive&lt;/a&gt; covers the exact port matrix, string-encryption exclusions, keep rules, retrace lifecycle, and local-build boundary. It also separates hardening from App Shield and the encrypted database work still under review.&lt;/p&gt;

&lt;h2&gt;
  
  
  A breakpoint no longer dereferences a random local
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5536" rel="noopener noreferrer"&gt;PR #5536&lt;/a&gt; fixes the &lt;code&gt;signal 11&lt;/code&gt; crash that could take down an iOS app when NetBeans asked for locals at a breakpoint. One debugger table had one address per JVM slot but one row per declared local. Reusing a slot for an &lt;code&gt;int&lt;/code&gt; and later an object could pair the object row with four bytes of integer storage, then dereference it as an eight-byte object pointer.&lt;/p&gt;

&lt;p&gt;The generated table now stores one address per row. Frame entry clears stale debugger side channels, and object references pass through a Darwin memory-read check before native code dereferences them. The same PR adds real thread enumeration, deferred breakpoint replay, scoped locals, and ready-made on-device debug actions.&lt;/p&gt;

&lt;p&gt;The PR added 66 tests around the generated C and JDWP proxy. It did not include a tethered-device debugging session, so the merge proves the policies and generated code, not every IDE and device combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  A skipped test is not a pass
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5538" rel="noopener noreferrer"&gt;PR #5538&lt;/a&gt; found that screenshot skip markers used output names while the report parser looked only at Java class names. The marker disappeared, and the surrounding start and finish lines counted the test as a pass.&lt;/p&gt;

&lt;p&gt;Correcting that mapping exposed 11 hidden skips on watchOS, six on tvOS, and two or three on every other port. An unknown marker now fails the reporting contract instead of disappearing.&lt;/p&gt;

&lt;p&gt;The same investigation fixed Linux &lt;code&gt;VideoIO&lt;/code&gt;. The CI image lacked the codec plugins it claimed to test. GStreamer returned a partial pipeline plus an error for a missing element, but the port ignored the error. The reader also asked a paused pipeline for a normal sample when the decoded frame was still the pre-roll buffer. Linux now installs the codecs, rejects partial pipelines, pulls the pre-roll frame correctly, and reports only encoders and decoders present in the GStreamer registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Sign-In now runs in an arm64 simulator
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5544" rel="noopener noreferrer"&gt;PR #5544&lt;/a&gt; moves the iOS Google Sign-In integration from the old bundled 5.x framework to &lt;code&gt;GoogleSignIn&lt;/code&gt; 7.1. The old framework had an arm64 device slice but no arm64 simulator slice. On an Apple Silicon Mac, the linker selected the device slice and rejected it.&lt;/p&gt;

&lt;p&gt;Version 7.1 builds from source for the selected SDK. The native bridge now uses completion handlers and the current token API. We stopped at 7.1 because later versions introduce Swift dependencies that would force modular headers into every generated Podfile, including projects unrelated to Google Sign-In.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small-object pages can return to the OS
&lt;/h2&gt;

&lt;p&gt;ParparVM's BiBOP allocator segregates small objects by size class. Before &lt;a href="https://github.com/codenameone/CodenameOne/pull/5540" rel="noopener noreferrer"&gt;PR #5540&lt;/a&gt;, empty pages stayed in that allocator forever. A temporary peak in small objects could therefore crowd out a later image buffer, Metal texture, or glyph atlas even after the small objects were collected.&lt;/p&gt;

&lt;p&gt;The collector now keeps a 4 MB warm pool and releases the slot area of surplus pages. On Apple platforms it uses &lt;code&gt;MADV_FREE_REUSABLE&lt;/code&gt;, which reduces the &lt;code&gt;phys_footprint&lt;/code&gt; value used for memory pressure. A controlled integration test warmed 192 MB of 256-byte objects and then allocated two identical large-buffer sets. The measured peak fell from 466,224 KB to 287,824 KB, while the benchmark geomean stayed at 1.0031 relative to the no-release control.&lt;/p&gt;

&lt;p&gt;Arm64 retains roughly one quarter of each 64 KB allocator page because its 16 KB system page also contains the allocator header. Moving that header would require a larger allocator redesign.&lt;/p&gt;

&lt;h2&gt;
  
  
  A retired URL now has somewhere useful to go
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5546" rel="noopener noreferrer"&gt;PR #5546&lt;/a&gt; adds a &lt;code&gt;/download/&lt;/code&gt; compatibility page for old plugin and Ant-era links. New projects go to Initializr and Getting Started. Existing legacy projects go to the Maven migration guide.&lt;/p&gt;

&lt;p&gt;The general 404 page now offers search plus direct routes to setup, documentation, demos, pricing, community, and legacy downloads. It is marked &lt;code&gt;noindex, nofollow&lt;/code&gt;; normal pages remain indexable. The constrained &lt;code&gt;_redirects&lt;/code&gt; file did not change.&lt;/p&gt;

&lt;p&gt;Initializr now generates projects on Codename One 7.0.265 through &lt;a href="https://github.com/codenameone/CodenameOne/pull/5533" rel="noopener noreferrer"&gt;PR #5533&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The work around the app is part of the app
&lt;/h2&gt;

&lt;p&gt;The GUI Builder rewrite is about keeping control close to the artifact you ship. Forms, CSS, generated Java, and responsive behavior now share one inspectable workspace. The debugger, port reports, memory allocator, login bridge, and recovery pages all remove places where the toolchain previously hid the real state.&lt;/p&gt;

&lt;p&gt;Security follows the same direction. Last week &lt;a href="https://www.codenameone.com/blog/app-shield-server-attestation/" rel="noopener noreferrer"&gt;App Shield&lt;/a&gt; moved the final trust decision from the phone to the backend. Saturday's &lt;a href="https://www.codenameone.com/blog/app-hardening-cross-platform/" rel="noopener noreferrer"&gt;App Hardening post&lt;/a&gt; covers the binary layer. The open &lt;a href="https://github.com/codenameone/CodenameOne/pull/5526" rel="noopener noreferrer"&gt;portable encrypted database PR&lt;/a&gt; addresses data at rest and is still under review.&lt;/p&gt;

&lt;p&gt;These controls solve different problems. Together they move Codename One toward a secure default at each boundary: readable code in the binary, modified clients calling a server, and plaintext data on disk. That is a more useful security lead than one large checkbox with an impressive name.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>App Shield: Your Server Should Not Trust the App Calling It</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 14 Aug 2026 14:11:17 +0000</pubDate>
      <link>https://dev.to/codenameone/app-shield-your-server-should-not-trust-the-app-calling-it-4cpa</link>
      <guid>https://dev.to/codenameone/app-shield-your-server-should-not-trust-the-app-calling-it-4cpa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs1zomue9b97t5kou1ag6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs1zomue9b97t5kou1ag6.jpg" alt="A shield between a mobile application and a protected server API" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any security check that runs only on a phone can be patched out on that phone. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5486" rel="noopener noreferrer"&gt;App Shield&lt;/a&gt; moves the final decision to your server by attaching a short-lived, server-verified attestation token to protected requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Greetings from Thailand. My family dragged me here for a forced vacation. It is a lovely country, but beaches, sunshine, and the sea aren't really my thing. The GitHub Actions downtime didn't help either, so progress was slower than usual this week. Several interesting PRs are still in progress, and we chose not to rush them.&lt;/p&gt;

&lt;h2&gt;
  
  
  This week in one page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
App Shield connects device integrity, certificate pinning, and your backend instead of asking the app to trust its own verdict.&lt;/li&gt;
&lt;li&gt;
OpenType fonts now work as &lt;code&gt;.otf&lt;/code&gt; files across the supported ports. The CSS compiler also catches several font failures before they reach a device.&lt;/li&gt;
&lt;li&gt;
Windows desktop builders are moving to newer machines. This is the older JavaSE Windows target, not the native Win32 target.&lt;/li&gt;
&lt;li&gt;
The Maven repository migration has entered phase two. Newly generated projects now point dependencies and plugins at our R2 repository.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;push.codenameone.com&lt;/code&gt; redirects to the new cloud service on Saturday, August 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A local check is not a security boundary
&lt;/h2&gt;

&lt;p&gt;Codename One App Shield is an Enterprise application-attestation layer. It asks Apple App Attest or Google Play Integrity for a hardware-backed statement, verifies that statement through the Codename One service, and gives the app a short-lived ES256 token. Your backend verifies that token before it performs a sensitive operation.&lt;/p&gt;

&lt;p&gt;We already serve &lt;a href="https://www.codenameone.com/blog/device-integrity-and-app-review/" rel="noopener noreferrer"&gt;several banking customers&lt;/a&gt;, and high-security requirements have shaped Codename One for years. Java is part of that fit. These teams get mature analysis tooling, a familiar type system, and one application codebase to review instead of separate iOS and Android implementations.&lt;/p&gt;

&lt;p&gt;Java is not a security boundary by itself. The build pipeline adds useful friction for an attacker, but determined attackers can still reverse engineer a client they control.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iOS native compilation&lt;/td&gt;
&lt;td&gt;On&lt;/td&gt;
&lt;td&gt;ParparVM translates the application's Java bytecode to C and then builds a native binary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obfuscation&lt;/td&gt;
&lt;td&gt;On&lt;/td&gt;
&lt;td&gt;Removes useful names and makes static inspection harder.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debug flags&lt;/td&gt;
&lt;td&gt;Off in release builds&lt;/td&gt;
&lt;td&gt;Blocks the ordinary production debugging path.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root, jailbreak, Frida, and accessibility checks&lt;/td&gt;
&lt;td&gt;Opt-in&lt;/td&gt;
&lt;td&gt;Adds device-side signals or launch-time gates for specific risks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure screen and clipboard restrictions&lt;/td&gt;
&lt;td&gt;Opt-in&lt;/td&gt;
&lt;td&gt;Reduces capture and clipboard exposure. It is not a keylogger defense.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Shield&lt;/td&gt;
&lt;td&gt;Enterprise opt-in&lt;/td&gt;
&lt;td&gt;Makes your backend act on a server-verified attestation token rather than a client boolean.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction in the last row is the point. A modified app can force a local &lt;code&gt;isDeviceCompromised()&lt;/code&gt; call to return &lt;code&gt;false&lt;/code&gt;. It cannot mint a valid token signed by a key your server trusts.&lt;/p&gt;

&lt;h2&gt;
  
  
  From hardware statement to protected API
&lt;/h2&gt;

&lt;p&gt;App Shield joins the platform attestation provider, the Codename One verification service, and your backend:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ29kZW5hbWUgT25lIGFwcAogICAgcGFydGljaXBhbnQgU2hpZWxkIGFzIEF0dGVzdGF0aW9uIHNlcnZpY2UKICAgIHBhcnRpY2lwYW50IFBsYXRmb3JtIGFzIEFwcCBBdHRlc3Qgb3IgUGxheSBJbnRlZ3JpdHkKICAgIHBhcnRpY2lwYW50IEFQSSBhcyBZb3VyIGJhY2tlbmQKICAgIEFwcC0-PlNoaWVsZDogUmVxdWVzdCBhIG9uZS10aW1lIGNoYWxsZW5nZQogICAgU2hpZWxkLS0-PkFwcDogTm9uY2UKICAgIEFwcC0-PlBsYXRmb3JtOiBBdHRlc3QgYXBwIGFuZCBkZXZpY2UgYWdhaW5zdCBub25jZQogICAgUGxhdGZvcm0tLT4-QXBwOiBIYXJkd2FyZS1iYWNrZWQgc3RhdGVtZW50CiAgICBBcHAtPj5TaGllbGQ6IFN0YXRlbWVudCBhbmQgcnVudGltZSBzaWduYWxzCiAgICBTaGllbGQtLT4-QXBwOiBTaG9ydC1saXZlZCBFUzI1NiB0b2tlbiBhbmQgcGluIHNldAogICAgQXBwLT4-QVBJOiBSZXF1ZXN0IHdpdGggWC1DTjEtQXR0ZXN0CiAgICBBUEktPj5BUEk6IFZlcmlmeSBzaWduYXR1cmUsIGFwcCwgdmVyZGljdCwgcG9saWN5LCBhbmQgZXhwaXJ5CiAgICBBUEktLT4-QXBwOiBTZXJ2ZSBvciByZWplY3QgdGhlIG9wZXJhdGlvbg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ29kZW5hbWUgT25lIGFwcAogICAgcGFydGljaXBhbnQgU2hpZWxkIGFzIEF0dGVzdGF0aW9uIHNlcnZpY2UKICAgIHBhcnRpY2lwYW50IFBsYXRmb3JtIGFzIEFwcCBBdHRlc3Qgb3IgUGxheSBJbnRlZ3JpdHkKICAgIHBhcnRpY2lwYW50IEFQSSBhcyBZb3VyIGJhY2tlbmQKICAgIEFwcC0-PlNoaWVsZDogUmVxdWVzdCBhIG9uZS10aW1lIGNoYWxsZW5nZQogICAgU2hpZWxkLS0-PkFwcDogTm9uY2UKICAgIEFwcC0-PlBsYXRmb3JtOiBBdHRlc3QgYXBwIGFuZCBkZXZpY2UgYWdhaW5zdCBub25jZQogICAgUGxhdGZvcm0tLT4-QXBwOiBIYXJkd2FyZS1iYWNrZWQgc3RhdGVtZW50CiAgICBBcHAtPj5TaGllbGQ6IFN0YXRlbWVudCBhbmQgcnVudGltZSBzaWduYWxzCiAgICBTaGllbGQtLT4-QXBwOiBTaG9ydC1saXZlZCBFUzI1NiB0b2tlbiBhbmQgcGluIHNldAogICAgQXBwLT4-QVBJOiBSZXF1ZXN0IHdpdGggWC1DTjEtQXR0ZXN0CiAgICBBUEktPj5BUEk6IFZlcmlmeSBzaWduYXR1cmUsIGFwcCwgdmVyZGljdCwgcG9saWN5LCBhbmQgZXhwaXJ5CiAgICBBUEktLT4-QXBwOiBTZXJ2ZSBvciByZWplY3QgdGhlIG9wZXJhdGlvbg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1081" height="611"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The nonce prevents a captured platform statement from becoming a permanent replay credential. The token identifies the expected package and platform, carries the policy decision, and can bind to one request body. The service can also include signals for root, jailbreak, hooking frameworks, emulators, debuggers, repackaging, or untrusted accessibility services.&lt;/p&gt;

&lt;p&gt;Your backend remains the enforcement point. The client reports what it sees. The service evaluates the attestation and policy. Your API decides whether to move money, return personal data, request step-up authentication, or reject the call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The app-side setup is a host list
&lt;/h2&gt;

&lt;p&gt;Enable the injected engine in &lt;code&gt;codenameone_settings.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.shield.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then register the hosts that should receive a token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AppShield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShieldConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;// A request without a token must not leave the device.&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api.mybank.example"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HostPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ENFORCED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// Other subdomains get a token when one is available.&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"*.mybank.example"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HostPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROTECTED&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"https://api.mybank.example/transfer"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;NetworkManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;addToQueueAndWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When App Shield owns the application's network guard, &lt;code&gt;ConnectionRequest&lt;/code&gt;, &lt;code&gt;Rest&lt;/code&gt;, &lt;code&gt;RequestBuilder&lt;/code&gt;, and other code built on &lt;code&gt;NetworkManager&lt;/code&gt; pass through it automatically. App Shield attaches &lt;code&gt;X-CN1-Attest&lt;/code&gt; on the network thread and checks the certificate chain against the current SPKI pin set. An unregistered host is untouched.&lt;/p&gt;

&lt;p&gt;The two policies encode different outage choices:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;If no valid token is available&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HostPolicy.PROTECTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send the request without a token. The backend can degrade or reject it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HostPolicy.ENFORCED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail before the request leaves the device. Use this for the few endpoints where an unverified request is never acceptable.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  If your application already has a network guard
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NetworkManager&lt;/code&gt; accepts one &lt;code&gt;NetworkGuard&lt;/code&gt; and seals that slot after the first call to &lt;code&gt;setNetworkGuard()&lt;/code&gt;. In the usual setup, call &lt;code&gt;AppShield.init()&lt;/code&gt; at the top of your application's &lt;code&gt;init(Object)&lt;/code&gt; method, before any library or application code installs a guard.&lt;/p&gt;

&lt;p&gt;If another guard is installed first, App Shield cannot replace it. Initialization continues, but ordinary requests receive neither the attestation token nor App Shield's certificate-pin check. This includes hosts marked &lt;code&gt;ENFORCED&lt;/code&gt;: without the shield guard, nothing sees that policy before the request leaves the device.&lt;/p&gt;

&lt;p&gt;An application that needs its own guard must install one composite guard and forward every callback to App Shield's guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NetworkGuard&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AppShield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNetworkGuard&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;NetworkManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setNetworkGuard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NetworkGuard&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addRequestHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-My-Trace"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newTraceId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beforeRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isCertificateCheckRequired&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCertificateCheckRequired&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;checkCertificates&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SSLCertificate&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;certificates&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkCertificates&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;certificates&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;interestingResponseHeaders&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interestingResponseHeaders&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConnectionRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;responseCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;shield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;afterResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="nc"&gt;AppShield&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShieldConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;protect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api.mybank.example"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HostPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ENFORCED&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AppShield.getNetworkGuard()&lt;/code&gt; is safe to call before &lt;code&gt;init()&lt;/code&gt; because the guard reads the configuration when it handles a request. Forwarding only &lt;code&gt;beforeRequest()&lt;/code&gt; is not enough. The certificate callbacks enforce the pin set, while the response callbacks let App Shield discard a rejected token. If your guard also captures response headers, return the union of both guards' header names and preserve that order when passing App Shield its values.&lt;/p&gt;

&lt;p&gt;The simulator now has &lt;strong&gt;Simulate &amp;gt; App Shield&lt;/strong&gt; controls for rejected attestations, expired tokens, compromised-device signals, and forced certificate-pin mismatches. That makes the failure path testable without misconfiguring a live server.&lt;/p&gt;

&lt;p&gt;The public API lives in the open-source core. A build without the Enterprise engine degrades to a documented no-op, so shared code still compiles and runs. A cloud build that explicitly requests &lt;code&gt;shield.enabled=true&lt;/code&gt; without entitlement fails with an explanation instead of silently producing an unprotected binary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What App Shield does not protect
&lt;/h2&gt;

&lt;p&gt;App Shield does not make an application unhackable. It raises the cost of calling a protected backend from a modified app and gives the server a cryptographically verifiable input for its policy.&lt;/p&gt;

&lt;p&gt;A genuine attested device can still relay requests for an attacker. Short token lifetimes and payload binding reduce replay, but they do not replace backend authorization, rate limits, or checks on the business operation itself.&lt;/p&gt;

&lt;p&gt;It also cannot cover traffic it cannot see. &lt;code&gt;ConnectionRequest&lt;/code&gt;-based APIs get automatic tokens and pinning. A third-party native HTTP client needs a token attached manually. A &lt;code&gt;BrowserComponent&lt;/code&gt; can receive a token for its initial navigation, but requests made by the loaded page remain outside the framework. WebSocket handshake headers are not available through the platform socket on iOS or in the browser, and WebSocket certificate pinning is not exposed.&lt;/p&gt;

&lt;p&gt;Certificate pinning has its own operational risk. App Shield pins public keys rather than whole certificates, so a certificate renewal on the same key does not break the app. You should still roll out the server policy in monitor mode first, measure the &lt;code&gt;would_deny&lt;/code&gt; traffic, and only then reject requests.&lt;/p&gt;

&lt;p&gt;The complete wire format, failure statuses, transport boundaries, pin lifecycle, and backend examples are in the &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/App-Shield.asciidoc" rel="noopener noreferrer"&gt;App Shield developer guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenType fonts now work without renaming
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5508" rel="noopener noreferrer"&gt;PR #5508&lt;/a&gt; makes &lt;code&gt;.otf&lt;/code&gt; a supported font extension across iOS, tvOS, watchOS, Android, JavaSE, JavaScript, Windows, and Linux. This corrects an inconsistent path where some tools could parse an OpenType font but device packaging ignored its extension.&lt;/p&gt;

&lt;p&gt;You can now keep the original file in or below the directory containing your CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Brand Display"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("fonts/BrandDisplay.otf")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Brand Display"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSS compiler reads local fonts during the build. It now reports a missing file, an unreadable font, a missing PostScript name, a path outside the CSS directory, or two different files that would collide after packaging. Web font formats such as &lt;code&gt;.woff&lt;/code&gt; remain unsupported.&lt;/p&gt;

&lt;h2&gt;
  
  
  New builders for the JavaSE Windows target
&lt;/h2&gt;

&lt;p&gt;We are bringing down the old Windows desktop build machines and replacing them with newer servers. These machines build the older JavaSE-based Windows desktop target. They are separate from the new native Win32 target.&lt;/p&gt;

&lt;p&gt;The old builders crashed too often and held this target behind the rest of the toolchain. We expect the replacements to reduce those failures and finally make &lt;a href="https://www.codenameone.com/blog/official-experimental-java-17-support/" rel="noopener noreferrer"&gt;Java 17 projects&lt;/a&gt; available for this desktop build path. That work is still a server rollout, so treat JDK 17 as the intended result until we finish validation on real builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  New projects now resolve Codename One through R2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5524" rel="noopener noreferrer"&gt;Phase two of the Maven repository migration&lt;/a&gt; merged today. The application archetype, library archetype, and &lt;a href="https://start.codenameone.com" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt; now put &lt;code&gt;https://repo.codenameone.com/maven2&lt;/code&gt; in both repository lists.&lt;/p&gt;

&lt;p&gt;Maven keeps ordinary dependencies and build plugins in separate lists. Both blocks matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;codenameone&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://repo.codenameone.com/maven2&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;pluginRepositories&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pluginRepository&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;codenameone-plugins&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://repo.codenameone.com/maven2&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/pluginRepository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/pluginRepositories&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Newly generated builds now resolve Codename One releases through R2. Existing projects can add the same blocks before the planned August 28 cutover. We will stop publishing new versions to Maven Central on that date if the observation period remains clean. The archetype lookup itself still starts on Central; moving that lookup is phase three.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/maven-central-cloudflare-r2/" rel="noopener noreferrer"&gt;repository migration post&lt;/a&gt; explains the dates, artifact retention, signatures, and safeguards against partial releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  The push hostname switch happens Saturday
&lt;/h2&gt;

&lt;p&gt;On Saturday, August 8, we will redirect &lt;code&gt;push.codenameone.com&lt;/code&gt; to &lt;code&gt;cloud.codenameone.com&lt;/code&gt;. We announced the new service and compatibility endpoint in &lt;a href="https://www.codenameone.com/blog/push-v3-new-cloud/" rel="noopener noreferrer"&gt;the Push V3 release post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Existing push code should continue to work because the new service accepts the classic request format. Before the redirect, you can test the exact path by changing only the hostname on your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-https://push.codenameone.com/push/push
&lt;/span&gt;&lt;span class="gi"&gt;+https://cloud.codenameone.com/push/push
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send a real notification to every platform you support. Test a visible notification, a data payload, and a cold start. If the result differs from the old host after Saturday's switch, &lt;a href="https://github.com/codenameone/CodenameOne/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; or contact us through the website as soon as possible.&lt;/p&gt;

&lt;p&gt;App Shield is the larger direction behind this release: security controls should compose across the app, the build, and the server. OpenType support, new Windows builders, the R2 migration, and the push cutover are smaller changes, but they follow the same rule. The dependable path should be the normal path, and failures should surface where we can act on them.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Why Codename One Is Moving Beyond Maven Central</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:16:04 +0000</pubDate>
      <link>https://dev.to/codenameone/why-codename-one-is-moving-beyond-maven-central-35f2</link>
      <guid>https://dev.to/codenameone/why-codename-one-is-moving-beyond-maven-central-35f2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrf20ehw6mwh24xqkulw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrf20ehw6mwh24xqkulw.jpg" alt="Codename One Maven artifacts move through a staged Central and Cloudflare R2 migration" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codename One is starting a staged move from Maven Central to a repository we operate on Cloudflare R2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is not a story about Maven Central being bad. Sonatype runs expensive public infrastructure and has every right to define usage limits or sell a commercial service. Our release shape is simply a bad fit for those limits, and passing that infrastructure bill to Codename One users would make less sense than serving the same signed Maven layout ourselves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5497" rel="noopener noreferrer"&gt;PR #5497&lt;/a&gt; implements phase one. It reduces what each release publishes and adds dual publication to R2. Maven Central remains authoritative during this phase.&lt;/p&gt;

&lt;p&gt;We first talked about the repository move in &lt;a href="https://www.codenameone.com/blog/push-v3-new-cloud/" rel="noopener noreferrer"&gt;Friday's release post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our dashboard is not a near miss
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9gm4mat8rr0kjcu2t8pa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9gm4mat8rr0kjcu2t8pa.png" alt="Maven Central publishing usage shows Codename One far beyond the soft guidelines" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current Central Publishing usage dashboard reports:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Codename One&lt;/th&gt;
&lt;th&gt;Dashboard guideline&lt;/th&gt;
&lt;th&gt;Reported use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;2.12 GB&lt;/td&gt;
&lt;td&gt;80 MB&lt;/td&gt;
&lt;td&gt;2,652%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files&lt;/td&gt;
&lt;td&gt;19,962&lt;/td&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;1,996%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Releases&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;386%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That screenshot also explains Sonatype's position. It says the guidelines are soft, describes adjustments for qualifying open-source projects, and offers a commercial Publisher Pro route. We are an unusually heavy publisher, not an innocent bystander being charged for one small JAR.&lt;/p&gt;

&lt;p&gt;The question is who should carry the cost. In &lt;a href="https://debugagent.com/open-source-bait-and-switch" rel="noopener noreferrer"&gt;Open Source Bait and Switch&lt;/a&gt;, I argued that monetization pressure often lands on small open-source vendors while the largest companies capture much of the value. Maven hosting is a different product and Sonatype is not changing our license. The same asymmetry is still relevant: charging a small framework company does not necessarily collect from the enterprise organizations receiving the largest downstream benefit.&lt;/p&gt;

&lt;p&gt;We can provide package hosting free to Codename One developers on infrastructure that matches our release process. That is the more sustainable answer for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  The migration has three visible dates
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;User impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;July 31, 2026&lt;/td&gt;
&lt;td&gt;Reduce release payload and start dual publishing&lt;/td&gt;
&lt;td&gt;None. Central remains authoritative.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 7, 2026&lt;/td&gt;
&lt;td&gt;Generated projects and Initializr add the Codename One repository&lt;/td&gt;
&lt;td&gt;New projects receive the repository automatically. Existing projects can add it manually.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;August 28, 2026&lt;/td&gt;
&lt;td&gt;Stop publishing new Codename One versions to Central, if the dual-publish period is clean&lt;/td&gt;
&lt;td&gt;Existing projects need the Codename One repository to discover future versions.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dates are more useful than “next week” in a migration document. If validation changes the schedule, we will update this post before changing the source of new releases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJQaGFzZSAxPGJyLz5TaHJpbmsgYW5kIGR1YWwgcHVibGlzaCJdIC0tPiBCWyJQaGFzZSAyPGJyLz5HZW5lcmF0ZWQgUE9NcyB1c2UgUjIiXQogICAgQiAtLT4gQ1siVGhyZWUtd2VlayBvYnNlcnZhdGlvbiB3aW5kb3ciXQogICAgQyAtLT4gRFsiTmV3IHJlbGVhc2VzIG9uIFIyIl0KICAgIEEgLS0-IEVbIk1hdmVuIENlbnRyYWwgcmVtYWlucyBhdXRob3JpdGF0aXZlIl0KICAgIEIgLS0-IEUKICAgIEUgLS0-IEZbIkV4aXN0aW5nIENlbnRyYWwgdmVyc2lvbnMgcmVtYWluIGF2YWlsYWJsZSJd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJQaGFzZSAxPGJyLz5TaHJpbmsgYW5kIGR1YWwgcHVibGlzaCJdIC0tPiBCWyJQaGFzZSAyPGJyLz5HZW5lcmF0ZWQgUE9NcyB1c2UgUjIiXQogICAgQiAtLT4gQ1siVGhyZWUtd2VlayBvYnNlcnZhdGlvbiB3aW5kb3ciXQogICAgQyAtLT4gRFsiTmV3IHJlbGVhc2VzIG9uIFIyIl0KICAgIEEgLS0-IEVbIk1hdmVuIENlbnRyYWwgcmVtYWlucyBhdXRob3JpdGF0aXZlIl0KICAgIEIgLS0-IEUKICAgIEUgLS0-IEZbIkV4aXN0aW5nIENlbnRyYWwgdmVyc2lvbnMgcmVtYWluIGF2YWlsYWJsZSJd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1152" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The POM change
&lt;/h2&gt;

&lt;p&gt;Existing projects do not need to change during phase one. To prepare a project for new releases after the cutover, add both a repository and a plugin repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;codenameone&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://repo.codenameone.com/maven2&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;pluginRepositories&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pluginRepository&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;codenameone-plugins&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://repo.codenameone.com/maven2&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/pluginRepository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/pluginRepositories&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin repository is not redundant. Maven resolves build plugins and ordinary dependencies through separate repository lists. A project with only &lt;code&gt;&amp;lt;repositories&amp;gt;&lt;/code&gt; can download a runtime artifact and still fail to discover a future &lt;code&gt;codenameone-maven-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next week's Initializr and archetype change will generate this configuration. Existing projects can add it at any time; the repository uses standard Maven layout and does not change dependency coordinates.&lt;/p&gt;

&lt;h2&gt;
  
  
  We were publishing the same bytes repeatedly
&lt;/h2&gt;

&lt;p&gt;The real Codename One 7.0.258 release published 44 artifacts and 127 JARs totaling 229.5 MB. The core JAR itself is only 4.49 MB. Most of the weight came from shaded fat JARs that republished the same third-party libraries each week.&lt;/p&gt;

&lt;p&gt;Phase one reduces the measured release payload to 76.9 MB, a 66 percent reduction:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Per release&lt;/th&gt;
&lt;th&gt;Versions in 8 GB&lt;/th&gt;
&lt;th&gt;Approximate history at 65 versions per year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before&lt;/td&gt;
&lt;td&gt;229.5 MB&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After&lt;/td&gt;
&lt;td&gt;76.9 MB&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;1.6 years&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The changes are deliberately targeted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Five unused &lt;code&gt;jar-with-dependencies&lt;/code&gt; attachments stop being published. Builder bundles still contain what they need.&lt;/li&gt;
&lt;li&gt;Stable &lt;code&gt;designer&lt;/code&gt;, &lt;code&gt;javase-svg&lt;/code&gt;, and &lt;code&gt;sqlite-jdbc&lt;/code&gt; artifacts are frozen at pinned versions instead of being copied into every release.&lt;/li&gt;
&lt;li&gt;The headless CSS command moves out of the 43.5 MB Resource Editor fat JAR.&lt;/li&gt;
&lt;li&gt;A new &lt;code&gt;css-cli&lt;/code&gt; module carries that command in a 28 KB JAR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CSS compiler itself had already been separated. Its command-line driver had not. Moving that driver avoids launching the complete Swing editor dependency graph for a headless build.&lt;/p&gt;

&lt;p&gt;This optimization would not get us under the dashboard guidelines. It is still worth doing. It reduces upload time, metadata churn, retention pressure, and the number of bytes a failed release has to retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  R2 is static hosting, which is what Maven needs
&lt;/h2&gt;

&lt;p&gt;A release repository does not need a database-driven artifact server in the request path. Maven needs immutable files in a known directory layout, metadata, checksums, and signatures.&lt;/p&gt;

&lt;p&gt;The release pipeline uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;maven2/com/codenameone/&amp;lt;artifactId&amp;gt;/&amp;lt;version&amp;gt;/...
maven2/com/codenameone/&amp;lt;artifactId&amp;gt;/maven-metadata.xml
maven2/com/codenameone/maven-metadata.xml
maven2/archetype-catalog.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The R2 publication reuses the tree already staged for Central. It includes MD5, SHA-1, SHA-256, SHA-512, and &lt;code&gt;.asc&lt;/code&gt; signatures. There is no second Maven build that could produce different bytes.&lt;/p&gt;

&lt;p&gt;Cloudflare's edge cache and object storage should reduce download latency and remove Central throttling from our release path. We also expect releases and CI to become faster and more stable because publication no longer depends on a second service accepting our volume at that moment. Those are expectations, not measurements yet. We will compare publish time, artifact resolution time, cache behavior, and failure rate during dual publication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static hosting still needs release semantics
&lt;/h2&gt;

&lt;p&gt;Object storage will happily accept a partial or overwritten release unless the publishing scripts stop it. The phase-one tooling adds those rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Released bytes are immutable. An upload fails if the same path already contains different content.&lt;/li&gt;
&lt;li&gt;A release is invisible until every core and editor artifact is present.&lt;/li&gt;
&lt;li&gt;A per-release completion marker is written only after all uploads succeed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maven-metadata.xml&lt;/code&gt; is rebuilt from the bucket listing, never from the one-version staging tree.&lt;/li&gt;
&lt;li&gt;Metadata advertises only versions with a completion marker.&lt;/li&gt;
&lt;li&gt;Upload uses a copy operation that cannot delete older versions.&lt;/li&gt;
&lt;li&gt;Frozen dependencies are seeded from Central and verified against their checksums.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBCdWlsZCBhcyBSZWxlYXNlIGJ1aWxkCiAgICBwYXJ0aWNpcGFudCBTdGFnZSBhcyBDZW50cmFsIHN0YWdpbmcgdHJlZQogICAgcGFydGljaXBhbnQgUjIgYXMgUjIgYnVja2V0CiAgICBwYXJ0aWNpcGFudCBNZXRhIGFzIE1ldGFkYXRhIGdlbmVyYXRvcgogICAgQnVpbGQtPj5TdGFnZTogUHJvZHVjZSBzaWduZWQgYXJ0aWZhY3RzIGFuZCBjaGVja3N1bXMKICAgIFN0YWdlLT4-UjI6IENvcHkgd2l0aG91dCBkZWxldGUKICAgIFIyLT4-UjI6IFJlamVjdCBkaWZmZXJlbnQgYnl0ZXMgYXQgYW4gZXhpc3RpbmcgcGF0aAogICAgQnVpbGQtPj5SMjogTWFyayBjb21wbGV0ZSBhZnRlciBldmVyeSBjb21wb25lbnQgc3VjY2VlZHMKICAgIE1ldGEtPj5SMjogTGlzdCBjb21wbGV0ZSB2ZXJzaW9ucwogICAgTWV0YS0-PlIyOiBXcml0ZSBtZXRhZGF0YSBsYXN0CiAgICBOb3RlIG92ZXIgUjI6IFBhcnRpYWwgcmVsZWFzZXMgcmVtYWluIHVuZGlzY292ZXJhYmxl%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBCdWlsZCBhcyBSZWxlYXNlIGJ1aWxkCiAgICBwYXJ0aWNpcGFudCBTdGFnZSBhcyBDZW50cmFsIHN0YWdpbmcgdHJlZQogICAgcGFydGljaXBhbnQgUjIgYXMgUjIgYnVja2V0CiAgICBwYXJ0aWNpcGFudCBNZXRhIGFzIE1ldGFkYXRhIGdlbmVyYXRvcgogICAgQnVpbGQtPj5TdGFnZTogUHJvZHVjZSBzaWduZWQgYXJ0aWZhY3RzIGFuZCBjaGVja3N1bXMKICAgIFN0YWdlLT4-UjI6IENvcHkgd2l0aG91dCBkZWxldGUKICAgIFIyLT4-UjI6IFJlamVjdCBkaWZmZXJlbnQgYnl0ZXMgYXQgYW4gZXhpc3RpbmcgcGF0aAogICAgQnVpbGQtPj5SMjogTWFyayBjb21wbGV0ZSBhZnRlciBldmVyeSBjb21wb25lbnQgc3VjY2VlZHMKICAgIE1ldGEtPj5SMjogTGlzdCBjb21wbGV0ZSB2ZXJzaW9ucwogICAgTWV0YS0-PlIyOiBXcml0ZSBtZXRhZGF0YSBsYXN0CiAgICBOb3RlIG92ZXIgUjI6IFBhcnRpYWwgcmVsZWFzZXMgcmVtYWluIHVuZGlzY292ZXJhYmxl%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="990" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare can cache a 404 just before a new artifact appears. The runbook therefore requires a cache rule that does not store 4xx and 5xx responses. Release polling also uses a cache-busting query so a stale negative cannot turn a successful upload into a false release failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retention: the guarantee and the likely window
&lt;/h2&gt;

&lt;p&gt;We will guarantee at least six months of historical Codename One versions in the new repository. The optimized payload currently fits roughly 106 releases in the planned capacity, which is about 1.6 years at the recent pace.&lt;/p&gt;

&lt;p&gt;The guarantee is shorter than the estimate because release contents and frequency can change. We would rather guarantee a window we can defend and retain more in practice than publish a long promise that quietly fails.&lt;/p&gt;

&lt;p&gt;Freezing stable packages also means “history” is no longer one full copy of every byte per weekly tag. A future version can depend on a pinned stable artifact while the frequently changing core keeps its own version cadence.&lt;/p&gt;

&lt;p&gt;Versions already on Maven Central are not deleted by this move. The retention policy applies to the repository we operate and to future availability, not to removing the public history that Central already stores.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can still go wrong
&lt;/h2&gt;

&lt;p&gt;The red-team list is part of the design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A partial upload is advertised.&lt;/strong&gt; Completion markers and metadata-last publication prevent it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A retry changes released bytes.&lt;/strong&gt; The overwrite guard fails the release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A stale cached 404 hides a new file.&lt;/strong&gt; The cache rule and cache-busted poll address it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A project adds only the dependency repository.&lt;/strong&gt; Generated POMs include &lt;code&gt;&amp;lt;pluginRepositories&amp;gt;&lt;/code&gt; too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A frozen artifact remains available only on Central.&lt;/strong&gt; The seed step copies and verifies every pinned dependency before the final cutover.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R2 is slower in a real region.&lt;/strong&gt; The observation window measures this before Central publication stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three release tags arrive while one is running.&lt;/strong&gt; GitHub Actions can replace a pending member of a concurrency group. The current runbook requires pushing release tags one at a time until dispatch is made durable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last item is not polished away. The repository removes one availability dependency, but it does not turn GitHub Actions into a release queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this should be mostly seamless
&lt;/h2&gt;

&lt;p&gt;Coordinates, version numbers, signatures, checksums, and normal Maven resolution remain unchanged. New projects receive the repository automatically next week. Existing projects need one POM block before they request a post-cutover version.&lt;/p&gt;

&lt;p&gt;That is the whole user-facing migration. The work underneath it is larger because package hosting is part of the product supply chain. If we do this well, developers will notice faster and more reliable resolution, not a new thing they have to learn.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>A Polyline Is Not a Route</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 10 Aug 2026 14:43:58 +0000</pubDate>
      <link>https://dev.to/codenameone/a-polyline-is-not-a-route-5fek</link>
      <guid>https://dev.to/codenameone/a-polyline-is-not-a-route-5fek</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6h8mdnsm93d07gxbwxna.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6h8mdnsm93d07gxbwxna.jpg" alt="A road-following route replaces a straight line between two map pins" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A polyline can draw the points you give it. It cannot discover the road between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5480" rel="noopener noreferrer"&gt;PR #5480&lt;/a&gt; adds &lt;code&gt;com.codename1.maps.routing&lt;/code&gt;, a portable route model and service layer for road geometry, distance, duration, waypoints, alternatives, legs, steps, and encoded polylines.&lt;/p&gt;

&lt;p&gt;We talked about routing in &lt;a href="https://www.codenameone.com/blog/push-v3-new-cloud/" rel="noopener noreferrer"&gt;Friday's release post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-line route
&lt;/h2&gt;

&lt;p&gt;For the common case, give &lt;code&gt;Routing&lt;/code&gt; a map and two coordinates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;MapView&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MapView&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showRoute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LatLng&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;38.8977&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;77.0365&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LatLng&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;38.8894&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;77.0352&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call returns immediately. The routing service finds the best route, the API adds its polyline, and the map frames the result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJPcmlnaW4gYW5kIGRlc3RpbmF0aW9uIl0gLS0-IEJbIlJvdXRlUmVxdWVzdCJdCiAgICBCIC0tPiBDWyJSb3V0ZVNlcnZpY2UiXQogICAgQyAtLT4gRFsiUm9hZCBuZXR3b3JrIGNhbGN1bGF0aW9uIl0KICAgIEQgLS0-IEVbIlJvdXRlLCBsZWdzLCBzdGVwcywgZGlzdGFuY2UsIGR1cmF0aW9uIl0KICAgIEUgLS0-IEZbIlBvbHlsaW5lIG9uIE1hcFN1cmZhY2UiXQogICAgRSAtLT4gR1siQXBwbGljYXRpb24gVUkiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJPcmlnaW4gYW5kIGRlc3RpbmF0aW9uIl0gLS0-IEJbIlJvdXRlUmVxdWVzdCJdCiAgICBCIC0tPiBDWyJSb3V0ZVNlcnZpY2UiXQogICAgQyAtLT4gRFsiUm9hZCBuZXR3b3JrIGNhbGN1bGF0aW9uIl0KICAgIEQgLS0-IEVbIlJvdXRlLCBsZWdzLCBzdGVwcywgZGlzdGFuY2UsIGR1cmF0aW9uIl0KICAgIEUgLS0-IEZbIlBvbHlsaW5lIG9uIE1hcFN1cmZhY2UiXQogICAgRSAtLT4gR1siQXBwbGljYXRpb24gVUkiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1528" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;showRoute(...)&lt;/code&gt; is intentionally small. It is useful when the line itself is the result. An application that needs error UI, custom styling, ETA, or alternatives should use the callback route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Own the result when it matters
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;RouteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RouteRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTravelMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TravelMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DRIVING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWaypoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coffeeStop&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAlternatives&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSteps&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findRoute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RouteCallback&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;routesFound&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPolyline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toPolyline&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStrokeColor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff5722&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStrokeWidth&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fitBounds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBounds&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;distanceLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;round&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDistanceMeters&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" km"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;etaLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;round&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDurationSeconds&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" min"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;routeFailed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ToastBar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showErrorMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback is invoked exactly once and always later on the Codename One EDT, even if a custom service responds synchronously, responds from a worker thread, responds twice, or throws after responding. The facade contains those service errors so application timing does not depend on the provider implementation.&lt;/p&gt;

&lt;p&gt;The model exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One or more route alternatives&lt;/li&gt;
&lt;li&gt;Total distance and duration&lt;/li&gt;
&lt;li&gt;Geographic bounds&lt;/li&gt;
&lt;li&gt;Route geometry&lt;/li&gt;
&lt;li&gt;Legs between waypoints&lt;/li&gt;
&lt;li&gt;Step instructions and maneuver locations&lt;/li&gt;
&lt;li&gt;Provider metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;PolylineCodec&lt;/code&gt; supports precision 5 and precision 6 encoded geometry. That lets a service retain the compact wire format and decode it only when a &lt;code&gt;Polyline&lt;/code&gt; is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  OSRM makes the first run easy
&lt;/h2&gt;

&lt;p&gt;The default &lt;code&gt;RouteService&lt;/code&gt; is &lt;code&gt;OsrmRouteService&lt;/code&gt;. It needs no API key, so the two-line example can work without provider signup.&lt;/p&gt;

&lt;p&gt;That default points to the public OSRM demonstration server. The server has no production SLA and asks clients to keep usage light. It can reject large or abusive workloads, and its public profile is configured for cars.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DRIVING&lt;/code&gt; is appropriate for a quick test.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WALKING&lt;/code&gt; or &lt;code&gt;CYCLING&lt;/code&gt; against the default demo can still return car routing.&lt;/li&gt;
&lt;li&gt;A shipping application should use a provider and capacity it controls.&lt;/li&gt;
&lt;li&gt;Offline routing requires another implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Travel mode is a request, not a guarantee. A &lt;code&gt;RouteService&lt;/code&gt; reports what it supports, and an application should not label a result “walking” when the active backend only has a car graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Point OSRM at infrastructure you control
&lt;/h2&gt;

&lt;p&gt;OSRM is open source and can be self-hosted. If your server exposes an OSRM-compatible route endpoint, install it as the application service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OsrmRouteService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"https://routing.example.com"&lt;/span&gt;
&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the exact constructor and endpoint configuration supported by your selected release. The architectural point is that application code still consumes &lt;code&gt;Route&lt;/code&gt;, not provider JSON.&lt;/p&gt;

&lt;p&gt;For a different provider, implement &lt;code&gt;RouteService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompanyRouteService&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RouteService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"company-routing"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isAvailable&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;credentialsAreReady&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;findRoutes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RouteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RouteCallback&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Translate the portable request, call the provider,&lt;/span&gt;
        &lt;span class="c1"&gt;// then return portable Route objects through the callback.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Routing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CompanyRouteService&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That seam supports commercial providers, a company gateway, an offline engine, or a route service with domain constraints such as truck height and hazardous materials.&lt;/p&gt;

&lt;h2&gt;
  
  
  The maps API and routing API stay separate
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;MapSurface&lt;/code&gt; displays geometry. &lt;code&gt;RouteService&lt;/code&gt; discovers geometry. Keeping them separate avoids tying a route provider to one renderer.&lt;/p&gt;

&lt;p&gt;The same route can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drawn on a native or vector map&lt;/li&gt;
&lt;li&gt;Styled according to traffic or accessibility&lt;/li&gt;
&lt;li&gt;Summarized as distance and ETA&lt;/li&gt;
&lt;li&gt;Saved as an encoded polyline&lt;/li&gt;
&lt;li&gt;Compared with alternatives&lt;/li&gt;
&lt;li&gt;Sent to another screen without retaining the map component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API does not yet claim turn-by-turn navigation, rerouting, traffic prediction, offline map packages, or voice guidance. Those are stateful products with location updates and provider-specific rules. This release provides the route result that those systems need, without naming a static polyline “navigation.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Production checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose a routing service with terms, capacity, data coverage, and travel profiles that fit the application.&lt;/li&gt;
&lt;li&gt;Report route failures to the user. The no-callback &lt;code&gt;showRoute(...)&lt;/code&gt; convenience method cannot do that.&lt;/li&gt;
&lt;li&gt;Verify units before displaying them. The model uses meters and seconds.&lt;/li&gt;
&lt;li&gt;Test dateline crossing, unreachable points, ferries, tolls, and waypoint order for the markets you serve.&lt;/li&gt;
&lt;li&gt;Keep provider keys out of client code when the provider expects a server-side secret.&lt;/li&gt;
&lt;li&gt;Attribute map and routing data according to each provider's license.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A route is an answer from a changing road graph, not a decorative line. The new API finally represents it that way.&lt;/p&gt;

&lt;p&gt;Next in the series: &lt;a href="https://www.codenameone.com/blog/maven-central-cloudflare-r2/" rel="noopener noreferrer"&gt;why Codename One is starting a staged move beyond Maven Central&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
