From 91eac790343f0a64b85075f07a7a344b8920613b Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 16:33:32 +0300 Subject: [PATCH 1/6] Fix Android SpotBugs calendar warning --- .../src/com/codename1/impl/android/AndroidCalendarSource.java | 3 +-- maven/android/pom.xml | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java b/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java index 7d79b8b160b..f0b32d477b6 100644 --- a/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java +++ b/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java @@ -119,9 +119,8 @@ private CalendarPage queryEventsBlocking(CalendarQuery query) List args = new ArrayList(); boolean expandInstances = query != null && (query.getStartTime() != null || query.getEndTime() != null); - String calendarColumn = expandInstances ? Instances.CALENDAR_ID : Events.CALENDAR_ID; if (query != null && query.getCalendarId() != null) { - where.append(" AND ").append(calendarColumn).append("=?"); + where.append(" AND ").append(Events.CALENDAR_ID).append("=?"); args.add(query.getCalendarId()); } where.append(" AND ").append(Events.DELETED).append("=0"); diff --git a/maven/android/pom.xml b/maven/android/pom.xml index 2afe8497b10..a3db907d8cc 100644 --- a/maven/android/pom.xml +++ b/maven/android/pom.xml @@ -43,7 +43,7 @@ Max Low - false + true true ${project.build.directory} ${project.basedir}/../../Ports/Android/spotbugs-exclude.xml @@ -54,6 +54,7 @@ verify spotbugs + check From 9104a0459874d5dab9dab5c07c4601d76793eddb Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 16:47:28 +0300 Subject: [PATCH 2/6] Default Android builds to API 36 --- docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc | 4 ++-- .../java/com/codename1/builders/AndroidGradleBuilder.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index 4aaa6196968..eb5575e08db 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -786,8 +786,8 @@ This is great as it allows apps to be installed with a single click and no permi ===== Enabling permissions -// vale-skip: write-good.TooWordy — 'minimum of API 33' is the precise term for the SDK floor. -Codename One's Gradle 8 based Android builder detects the highest Android SDK you've installed and uses that value (with a minimum of API 33) for both the compile and target SDK versions, so the modern runtime permission flow is enabled by default. If you override the target version through the `android.targetSDKVersion` build hint the builder will honour it, but lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. +// vale-skip: write-good.TooWordy — 'minimum of API 36' is the precise term for the SDK floor. +Codename One's Gradle 8 based Android builder detects the highest Android SDK you've installed and uses that value (with a minimum of API 36) for both the compile and target SDK versions, so the modern runtime permission flow is enabled by default. If you override the target version through the `android.targetSDKVersion` build hint the builder will honour it, but lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. ===== Permission prompts diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 5a198a861a8..196738e0511 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -762,7 +762,7 @@ public boolean build(File sourceZip, final BuildRequest request) throws BuildExc maxBuildToolsVersionInt = Math.max(33, maxBuildToolsVersionInt); maxBuildToolsVersion = "" + maxBuildToolsVersionInt; - maxPlatformVersionInt = Math.max(33, maxPlatformVersionInt); + maxPlatformVersionInt = Math.max(36, maxPlatformVersionInt); maxPlatformVersion = "" + maxPlatformVersionInt; } From 33810272982e131bd1c23f1f7bb486062ae3384a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:13:11 +0300 Subject: [PATCH 3/6] Address Android build review feedback --- .../impl/android/AndroidCalendarSource.java | 4 +- .../Advanced-Topics-Under-The-Hood.asciidoc | 2 +- maven/android/pom.xml | 1 - .../builders/AndroidGradleBuilder.java | 14 +++++- .../AndroidGradleBuilderSdkVersionTest.java | 48 +++++++++++++++++++ 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java diff --git a/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java b/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java index f0b32d477b6..55cd880452d 100644 --- a/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java +++ b/Ports/Android/src/com/codename1/impl/android/AndroidCalendarSource.java @@ -54,6 +54,8 @@ /** Android Calendar Provider integration. */ final class AndroidCalendarSource extends LocalCalendarSource { + // Events and Instances use the same Calendar Provider selection column. + private static final String CALENDAR_ID_SELECTION_COLUMN = Events.CALENDAR_ID; private static final String[] EVENT_COLUMNS = {Events._ID, Events.CALENDAR_ID, Events.TITLE, Events.DESCRIPTION, Events.EVENT_LOCATION, Events.DTSTART, Events.DTEND, Events.DURATION, Events.EVENT_TIMEZONE, Events.ALL_DAY, Events.RRULE, Events.STATUS, Events.AVAILABILITY, @@ -120,7 +122,7 @@ private CalendarPage queryEventsBlocking(CalendarQuery query) boolean expandInstances = query != null && (query.getStartTime() != null || query.getEndTime() != null); if (query != null && query.getCalendarId() != null) { - where.append(" AND ").append(Events.CALENDAR_ID).append("=?"); + where.append(" AND ").append(CALENDAR_ID_SELECTION_COLUMN).append("=?"); args.add(query.getCalendarId()); } where.append(" AND ").append(Events.DELETED).append("=0"); diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index eb5575e08db..2d3c8caf5e4 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -803,7 +803,7 @@ If you explicitly lower the target SDK (for example: `android.targetSDKVersion=2 .Install UI when using the old permissions system image::img/marshmallow-permissions-level21.png[Install UI when using the old permissions system,scaledwidth=20%] -When you keep the default target (API 33+) the installer defers to the runtime permission flow and the installation UI looks like this instead: +When you keep the default target (API 36+) the installer defers to the runtime permission flow and the installation UI looks like this instead: .Install UI when using the new permissions system image::img/marshmallow-permissions-level23.png[Install UI when using the new permissions system,scaledwidth=20%] diff --git a/maven/android/pom.xml b/maven/android/pom.xml index a3db907d8cc..7cc093111e0 100644 --- a/maven/android/pom.xml +++ b/maven/android/pom.xml @@ -53,7 +53,6 @@ spotbugs verify - spotbugs check diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 196738e0511..6c560dc2369 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -759,6 +759,8 @@ public boolean build(File sourceZip, final BuildRequest request) throws BuildExc } if (useGradle8) { + // Build Tools and platform SDK versions are independent; compileSdk is + // clamped to targetSdk when the Gradle project is generated below. maxBuildToolsVersionInt = Math.max(33, maxBuildToolsVersionInt); maxBuildToolsVersion = "" + maxBuildToolsVersionInt; @@ -4995,6 +4997,7 @@ public void usesClassMethod(String cls, String method) { compileSdkVersion = "36"; supportLibVersion = "28"; } + compileSdkVersion = ensureCompileSdkAtLeastTarget(compileSdkVersion, targetNumber); jcenter = " google()\n" + " jcenter()\n" + @@ -6200,7 +6203,16 @@ private void initPlayServiceVersions(BuildRequest request) { } } - private Integer parseSdkInt(String value) { + static String ensureCompileSdkAtLeastTarget(String compileSdkVersion, String targetSdkVersion) { + Integer compileSdkInt = parseSdkInt(compileSdkVersion); + Integer targetSdkInt = parseSdkInt(targetSdkVersion); + if (targetSdkInt != null && (compileSdkInt == null || targetSdkInt > compileSdkInt)) { + return String.valueOf(targetSdkInt); + } + return compileSdkVersion; + } + + private static Integer parseSdkInt(String value) { if (value == null) { return null; } diff --git a/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java b/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java new file mode 100644 index 00000000000..f6e6229734c --- /dev/null +++ b/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codename1.builders; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AndroidGradleBuilderSdkVersionTest { + + @Test + void raisesCompileSdkToTargetSdk() { + assertEquals("36", + AndroidGradleBuilder.ensureCompileSdkAtLeastTarget("33", "36")); + } + + @Test + void doesNotLowerCompileSdk() { + assertEquals("36", + AndroidGradleBuilder.ensureCompileSdkAtLeastTarget("36", "35")); + } + + @Test + void handlesLegacyCompileSdkNotation() { + assertEquals("36", + AndroidGradleBuilder.ensureCompileSdkAtLeastTarget("'android-21'", "36")); + } +} From 13c3ec53b36e3241823875c00561469773027674 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:18:26 +0300 Subject: [PATCH 4/6] Fix API 36 emulator disk exhaustion --- .github/workflows/car-android.yml | 6 ++++++ .github/workflows/scripts-android.yml | 1 - .../developer-guide/Advanced-Topics-Under-The-Hood.asciidoc | 2 +- .../java/com/codename1/builders/AndroidGradleBuilder.java | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/car-android.yml b/.github/workflows/car-android.yml index 4db26465fa2..3cbf7b9c431 100644 --- a/.github/workflows/car-android.yml +++ b/.github/workflows/car-android.yml @@ -42,6 +42,12 @@ jobs: - uses: actions/checkout@v6 - name: Set TMPDIR run: echo "TMPDIR=${{ runner.temp }}" >> $GITHUB_ENV + - name: Free Disk Space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android/sdk/ndk + sudo rm -rf /opt/ghc + sudo rm -rf /usr/share/swift - name: Cache codenameone-tools uses: actions/cache@v5 with: diff --git a/.github/workflows/scripts-android.yml b/.github/workflows/scripts-android.yml index e6274fd53e3..9f51c1b3d38 100644 --- a/.github/workflows/scripts-android.yml +++ b/.github/workflows/scripts-android.yml @@ -121,7 +121,6 @@ jobs: - name: Set TMPDIR run: echo "TMPDIR=${{ runner.temp }}" >> $GITHUB_ENV - name: Free Disk Space - if: matrix.id != 'default' run: | sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/local/lib/android/sdk/ndk diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index 2d3c8caf5e4..b86e714b5d7 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -787,7 +787,7 @@ This is great as it allows apps to be installed with a single click and no permi ===== Enabling permissions // vale-skip: write-good.TooWordy — 'minimum of API 36' is the precise term for the SDK floor. -Codename One's Gradle 8 based Android builder detects the highest Android SDK you've installed and uses that value (with a minimum of API 36) for both the compile and target SDK versions, so the modern runtime permission flow is enabled by default. If you override the target version through the `android.targetSDKVersion` build hint the builder will honour it, but lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. +By default, Codename One's Gradle 8 based Android builder uses the highest Android SDK you've installed, with a minimum of API 36, for both the compile and target SDK versions. If you override the target through the `android.targetSDKVersion` build hint, the builder honours it while keeping the compile SDK at least as high as the target SDK. Lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. ===== Permission prompts diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 6c560dc2369..71dd604edcd 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -760,7 +760,7 @@ public boolean build(File sourceZip, final BuildRequest request) throws BuildExc if (useGradle8) { // Build Tools and platform SDK versions are independent; compileSdk is - // clamped to targetSdk when the Gradle project is generated below. + // raised to at least targetSdk when the Gradle project is generated below. maxBuildToolsVersionInt = Math.max(33, maxBuildToolsVersionInt); maxBuildToolsVersion = "" + maxBuildToolsVersionInt; From 611bbfec26161ecb81f02c8e7539948aa17c4e39 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:44:36 +0300 Subject: [PATCH 5/6] Preserve preview Android compile SDK values --- .../com/codename1/builders/AndroidGradleBuilder.java | 9 ++++++++- .../builders/AndroidGradleBuilderSdkVersionTest.java | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index 71dd604edcd..93565eb7322 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -6203,10 +6203,17 @@ private void initPlayServiceVersions(BuildRequest request) { } } + // Package-private for direct unit testing; this is not part of the builder API. static String ensureCompileSdkAtLeastTarget(String compileSdkVersion, String targetSdkVersion) { Integer compileSdkInt = parseSdkInt(compileSdkVersion); Integer targetSdkInt = parseSdkInt(targetSdkVersion); - if (targetSdkInt != null && (compileSdkInt == null || targetSdkInt > compileSdkInt)) { + if (targetSdkInt == null) { + return compileSdkVersion; + } + if (compileSdkVersion == null || compileSdkVersion.trim().isEmpty()) { + return String.valueOf(targetSdkInt); + } + if (compileSdkInt != null && targetSdkInt > compileSdkInt) { return String.valueOf(targetSdkInt); } return compileSdkVersion; diff --git a/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java b/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java index f6e6229734c..0fe66904647 100644 --- a/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java +++ b/maven/codenameone-maven-plugin/src/test/java/com/codename1/builders/AndroidGradleBuilderSdkVersionTest.java @@ -45,4 +45,16 @@ void handlesLegacyCompileSdkNotation() { assertEquals("36", AndroidGradleBuilder.ensureCompileSdkAtLeastTarget("'android-21'", "36")); } + + @Test + void preservesNonNumericCompileSdk() { + assertEquals("'android-Baklava'", + AndroidGradleBuilder.ensureCompileSdkAtLeastTarget("'android-Baklava'", "36")); + } + + @Test + void suppliesMissingCompileSdk() { + assertEquals("36", + AndroidGradleBuilder.ensureCompileSdkAtLeastTarget(null, "36")); + } } From f1df9243117781bf2ebcf07acbaddef4bd529d31 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:37:30 +0300 Subject: [PATCH 6/6] Use US spelling in Android SDK guide --- docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc index b86e714b5d7..187c7bb2fc0 100644 --- a/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc +++ b/docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc @@ -787,7 +787,7 @@ This is great as it allows apps to be installed with a single click and no permi ===== Enabling permissions // vale-skip: write-good.TooWordy — 'minimum of API 36' is the precise term for the SDK floor. -By default, Codename One's Gradle 8 based Android builder uses the highest Android SDK you've installed, with a minimum of API 36, for both the compile and target SDK versions. If you override the target through the `android.targetSDKVersion` build hint, the builder honours it while keeping the compile SDK at least as high as the target SDK. Lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. +By default, Codename One's Gradle 8 based Android builder uses the highest Android SDK you've installed, with a minimum of API 36, for both the compile and target SDK versions. If you override the target through the `android.targetSDKVersion` build hint, the builder honors it while keeping the compile SDK at least as high as the target SDK. Lowering the target may disable some compatibility libraries. Keeping the target current is strongly recommended for Play Store compliance. ===== Permission prompts