Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| throw AndroidSnaptestingNoDeviceProviderInstrumentTestTasksException() | ||
| } | ||
|
|
||
| val extension = project.extensions.findByType(TestedExtension::class.java) |
There was a problem hiding this comment.
AGP 9.x removed TestedExtension from the public API. We now use the stable
variant API (AndroidComponentsExtension / ApplicationAndroidComponentsExtension) which is the recommended way to interact with Android build variants since AGP 7.x.
| .firstOrNull { it.name == deviceProviderTask.variantName } | ||
| ?: throw RuntimeException("TestVariant not found for ${deviceProviderTask.variantName}") | ||
| val applicationIdProvider = providerFactory.provider { testedVariant.applicationId } | ||
| val adbExecutablePath = extension.adbExecutable.absolutePath |
There was a problem hiding this comment.
adbExecutable was removed from TestedExtension in AGP 9.x.
sdkComponents.adb is the stable replacement: it returns a lazy Provider pointing to the adb binary resolved from the configured SDK.
| it.iDevice.executeShellCommand("rm -rf ${getDeviceAndroidSnaptestingRootAbsolutePath()}", receiver) | ||
| println(receiver.output) | ||
| devices.forEach { device -> | ||
| runAdb(device.serialNumber, "shell", "rm", "-rf", getDeviceAndroidSnaptestingRootAbsolutePath()) |
There was a problem hiding this comment.
Previously used DDMLib's IDevice.executeShellCommand() to run 'rm -rf' on the device. Now delegates to runAdb(), which spawns an 'adb -s shell rm -rf ...' subprocess.
|
|
||
| private fun getDeviceAndroidSnaptestingRootAbsolutePath(): String = | ||
| "${FileListingService.DIRECTORY_SDCARD}/Download/android-snaptesting/$applicationId" | ||
| "/sdcard/Download/android-snaptesting/$applicationId" |
There was a problem hiding this comment.
FileListingService.DIRECTORY_SDCARD was a DDMLib constant ("/sdcard"). Using the literal string directly removes the DDMLib dependency entirely.
| destinationPath: String, | ||
| ) { | ||
| val fileEntry = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice).toFileEntry() | ||
| val remotePath = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice) |
There was a problem hiding this comment.
The old implementation built a FileEntry tree using DDMLib's FileListingService and then called IDevice.pullFile() for each entry.
The new implementation runs adb shell ls <remotePath> to list files and
adb pull <remote> <local> for each one, filtering out error lines from ls in case the folder doesn't exist yet on the device.
| println(output) | ||
| } | ||
|
|
||
| private fun runAdbCapture(serial: String, vararg args: String): String { |
There was a problem hiding this comment.
runAdbCapture spawns a subprocess with the adb binary, targeting a specific device by serial (-s flag), merges stderr into stdout, and waits up to 60 seconds.
| ) { | ||
| device.fileListingService.getChildrenSync(androidSnaptestingDeviceFolder).forEach { | ||
| device.pullFile(it.fullPath, "$destinationPath/${it.name}") | ||
| private fun runAdb(serial: String, vararg args: String) { |
There was a problem hiding this comment.
Convenience wrapper that additionally prints the output to the Gradle log. Both replace the DDMLib IDevice shell/pull APIs removed in AGP 9.x.
🎟️ Jira ticket
ANDROID-17464
🥅 What's the goal?
Migrate the Gradle plugin from deprecated AGP internal APIs to the modern stable APIs, ensuring compatibility with newer AGP versions.
🚧 How do we do it?
TestedExtensionwithAndroidComponentsExtension+ApplicationAndroidComponentsExtension(new variant API) to resolveapplicationIdandadbpath.applicationIdper variant at configuration time usingonVariants {}as a lazyProvider<String>.ddmlibusage (IDevice,FileListingService,CollectingOutputReceiver) inDeviceFileManagerand replace with directadbsubprocess calls viaProcessBuilder.📘 Documentation changes?
🧪 How can I test this?
Run the connected Android tests with and without record mode to verify snapshots are pulled and reports are generated correctly.
Grabacion.de.pantalla.2026-03-30.a.las.16.21.22.mov
Grabacion.de.pantalla.2026-03-30.a.las.16.23.33.mov