I'm automating the creation of screenshots using instrumented tests (androidTest
) on a debug version of my app using UiAutomator and a shell script that uses adb pull
.
Once each screenshot has been taken, I need to be given access to a directory on the Android device to write each image to, that:
The Android docs for choosing a storage API are centred around normal usage by a user of the production app, and don't specify whether adb can access them.
There is so much outdated advice (thanks to the many, many breaking changes to the Android storage APIs over the last 10 years) that the signal-to-noise ratio for existing advice is very low, and checking whether each found solution still works is non-trivial.
Which storage API should/can I use that satisfies the above requirements?
In the end, the only modern API that was readable externally was MediaStore.
Using code adapted from the Android docs:
private fun saveWithMediaStore(context: Context, bm: Bitmap, fileName: String) {
// Add a media item that other apps don't see until the item is
// fully written to the media store.
val resolver = context.contentResolver
// Find all audio files on the primary external storage device.
val imageCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL_PRIMARY
)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val imageDetails = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.IS_PENDING, 1)
}
val songContentUri = resolver.insert(imageCollection, imageDetails)
// "w" for write.
resolver.openFileDescriptor(songContentUri!!, "w", null).use { pfd: ParcelFileDescriptor? ->
// Write data into the pending image file.
if(pfd !=null) {
try {
val fOut = FileOutputStream(pfd.fileDescriptor)
bm.compress(Bitmap.CompressFormat.PNG, 85, fOut)
fOut.flush()
fOut.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// Now we're finished, release the "pending" status and let other apps view the image.
imageDetails.clear()
imageDetails.put(MediaStore.Images.Media.IS_PENDING, 0)
resolver.update(songContentUri, imageDetails, null, null)
}
On my emulated device, this wrote the screenshot to
/storage/emulated/0/Pictures/<filename_you_specify>
so you can then just do
adb pull /storage/emulated/0/Pictures/<filename_you_specify>
to get the file from the device.