From ad59e89600e966be9f6a12882a6089e23ccc4f02 Mon Sep 17 00:00:00 2001 From: dgtv Date: Thu, 6 Aug 2026 22:27:06 -0500 Subject: [PATCH 1/2] [PR-27228] Upgrade Demo App target to Android 17 (API 37) Google Play requires target API >= 36 by Aug 31, 2026. Target 37 (latest stable, June 2026) so the app also clears the Aug 2027 deadline. App module only: compileSdk/targetSdk 35 -> 37, versionCode 4. The SDK library's public contract is unchanged (compileSdk 35, minSdk 16, v0.5.14) since the Play policy applies only to the uploaded app binary. Toolchain cascade required by compileSdk 37: AGP 8.5.2 -> 9.3.0, Gradle 8.7 -> 9.5.0, build-tools 37.0.0, unmock 0.7.8 -> 0.11.0, Groovy DSL migration (AGP 9 removed the old names; '=' assignment replaces the Gradle-10-removed space syntax), buildFeatures.buildConfig replaces the removed gradle.properties flag, CI image bump, jitpack.yml pins JDK 17 so JitPack tag builds survive Gradle 9. --- .circleci/config.yml | 4 +-- RELEASE.md | 12 ++++----- app/build.gradle | 32 ++++++++++++------------ build.gradle | 10 ++++---- gradle.properties | 12 ++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- jitpack.yml | 2 ++ sdk/build.gradle | 24 ++++++++++++------ 8 files changed, 53 insertions(+), 45 deletions(-) create mode 100644 jitpack.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 8426e0a..596f809 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: - auth: username: $DOCKERHUB_USERNAME password: $DOCKERHUB_ACCESS_TOKEN - image: cimg/android:2024.01.1 + image: cimg/android:2026.07.1 environment: JVM_OPTS: -Xmx3200m @@ -19,7 +19,7 @@ jobs: key: v2-dependencies-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} - run: name: Download dependencies - command: ./gradlew androidDependencies + command: ./gradlew :sdk:dependencies :app:dependencies - save_cache: paths: - ~/.gradle diff --git a/RELEASE.md b/RELEASE.md index f7123f2..75c1554 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -80,16 +80,16 @@ All version and build configuration lives in `gradle.properties`: | Property | Description | Example | |---|---|---| -| `android_build_tools` | Build tools version (shared) | `36.0.0` | +| `android_build_tools` | Build tools version (shared) | `37.0.0` | | `sdk_compile_sdk` | SDK compile SDK version | `35` | -| `sdk_target_sdk` | SDK target SDK version | `35` | +| `sdk_target_sdk` | SDK target SDK version (instrumented tests only as of AGP 9) | `35` | | `sdk_min_sdk` | SDK module minimum SDK | `16` | | `sdk_version_code` | SDK version code (integer) | `43` | | `sdk_version_name` | SDK version name (semver) | `0.5.14` | -| `app_compile_sdk` | App compile SDK version | `35` | -| `app_target_sdk` | App target SDK version | `35` | +| `app_compile_sdk` | App compile SDK version | `37` | +| `app_target_sdk` | App target SDK version | `37` | | `app_min_sdk` | App module minimum SDK | `21` | -| `app_version_code` | App module version code | `3` | -| `app_version_name` | App module version name | `1.0.0` | +| `app_version_code` | App module version code | `4` | +| `app_version_name` | App module version name | `1.0.1` | Both `sdk/build.gradle` and `app/build.gradle` read from these properties. Do not hardcode build config values in the module build files. diff --git a/app/build.gradle b/app/build.gradle index 5af7182..3bd39e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,35 +4,35 @@ def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(rootProject.file("keystore.properties"))) android { - namespace 'com.talkable.appdemo' + namespace = 'com.talkable.appdemo' signingConfigs { config { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile file(keystoreProperties['storeFile']) - storePassword keystoreProperties['storePassword'] + keyAlias = keystoreProperties['keyAlias'] + keyPassword = keystoreProperties['keyPassword'] + storeFile = file(keystoreProperties['storeFile']) + storePassword = keystoreProperties['storePassword'] } } - compileSdkVersion Integer.parseInt(project.app_compile_sdk) + compileSdk = Integer.parseInt(project.app_compile_sdk) defaultConfig { - applicationId "com.talkable.appdemo2" - minSdkVersion Integer.parseInt(project.app_min_sdk) - targetSdkVersion Integer.parseInt(project.app_target_sdk) - versionCode Integer.parseInt(project.app_version_code) - versionName project.app_version_name + applicationId = "com.talkable.appdemo2" + minSdk = Integer.parseInt(project.app_min_sdk) + targetSdk = Integer.parseInt(project.app_target_sdk) + versionCode = Integer.parseInt(project.app_version_code) + versionName = project.app_version_name } buildTypes { release { - minifyEnabled false + minifyEnabled = false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.config + signingConfig = signingConfigs.config } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } - buildToolsVersion project.android_build_tools + buildToolsVersion = project.android_build_tools } dependencies { diff --git a/build.gradle b/build.gradle index 8030920..edc1bf1 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - classpath 'com.github.bjoernq:unmockplugin:0.7.8' + classpath 'com.android.tools.build:gradle:9.3.0' + classpath 'com.github.bjoernq:unmockplugin:0.11.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -17,10 +17,10 @@ allprojects { repositories { mavenCentral() google() - maven { url 'https://jitpack.io' } + maven { url = 'https://jitpack.io' } } } -task clean(type: Delete) { - delete rootProject.buildDir +tasks.register('clean', Delete) { + delete rootProject.layout.buildDirectory } diff --git a/gradle.properties b/gradle.properties index 8b89402..1882074 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,13 +16,11 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -android.defaults.buildfeatures.buildconfig=true -android.enableJetifier=true android.nonFinalResIds=false android.nonTransitiveRClass=false android.useAndroidX=true -android_build_tools=36.0.0 +android_build_tools=37.0.0 sdk_compile_sdk=35 sdk_target_sdk=35 @@ -30,8 +28,8 @@ sdk_min_sdk=16 sdk_version_code=43 sdk_version_name=0.5.14 -app_compile_sdk=35 -app_target_sdk=35 +app_compile_sdk=37 +app_target_sdk=37 app_min_sdk=21 -app_version_code=3 -app_version_name=1.0.0 +app_version_code=4 +app_version_name=1.0.1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..1a70468 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..efde7bf --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +jdk: + - openjdk17 diff --git a/sdk/build.gradle b/sdk/build.gradle index c03b7e5..4102b31 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -5,32 +5,40 @@ apply plugin: 'maven-publish' group='com.github.talkable' android { - namespace 'com.talkable.sdk' - compileSdkVersion Integer.parseInt(project.sdk_compile_sdk) + namespace = 'com.talkable.sdk' + compileSdk = Integer.parseInt(project.sdk_compile_sdk) defaultConfig { - minSdkVersion Integer.parseInt(project.sdk_min_sdk) - targetSdkVersion Integer.parseInt(project.sdk_target_sdk) - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + minSdk = Integer.parseInt(project.sdk_min_sdk) + testInstrumentationRunner = 'androidx.test.runner.AndroidJUnitRunner' // Required for sdk version retrieval to work as of Android Studio 4.1.0: https://issuetracker.google.com/issues/158695880 buildConfigField 'int', 'VERSION_CODE', "${project.sdk_version_code}" buildConfigField 'String', 'VERSION_NAME', "\"${project.sdk_version_name}\"" } + buildFeatures { + buildConfig = true + } buildTypes { release { - minifyEnabled false + minifyEnabled = false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } testOptions { + // targetSdk on library modules only applies to instrumented tests as of AGP 9 + targetSdk = Integer.parseInt(project.sdk_target_sdk) unitTests.returnDefaultValues = true } lint { - abortOnError false + abortOnError = false } - buildToolsVersion project.android_build_tools + buildToolsVersion = project.android_build_tools publishing { singleVariant('release') { From e0b9c1f3588ce1e6e3eb0dfc2db1b8e2d96a8e99 Mon Sep 17 00:00:00 2001 From: dgtv Date: Fri, 7 Aug 2026 23:39:41 -0500 Subject: [PATCH 2/2] Fix AGP 9 proguard file requirement and unit tests on modern JDK - AGP 9 rejects getDefaultProguardFile('proguard-android.txt'); switch both modules to proguard-android-optimize.txt (inert while minifyEnabled = false). - SDK unit tests: PowerMock 1.6.6/Mockito 1.10.19 cannot run on JDK 17+ (module encapsulation), broken since CI dropped the Java 8 pin. Upgrade to PowerMock 2.0.9 / Mockito 2.28.2 (test-only deps) and open the JDK modules the mocking stack and OkHttp reflect into. Full :sdk:test suite passes on JDK 21 with Gradle 9.5/AGP 9.3. --- app/build.gradle | 2 +- sdk/build.gradle | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3bd39e2..ab4748b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,7 @@ android { buildTypes { release { minifyEnabled = false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig = signingConfigs.config } } diff --git a/sdk/build.gradle b/sdk/build.gradle index 4102b31..ed0bbd2 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -22,7 +22,7 @@ android { buildTypes { release { minifyEnabled = false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { @@ -34,6 +34,15 @@ android { // targetSdk on library modules only applies to instrumented tests as of AGP 9 targetSdk = Integer.parseInt(project.sdk_target_sdk) unitTests.returnDefaultValues = true + unitTests.all { + // PowerMock 1.6.6/Objenesis need reflective access that JDK 17+ encapsulates + jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED', + '--add-opens=java.base/java.lang.reflect=ALL-UNNAMED', + '--add-opens=java.base/java.util=ALL-UNNAMED', + '--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED', + // OkHttp 3.2.0 reflects into the JDK's SSL internals on the test JVM + '--add-opens=java.base/sun.security.ssl=ALL-UNNAMED' + } } lint { abortOnError = false @@ -68,14 +77,12 @@ dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:1.10.19' - testImplementation('org.powermock:powermock-api-mockito:1.6.6') { + testImplementation 'org.mockito:mockito-core:2.28.2' + testImplementation('org.powermock:powermock-api-mockito2:2.0.9') { exclude module: 'hamcrest-core' - exclude module: 'objenesis' } - testImplementation('org.powermock:powermock-module-junit4:1.6.6') { + testImplementation('org.powermock:powermock-module-junit4:2.0.9') { exclude module: 'hamcrest-core' - exclude module: 'objenesis' } androidTestImplementation 'androidx.annotation:annotation:1.8.2'