Skip to content

Comments

Fix/splits#4626

Merged
jamesarich merged 2 commits intomainfrom
fix/splits
Feb 22, 2026
Merged

Fix/splits#4626
jamesarich merged 2 commits intomainfrom
fix/splits

Conversation

@jamesarich
Copy link
Collaborator

This pull request introduces improvements to the Gradle build configuration for the Android app, focusing on simplifying flavor handling and making APK splitting conditional based on build tasks. The changes streamline how product flavors are configured, automate application ID assignment for debug builds, and ensure ABI splits are only enabled for certain build types.

Build configuration improvements:

  • Conditional ABI splits: ABI splits are now enabled only when not building a bundle or Google variant, reducing unnecessary APK generation for those tasks. (app/build.gradle.kts, app/build.gradle.ktsR131-R139)
  • Simplified product flavor configuration: The productFlavors block now uses configureEach to set the versionName for all flavors dynamically, and sets the MAPS_API_KEY placeholder for the Google flavor. (app/build.gradle.kts, app/build.gradle.ktsL154-R164)

Flavor and build type automation:

  • Automated application ID assignment for debug builds: The onVariants callback now assigns application IDs for all debug variants based on their flavor, instead of hardcoding for each flavor. (app/build.gradle.kts, app/build.gradle.ktsL184-R195)

This commit modifies the build process to conditionally disable ABI (Application Binary Interface) splits.

The new logic checks if the current Gradle task name contains "bundle" or "google". If it does, ABI splitting is disabled. This is necessary because the Google Play Store requires a universal APK or an App Bundle for releases, and ABI splits are not compatible with this format. For all other builds, such as for F-Droid, ABI splits remain enabled to generate smaller, architecture-specific APKs.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit refactors the `app/build.gradle.kts` file to simplify the configuration of product flavors and their corresponding application IDs.

The previous separate configurations for the "google" and "fdroid" product flavors have been consolidated into a single `configureEach` block. This block now dynamically sets the `versionName` for each flavor based on its name, reducing code duplication.

Similarly, the `androidComponents` block has been streamlined. Instead of targeting specific variant names like "fdroidDebug" and "googleDebug", the logic now uses a `withBuildType("debug")` selector. It then dynamically constructs the `applicationId` for debug builds by appending the flavor name (e.g., "com.geeksville.mesh.google.debug"), making the process more scalable and maintainable.

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
@github-actions github-actions bot added the bugfix PR tag label Feb 22, 2026
@jamesarich jamesarich merged commit 96adc70 into main Feb 22, 2026
4 checks passed
@jamesarich jamesarich deleted the fix/splits branch February 22, 2026 14:48
@codecov
Copy link

codecov bot commented Feb 22, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
340 1 339 0
View the top 1 failed test(s) by shortest run time
com.geeksville.mesh.repository.radio.NordicBleInterfaceDrainTest::drainPacketQueueAndDispatch reads multiple packets until empty
Stack Traces | 9.28s run time
java.lang.AssertionError: Verification failed: call 1 of 1: RadioInterfaceService(#1).handleFromRadio(eq([1]))) was not called (timeout = 2000 ms)
	at io.mockk.impl.recording.states.VerifyingState.failIfNotPassed(VerifyingState.kt:65)
	at io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:39)
	at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:50)
	at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:63)
	at io.mockk.impl.eval.VerifyBlockEvaluator.verify(VerifyBlockEvaluator.kt:32)
	at io.mockk.MockKDsl.internalVerify(API.kt:128)
	at io.mockk.MockKKt.verify(MockK.kt:240)
	at io.mockk.MockKKt.verify$default(MockK.kt:230)
	at com.geeksville.mesh.repository.radio.NordicBleInterfaceDrainTest$drainPacketQueueAndDispatch reads multiple packets until empty$1.invokeSuspend(NordicBleInterfaceDrainTest.kt:153)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:233)
	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:152)
	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:470)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core(CancellableContinuationImpl.kt:504)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$kotlinx_coroutines_core$default(CancellableContinuationImpl.kt:493)
	at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:596)
	at kotlinx.coroutines.test.CancellableContinuationRunnable.run(TestDispatcher.kt:60)
	at kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:24)
	at kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTaskUnless$kotlinx_coroutines_test(TestCoroutineScheduler.kt:99)
	at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$workRunner$1.invokeSuspend(TestBuilders.kt:326)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:263)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:94)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:70)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:48)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:10)
	at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:309)
	at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source)
	at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:167)
	at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source)
	at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0$default(TestBuilders.kt:159)
	at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0$default(Unknown Source)
	at com.geeksville.mesh.repository.radio.NordicBleInterfaceDrainTest.drainPacketQueueAndDispatch reads multiple packets until empty(NordicBleInterfaceDrainTest.kt:54)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestExecutor.runRequest(JUnitTestExecutor.java:175)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestExecutor.accept(JUnitTestExecutor.java:84)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestExecutor.accept(JUnitTestExecutor.java:47)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestDefinitionProcessor.processTestDefinition(AbstractJUnitTestDefinitionProcessor.java:65)
	at org.gradle.api.internal.tasks.testing.SuiteTestDefinitionProcessor.processTestDefinition(SuiteTestDefinitionProcessor.java:54)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at org.gradle.internal.dispatch.MethodInvocation.invokeOn(MethodInvocation.java:77)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:28)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:19)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)
	at jdk.proxy1/jdk.proxy1.$Proxy4.processTestDefinition(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:178)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:126)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:72)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant