Automate Android Screenshots with fastlane screengrab
Set up fastlane screengrab to capture Android screenshots in every locale: Gradle dependency, manifest permissions, LocaleTestRule, Screengrabfile and output.
ScreenShipper team · · 7 min read
Quick answer: fastlane screengrab runs your instrumented Android tests on an emulator or device once per locale and saves a screenshot wherever a test calls Screengrab.screenshot("01_home"). Add the tools.fastlane:screengrab test dependency, a few debug-only manifest permissions and a LocaleTestRule, build with ./gradlew assembleDebug assembleAndroidTest, then run fastlane screengrab. Images land in fastlane/metadata/android.
How screengrab Works
screengrab is the Android counterpart of fastlane snapshot. It doesn't record taps or explore your app; it runs the instrumented tests you write, switching the device locale between runs.
One run looks like this:
- You build two APKs: your debug app and its instrumentation test APK.
- screengrab installs both on a connected emulator or device.
- For each locale in your list, it runs the tests.
LocaleTestRuleswitches the app's locale before they start. - Each
Screengrab.screenshot()call captures the screen. - screengrab pulls the images into
fastlane/metadata/android, organized per locale in the folder layout fastlane's supply tool uploads from, such asen-US/images/phoneScreenshots.
Because the tests drive real UI, you can use Espresso, UI Automator or Compose test APIs to navigate. The Espresso documentation is the best starting point if you haven't written instrumented tests before.
Step 1: Add the Dependency and Permissions
Install fastlane with Bundler, Homebrew or RubyGems, then add the screengrab library to your app module's test dependencies:
- Groovy:
androidTestImplementation 'tools.fastlane:screengrab:x.x.x' - Kotlin DSL:
androidTestImplementation("tools.fastlane:screengrab:x.x.x")
Replace x.x.x with the latest version listed in the fastlane docs. Since screengrab 2.0.0, its test dependencies use AndroidX. Your defaultConfig should use androidx.test.runner.AndroidJUnitRunner as the testInstrumentationRunner, which is also screengrab's default.
Next, add permissions to src/debug/AndroidManifest.xml, so they only exist in debug builds and never reach your release app. The screengrab action documentation lists:
android.permission.WRITE_EXTERNAL_STORAGEwithandroid:maxSdkVersion="18", for storing screenshots where adb can reach them on old devicesandroid.permission.CHANGE_CONFIGURATIONwithtools:ignore="ProtectedPermissions", which lets screengrab change the localeandroid.permission.DUMPwithtools:ignore="ProtectedPermissions"
The tools:ignore attribute requires the tools namespace on the manifest element: xmlns:tools="http://schemas.android.com/tools". Older guides list a different set, including keyguard and wake lock permissions, so follow the docs for the screengrab version you install.
Step 2: Write the Screenshot Test
Create a JUnit 4 test class under src/androidTest. It needs two rules: one to set the locale and one to launch your activity.
- Java:
@ClassRule public static final LocaleTestRule localeTestRule = new LocaleTestRule(); - Kotlin:
@Rule @JvmField val localeTestRule = LocaleTestRule()
In Kotlin, the docs stress that @JvmField is required and that declaring the rule in a companion object won't work. For launching, the docs' examples use ActivityScenarioRule<MainActivity>; in Kotlin that's @get:Rule val activityRule = ActivityScenarioRule(MainActivity::class.java).
Then navigate and capture:
- Wait for the first screen to load and call
Screengrab.screenshot("01_home"). - Perform an action with Espresso, such as
onView(withId(R.id.search)).perform(click()). - Call
Screengrab.screenshot("02_search"), and so on for each screen you want.
Numeric prefixes keep the files in gallery order.
Screenshot strategy and status bar
In screengrab 2.x, the default capture strategy uses UI Automator, which requires API level 18 or higher and captures the whole screen, including dialogs. For very old devices, the docs point to the 1.x library with DecorViewScreenshotStrategy.
For a tidy status bar, call CleanStatusBar.enableWithDefaults() in a @BeforeClass method and CleanStatusBar.disable() in @AfterClass. This works on API level 23 and higher. Our clean status bar guide also covers Android's demo mode if you capture screens by hand.
Step 3: Build the APKs and Configure the Screengrabfile
Build both APKs with ./gradlew assembleDebug assembleAndroidTest. With a standard Gradle setup, they appear at app/build/outputs/apk/debug/app-debug.apk and app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk.
Run fastlane screengrab init to generate a Screengrabfile. The template sets locales(["en-US", "fr-FR", "it-IT"]) and clear_previous_screenshots(true) and includes commented-out lines for package names and APK paths. The options you'll use most:
| Option | Example or default | Purpose |
|---|---|---|
locales | locales(["en-US", "de-DE", "ja-JP"]) | Locales to capture; default is en-US |
app_package_name | app_package_name("com.example.app") | The app under test |
app_apk_path / tests_apk_path | paths from your Gradle build | Which APKs to install |
use_tests_in_packages / use_tests_in_classes | your screenshot test package or class | Run only screenshot tests, not your whole suite |
clear_previous_screenshots | true | Remove old images first |
output_directory | default fastlane/metadata/android | Where images are saved |
device_type | default phone | Google Play type: phone, sevenInch, tenInch, tv or wear |
specific_device | an adb devices serial | Pick one device when several are connected |
use_timestamp_suffix | default true | Adds a timestamp to file names |
launch_arguments | launch_arguments(["demo_mode true"]) | Values your tests read with InstrumentationRegistry.getArguments() |
exit_on_test_failure | true or false | Whether a failing test stops the run |
Set use_timestamp_suffix(false) if you want stable file names that overwrite each run. Options such as ending_locale and use_adb_root are deprecated, so leave them out of new setups. Run fastlane action screengrab to see the full list for your version.
Step 4: Run screengrab and Upload
Start an emulator from Android Studio's Device Manager, or connect a phone with USB debugging enabled, then run fastlane screengrab from the project folder. screengrab works on emulators and real devices.
For tablets, run it again on a tablet emulator with device_type set to sevenInch or tenInch, so the images land in the matching folders. Google recommends at least four large-screen screenshots for tablet visibility; our tablet screenshot guide explains why.
To automate the whole flow, put it in a lane: build_android_app(task: "assemble", build_type: "Debug"), then build_android_app(task: "assemble", build_type: "AndroidTest"), then capture_android_screenshots, then upload_to_play_store. To upload screenshots on their own, fastlane supply reads the same fastlane/metadata/android folder.
Make the Captures Google Play Ready
Raw captures often can't be uploaded unchanged. Google Play currently requires:
- 2 to 8 screenshots per device type to publish
- JPEG or 24-bit PNG with no alpha channel, up to 8 MB each
- Each side between 320 and 3840 pixels
- A long side no more than twice the short side
That last rule catches many teams. Current phones often have tall screens such as 1080 × 2400, which is more than 2 to 1, so a full-screen capture can fail the check. Place the capture inside a designed canvas, such as 1080 × 1920, with a headline and device frame. Screenshots at least 1080 pixels on the short side are also what Google looks for when deciding eligibility for featured placements. The Google Play screenshot sizes guide and Google Play screenshot requirements have the details.
If you want framing inside fastlane, fastlane frameit android adds device frames; see our frameit guide. For a designed set in many languages, use the captures as source images in your design tool.
Troubleshooting screengrab
| Problem | What to check |
|---|---|
| Every screenshot is in English | CHANGE_CONFIGURATION is in the debug manifest; the locale rule is declared as shown above |
| Kotlin locale rule has no effect | Use @Rule @JvmField, not a companion object |
| Tests not found | app_package_name, tests_apk_path and the instrumentation runner match your build |
| Wrong device used | Set specific_device to the serial from adb devices |
| Status bar shows notifications | Enable CleanStatusBar (API 23 and higher) |
| Dialogs missing from captures | Make sure you're on the UI Automator strategy |
Frequently Asked Questions
Does screengrab work with Jetpack Compose?
Yes. Compose UI tests are instrumented tests, so you can call Screengrab.screenshot() from them, and the default UI Automator strategy captures the whole screen regardless of how it was drawn.
Can I use screengrab with Flutter or React Native?
screengrab is built for native instrumented tests. Flutter apps usually capture screenshots with the integration_test package instead; see Flutter App Store screenshots.
Is screengrab the Android version of fastlane snapshot?
Yes. snapshot drives Xcode UI tests on iOS simulators, while screengrab drives instrumented tests on Android. Our fastlane snapshot guide covers the iOS side.
Where does screengrab save the screenshots?
In fastlane/metadata/android inside the folder where you ran it, split by locale and device type. That's the structure fastlane supply uploads from.
Sources
From raw captures to store-ready screenshots
Drop your simulator or emulator captures into a template and export every store size, or frame a single screen with the free mockup generator.