Support full screen screenshot and favour them in a SystemUiTestRule - #163
Support full screen screenshot and favour them in a SystemUiTestRule#163sergio-sastre wants to merge 1 commit into
Conversation
The SystemUiTestRule helps optimize for full screenshot tests with stable and reproducible statusbar and navigation bar
WalkthroughAdds ChangesSystem UI test capture
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant Test
participant SystemUiTestRule
participant StatusBarTestRule
participant NavigationModeTestRule
participant UiDevice
Test->>SystemUiTestRule: apply test rule
SystemUiTestRule->>StatusBarTestRule: configure clock time
SystemUiTestRule->>NavigationModeTestRule: configure navigation mode
Test->>SystemUiTestRule: drawFullScreenToBitmap()
SystemUiTestRule->>UiDevice: wait for idle and capture screenshot
UiDevice-->>Test: return full-screen Bitmap
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt (1)
14-15: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSynchronize System UI before capturing.
UiDevice.waitForIdle()waits for the current application, not necessarily for status/navigation-bar updates. The composed rules explicitly perform additional instrumentation idleness waits after changing System UI; mirror that synchronization here or verify a stronger stable-state condition before capturing. (developer.android.com)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt` around lines 14 - 15, Update drawFullScreenToBitmap to synchronize instrumentation/system UI after UiDevice.waitForIdle() and before takeScreenshot(). Mirror the additional instrumentation idleness wait used by the composed rules, or apply an equivalent stable-state check, while preserving the existing screenshot behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt`:
- Around line 14-15: Update drawFullScreenToBitmap so it does not force-unwrap
the nullable result from UiDevice.takeScreenshot(). Return the nullable Bitmap
or explicitly handle null by throwing a descriptive IllegalStateException that
identifies the screenshot failure.
---
Nitpick comments:
In
`@utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt`:
- Around line 14-15: Update drawFullScreenToBitmap to synchronize
instrumentation/system UI after UiDevice.waitForIdle() and before
takeScreenshot(). Mirror the additional instrumentation idleness wait used by
the composed rules, or apply an equivalent stable-state check, while preserving
the existing screenshot behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ceac09af-25ab-4bc8-9cb6-0690b7cec124
📒 Files selected for processing (2)
utils/src/main/java/sergio/sastre/uitesting/utils/testrules/systemui/SystemUiTestRule.ktutils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt
| fun drawFullScreenToBitmap(): Bitmap = | ||
| UiDevice.getInstance(getInstrumentation()).apply { waitForIdle() }.takeScreenshot()!! No newline at end of file |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
curl -fsSL 'https://developer.android.com/reference/kotlin/androidx/test/uiautomator/UiDevice' \
| grep -A8 -B2 'takeScreenshot(): Bitmap?'Repository: sergio-sastre/AndroidUiTestingUtils
Length of output: 173
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== file exists =="
git ls-files | grep -F 'utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt' || true
echo "== file contents =="
cat -n 'utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt'
echo "== related usages =="
rg -n "drawFullScreenToBitmap|takeScreenshot\\(" .
echo "== dependency refs for uiAutomator =="
rg -n "androidx.test.uiautomator|uiautomator|UiDevice" . --glob '!build/**' --glob '!*.png' --glob '!*.jpg' | head -100
echo "== gradle deps around instrumentation =="
git ls-files | grep -E '(^|/)(build\.gradle|build\.gradle\.kts|gradle\.properties)$' | while read -r f; do
echo "-- $f"
sed -n '1,220p' "$f" | grep -n -i "androidx.test\|uiautomator\|instrumentation\|compileSdk\|minSdk" || true
doneRepository: sergio-sastre/AndroidUiTestingUtils
Length of output: 5283
🌐 Web query:
UiDevice takeScreenshot return type androidx.test.uiautomator
💡 Result:
In the androidx.test.uiautomator library, the UiDevice.takeScreenshot methods that save a screenshot to a file return a boolean value [1][2][3]. These methods return true if the screenshot was created and saved successfully, and false otherwise [4][5][3]. The available signatures for these methods are: - boolean takeScreenshot(File storePath) [2][6] - boolean takeScreenshot(File storePath, float scale, int quality) [2][6] Note that while there are other methods within the androidx.test.uiautomator package or extension libraries that return a Bitmap (such as those associated with UiObject2 or accessibility objects), the specific UiDevice.takeScreenshot methods designed to write to a File path return a boolean [2][6][7].
Citations:
- 1: https://developer.android.com/reference/kotlin/androidx/test/uiautomator/UiDevice
- 2: https://developer.android.com/reference/androidx/test/uiautomator/UiDevice
- 3: https://android.googlesource.com/platform/frameworks/support/+/efd96b5e24d301da39fd1e47b1031ac4df5608ed/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
- 4: https://developer.android.google.cn/reference/androidx/test/uiautomator/UiDevice
- 5: https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
- 6: https://android.googlesource.com/platform/frameworks/support/+/4a82be8eaba0cb03d9ea4fe58bc99da7853a4237/test/uiautomator/uiautomator/api/current.txt
- 7: https://developer.android.com/reference/kotlin/androidx/test/uiautomator/package-summary
🌐 Web query:
"UiDevice.takeScreenshot()" "Bitmap?" "androidx.test.uiautomator"
💡 Result:
The UiDevice.takeScreenshot() method in androidx.test.uiautomator does not return a Bitmap object; it returns a boolean indicating whether the screenshot was successfully saved to a specified File [1][2]. The standard UiDevice methods for taking screenshots require a file path to store the output [1][3]: * boolean takeScreenshot(File storePath): Saves a PNG to the specified file using default settings (1.0f scale, 90% quality) [1][2]. * boolean takeScreenshot(File storePath, float scale, int quality): Saves a PNG with customizable scale and quality [1][2]. If your goal is to obtain a Bitmap object directly for in-memory processing or visual assertions, the modern UI Automator framework provides alternative approaches [4]. When using the uiAutomator { ... } test block, you can capture bitmaps from specific UI components or windows [4]: val fullScreenBitmap: Bitmap = activeWindow.takeScreenshot val elementBitmap: Bitmap = onElement {... }.takeScreenshot Alternatively, for capturing the entire screen as a Bitmap outside of this specific DSL, you can use androidx.test.core.app.DeviceCapture.takeScreenshot(), which is designed to return a Bitmap [5]. Unlike the legacy UiDevice.takeScreenshot() method, DeviceCapture includes logic to ensure a stable screenshot by forcing the application's root window views to redraw [5]. Top results: [1][4][5]
Citations:
- 1: https://developer.android.com/reference/androidx/test/uiautomator/UiDevice
- 2: https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
- 3: https://developer.android.google.cn/reference/androidx/test/uiautomator/UiDevice
- 4: https://developer.android.com/training/testing/other-components/ui-automator
- 5: https://developer.android.com/reference/androidx/test/core/app/DeviceCapture
🌐 Web query:
androidx.test.core.app.DeviceCapture takeScreenshot return type
💡 Result:
The androidx.test.core.app.DeviceCapture.takeScreenshot() method returns a android.graphics.Bitmap object [1][2][3]. This method captures an image of the device's screen [2]. It is intended for debugging purposes where a full-screen image is required [2]. Note that the method throws an IllegalStateException if called on the main thread, as it relies on UiAutomation [2][4], and may throw a RuntimeException if the screenshot operation fails [2][4].
Citations:
- 1: https://developer.android.com/reference/androidx/test/core/app/DeviceCapture
- 2: https://developer.android.com/reference/kotlin/androidx/test/core/app/package-summary
- 3: https://developer.android.google.cn/reference/androidx/test/core/app/DeviceCapture
- 4: DeviceCapture.takeScreenshot() timeout issue android/android-test#2273
🌐 Web query:
"UiDevice.java" "takeScreenshot" "Bitmap" "androidx.test.uiautomator"
💡 Result:
In the androidx.test.uiautomator library, the UiDevice class does not provide a public method that returns a Bitmap directly [1][2]. Instead, its screenshot capabilities are designed to capture the screen and save it directly to a file [1][3]. The available methods in UiDevice for taking screenshots are: 1. boolean takeScreenshot(File storePath): Takes a screenshot of the default display and saves it as a PNG file at the specified path, using a default scale of 1.0f and 90% quality [1][3][2]. 2. boolean takeScreenshot(File storePath, float scale, int quality): Takes a screenshot of the default display and saves it as a PNG file, allowing you to specify the scale and compression quality [1][3][2]. If you require a Bitmap object, you may need to capture the screen using alternative Android instrumentation methods (such as UiAutomation.takeScreenshot) or utilize the newer UI Automator API (if using the latest library versions), which provides more flexible screenshot and assertion capabilities through its own domain-specific language (DSL) [4][2]. Note that recent updates to the UI Automator framework have introduced a new, more expressive API that allows for capturing screenshots of specific UI elements or windows as Bitmaps [4]. Top results for reference: [1] https://developer.android.com/reference/androidx/test/uiautomator/UiDevice [3] https://developer.android.google.cn/reference/androidx/test/uiautomator/UiDevice [4] https://developer.android.com/training/testing/other-components/ui-automator [2] https://android.googlesource.com/platform/frameworks/support/+/efd96b5e24d301da39fd1e47b1031ac4df5608ed/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
Citations:
- 1: https://developer.android.com/reference/androidx/test/uiautomator/UiDevice
- 2: https://android.googlesource.com/platform/frameworks/support/+/efd96b5e24d301da39fd1e47b1031ac4df5608ed/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
- 3: https://developer.android.google.cn/reference/androidx/test/uiautomator/UiDevice
- 4: https://developer.android.com/training/testing/other-components/ui-automator
🌐 Web query:
"UiDevice.java" "boolean takeScreenshot" "UiAutomation" "Bitmap"
💡 Result:
In the Android testing ecosystem, UiDevice and UiAutomation provide distinct methods for capturing screenshots, differing primarily in their return types and intended use cases. UiDevice (part of androidx.test.uiautomator) provides methods that store a screenshot directly to a file on the device's storage [1][2]. These methods return a boolean to indicate whether the operation was successful [3][1]. - Method: boolean takeScreenshot(File storePath) [1][4] - Method: boolean takeScreenshot(File storePath, float scale, int quality) [1][4] These methods are designed for high-level UI testing, where saving the screenshot to a specific path is the primary objective [3][5]. UiAutomation (part of android.app) provides a lower-level, more direct way to obtain a screenshot as a Bitmap object [6][7]. - Method: Bitmap takeScreenshot [6][7] (Available since API level 18) - Method: Bitmap takeScreenshot(Window window) [6][7] (Available since API level 34) The UiAutomation methods return a Bitmap directly, which allows for programmatic inspection, comparison, or manipulation of the pixel data within the test code [8][9]. If the capture fails, these methods return null [6][8][7]. In summary, use UiDevice when you need to save a screenshot file to disk for test reporting, and use UiAutomation when you need to work with the Bitmap data in memory [1][6][8].
Citations:
- 1: https://developer.android.com/reference/androidx/test/uiautomator/UiDevice
- 2: https://developer.android.com/reference/kotlin/androidx/test/uiautomator/UiDevice
- 3: https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
- 4: https://developer.android.google.cn/reference/androidx/test/uiautomator/UiDevice
- 5: https://android.googlesource.com/platform/frameworks/uiautomator/+/android-support-test/src/main/java/android/support/test/uiautomator/UiDevice.java
- 6: https://developer.android.com/reference/android/app/UiAutomation
- 7: https://developer.android.com/reference/kotlin/android/app/UiAutomation
- 8: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/UiAutomation.java
- 9: https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/java/android/app/UiAutomation.java
🌐 Web query:
"UiDevice.java" "UiDeviceBitmapProvider" "takeScreenshot()" null
💡 Result:
When UiDevice.takeScreenshot returns null or fails, it typically indicates that the underlying UiAutomation.takeScreenshot call failed to capture a valid bitmap [1][2]. This commonly occurs due to environment-specific limitations rather than a bug in the code itself [3][4]. The takeScreenshot method in UiDevice relies on the system-level UiAutomation bridge [1]. If the system is unable to return a bitmap—often because the window has not been laid out, lacks a surface control, or has encountered an internal error in SurfaceFlinger—the method returns null [2]. Common causes for this issue include: 1. Security Restrictions: If the target application has set the FLAG_SECURE window flag, the system intentionally prevents screenshots, which will cause the capture to return null [3]. 2. Emulator/AVD Configurations: Using hardware acceleration (GPU) or snapshot features in certain AVD configurations has historically caused issues with UI Automator screenshots [3]. Disabling these features or testing on a physical device is a common workaround [3]. 3. Permissions and File Access: Ensure the application has the necessary storage permissions and that the provided file path is valid and writable [5]. 4. ADB and Tool State: Sometimes the ADB process or the UI Automator viewer itself can enter a state where it fails to communicate correctly with the device. Restarting the ADB server or reconnecting the device can resolve transient issues [4]. To debug, verify if the issue persists across different applications; if it is isolated to one app, it is likely caused by the FLAG_SECURE flag or similar security policies [3][4]. If it happens globally, investigate the emulator settings or physical device environment [3][5].
Citations:
- 1: https://android.googlesource.com/platform/frameworks/support/+/efd96b5e24d301da39fd1e47b1031ac4df5608ed/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
- 2: https://developer.android.com/reference/kotlin/android/app/UiAutomation
- 3: https://stackoverflow.com/questions/25818574/ui-automator-viewer-receiving-error-taking-device-screenshot-null-every-time
- 4: https://stackoverflow.com/questions/39930375/ui-automator-viewer-device-screenshot-reason-error-while-creating-temporar
- 5: https://stackoverflow.com/questions/72915796/uidevice-takescreenshot-not-saving
🌐 Web query:
site:github.com/android/uiautomator UiDevice.java public Bitmap takeScreenshot
💡 Result:
The Bitmap version is:
public @nullable Bitmap takeScreenshot()
in androidx.test.uiautomator.UiDevice (added in 2.4.0). The source is in the AndroidX UiDevice.java file, and the API docs confirm it returns the screenshot bitmap or null on failure. (android.googlesource.com)
If you meant the older platform API, android.app.UiAutomation also has public Bitmap takeScreenshot(). (developer.android.com)
Citations:
- 1: https://android.googlesource.com/platform/frameworks/support/%2B/a92b75a787d9ac93387f1e7ab9813ec9db43108a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java?utm_source=openai
- 2: https://developer.android.com/reference/android/app/UiAutomation?utm_source=openai
Avoid force-unwrapping the screenshot result.
UiDevice.takeScreenshot() returns a nullable Bitmap; replacing the opaque NullPointerException with a nullable result or a descriptive IllegalStateException makes screenshot failures easier to diagnose.
Proposed fix
fun drawFullScreenToBitmap(): Bitmap =
- UiDevice.getInstance(getInstrumentation()).apply { waitForIdle() }.takeScreenshot()!!
+ UiDevice.getInstance(getInstrumentation())
+ .apply { waitForIdle() }
+ .takeScreenshot()
+ ?: error("Unable to capture a full-screen screenshot")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fun drawFullScreenToBitmap(): Bitmap = | |
| UiDevice.getInstance(getInstrumentation()).apply { waitForIdle() }.takeScreenshot()!! | |
| fun drawFullScreenToBitmap(): Bitmap = | |
| UiDevice.getInstance(getInstrumentation()) | |
| .apply { waitForIdle() } | |
| .takeScreenshot() | |
| ?: error("Unable to capture a full-screen screenshot") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@utils/src/main/java/sergio/sastre/uitesting/utils/utils/DrawFullScreenToBitmap.kt`
around lines 14 - 15, Update drawFullScreenToBitmap so it does not force-unwrap
the nullable result from UiDevice.takeScreenshot(). Return the nullable Bitmap
or explicitly handle null by throwing a descriptive IllegalStateException that
identifies the screenshot failure.
The SystemUiTestRule helps optimize for full screenshot tests with stable and reproducible statusbar and navigation bar
Summary by CodeRabbit