How to Debug Split Screen Issues in Mobile Apps
How to Debug Split Screen Issues in Mobile Apps
How to Debug Split Screen Issues in Mobile Apps
Understanding Split Screen Behavior on Mobile
Split‑screen mode lets users run two apps side‑by‑side (Android) or in a pane (iPadOS/iOS Split View). When an app enters this mode the system delivers a new window‑size configuration that can differ dramatically from the full‑screen baseline. The UI is laid out again, resources may be re‑selected, and lifecycle callbacks such as onConfigurationChanged (Android) or viewWillTransition(to:with:) (iOS) fire. If the app assumes a fixed screen width, hard‑codes padding, or relies on orientation‑specific resources, the new constraints can expose layout bugs, clipped controls, or even crashes.
On Android the relevant API is WindowMetrics (API 30+) or the deprecated Display#getSize. On iPadOS the trait collection provides horizontalSizeClass and verticalSizeClass. Both platforms also send a configuration change when the divider moves, meaning the app may receive multiple size updates while the user drags the split‑screen separator. Understanding this flow is the first step to diagnosing why a UI that works in full‑screen breaks when the window is resized.
Android Multi‑Window Fundamentals
- Supported modes – Freeform (API 24+), split‑screen (API 24+), and picture‑in‑picture.
- Configuration changes –
screenWidthDp,screenHeightDp,smallestScreenWidthDp, andorientationcan all change. - Lifecycle – If
android:configChangesdoes not includescreenSize|smallestScreenSize|orientation, the activity is destroyed and recreated; otherwiseonConfigurationChangedreceives the new metrics.
iPadOS/iOS Split View Fundamentals
- Size classes – Regular/Compact for horizontal and vertical axes; split‑screen yields combinations like Compact‑Regular.
- Trait collection updates – Delivered to
UIViewControllerviatraitCollectionDidChange(_:). - Preferred content size – If you set
preferredContentSizeon a presented view controller, the system may ignore it when the app is in a split pane unless you also implementsizeForChildContentContainer.
Common Root Causes of Split Screen Issues
Most split‑screen bugs trace back to a few recurring assumptions. Recognizing these patterns lets you target the fix rather than chasing symptoms.
| Category | Typical Symptom | Underlying Assumption |
|---|---|---|
| Hard‑coded dimensions | Views overflow or disappear when width < 360dp | UI designed for a single minimum width (e.g., 360dp) |
| Fixed padding/margin | Buttons clipped at the split‑screen divider | Padding defined in dimens.xml without sw* qualifiers |
| Orientation‑locked layout | Landscape‑only assets appear in portrait split | Manifest android:screenOrientation="landscape" or UISupportedInterfaceOrientations |
Improper handling of onConfigurationChanged | State loss, duplicated UI fragments | Missing super call or forgetting to re‑inflate views |
| Resource qualifier misuse | Wrong drawable/layout loaded | Using layout-w600dp but forgetting layout-sw600dp for smallest width |
| Inaccessible touch targets | Touch area < 48dp after shrink | Assuming original size remains constant |
| Animation jank | UI stutters while dragging divider | Layout passes triggered on every size change without RecyclerView setItemViewCacheSize |
Layout Assumptions
Developers often lock a layout to a specific width using match_parent on a parent that itself has a fixed layout_width. In split‑screen the parent may receive a width of 400dp while the child still tries to occupy 720dp, causing overflow. The fix is to let children use 0dp weight in a LinearLayout or ConstraintLayout chains, or to use % dimensions in ConstraintLayout.
Resource Qualifier Pitfalls
Qualifiers like sw600dp (smallest width) are more reliable than w600dp (current width) because they survive rotation and divider movement. A common mistake is placing a layout in layout-w600dp expecting it to apply when the app is in the left pane of a split screen; if the device is held in portrait, the width may be 410dp but the smallest width is still 410dp, so the qualifier fails and the fallback layout is used, leading to inconsistent UI.
Lifecycle Missteps
If an activity declares android:configChanges="orientation|screenSize" but omits smallestScreenSize, a change from width = 410dp to width = 410dp with a different smallest width (e.g., due to split‑screen) will still trigger a recreation. The resulting flicker or state loss can be mistaken for a layout bug when it is actually a lifecycle issue.
Reproducing Split Screen Issues Reliably
A bug that appears only when the user resizes the divider is hard to catch with ad‑hoc testing. A repeatable reproduction matrix saves time and enables automated verification.
Manual Reproduction Steps
- Enable developer options – On Android, turn on “Force activities to be resizable” if the app targets API < 24.
- Launch split‑screen – Open recent apps, drag the app’s title bar to the top/bottom (Android) or drag an app from the dock to the side (iPadOS).
- Adjust divider – Move the separator to 30 %, 50 %, and 70 % of the screen width, pausing at each position.
- Observe UI – Look for clipped views, missing content, misaligned text, or performance spikes.
Automated Reproduction via ADB
You can script the resize using adb shell wm commands. The following Bash snippet sets the app window to a specific width and height, then restores the original bounds:
# Replace com.example.app with your package
PACKAGE=com.example.app
# Get current bounds
ORIG=$(adb shell dumpsys window windows | grep -E "mCurrentFocus|mFocusedApp" | awk '{print $NF}' | sed 's/}//')
# Set split‑screen left pane to 400dp width, full height
adb shell am start -n $PACKAGE/.MainActivity
adb shell wm size 1080x2280 # ensure base resolution
adb shell wm density 420
adb shell wm overscan 0,0,0,0
adb shell wm resize 400 2280 # width x height
# Wait for UI to settle
sleep 2
# Capture screenshot for visual diff
adb shell screencap -p /sdcard/split_left.png
adb pull /sdcard/split_left.png .
# Restore
adb shell wm size reset
adb shell wm density reset
For iPadOS you can use xcrun simctl with a resizable simulator:
xcrun simctl boot "iPad Pro (12.9‑inch) (5th generation)"
xcrun simctl openurl booted "https://example.com"
# Resize the simulated window to 1/3 width
xcrun simctl resize booted --width 400 --height 1024
# Run your UI tests here
xcrun simctl shutdown booted
Test Matrix
| Device / Emulator | OS Version | Split‑Screen Ratio | Orientation | Expected Behavior | Observed Result |
|---|---|---|---|---|---|
| Pixel 5 (API 33) | Android 13 | 30 % / 70 % | Portrait | UI scales, no clipping | Button clipped at 30 % |
| Pixel 5 (API 33) | Android 13 | 50 % / 50 % | Landscape | Two‑panel layout shows | Overlap in middle |
| iPad Air (sim) | iPadOS 17 | 33 % / 66 % | Portrait | Sidebar visible | Sidebar missing |
| iPad Pro (sim) | iPadOS 17 | 50 % / 50 % | Landscape | Equal panes | Layout jumps |
Running this matrix on every CI build (via Firebase Test Lab or a local device farm) catches regressions early.
Tools and Signals for Diagnosis
When a split‑screen defect appears, you need concrete data to pinpoint the cause. The following tools provide complementary signals: logs, metrics, and visual inspection.
Logcat (Android)
- Window metrics –
adb logcat | grep -i "WindowManager"outputsWINevents withframe=andusableFrame=rectangles. - Configuration changes – Look for
CONFIGlines showingscreenWidthDp,smallestScreenWidthDp. - Layout passes –
ViewRootImpllogsperformLayoutandperformDrawtimestamps; a spike indicates expensive layout on each divider move.
Example command to capture a timed log while dragging the divider:
adb logcat -v time | grep -E "WindowManager|ViewRootImpl|CONFIG" > split_log.txt
Systrace / Perfetto
- UI thread stalls – Enable
gfx,view, andwmtags to see if the UI thread exceeds 16 ms during a resize. - Choreographer frames – Missed frames correlate with jank felt by the user.
python systrace.py --time=10 -o split_trace.html gfx view wm
Layout Inspector (Android Studio)
- Real‑time hierarchy – Connect to a running app, rotate the device, or trigger a split‑screen via ADB, then inspect constraints, margins, and measured dimensions.
- Attribute overrides – See if a dimension is being forced by a hard‑coded
dpvalue instead of awrap_contentor0dpweight.
GPU Profiler / Overdraw
- Excess overdraw – In split‑screen, overlapping backgrounds can cause overdraw > 2x; use the GPU profiler’s overdraw visualization to spot unnecessary layers.
Accessibility Scanner
- Touch target size – When the UI shrinks, buttons may fall below the 48 dp minimum. Accessibility Scanner flags these automatically.
iOS Instruments
- Core Animation – Measures frame rates during split‑screen resizing.
- View Debugging – Shows the actual frame of each view after trait collection changes.
- Signposts – Custom
os_signpostcalls aroundviewWillTransitionlet you measure how long layout takes.
os_signpost(.begin, log: .ui, name: "SplitScreenResize")
view.setNeedsLayout()
view.layoutIfNeeded()
os_signpost(.end, log: .ui, name: "SplitScreenResize")
Visual Regression
- Pixel‑by‑pixel diff – Tools like Shotgun (Android) or Felix (iOS) compare screenshots before and after a resize, highlighting regions that changed unexpectedly.
Step‑by‑Step Diagnosis Workflow
Below is a repeatable process you can follow when a split‑screen bug is reported. Each step narrows the scope until the root cause is isolated.
1. Triage with a Symptom Checklist
| Symptom | Likely Category | Quick Check |
|---|---|---|
| View disappears at a specific width | Hard‑coded dimension | Search for fixed dp values in layouts |
| UI jumps when divider moves | Missing onConfigurationChanged handling | Verify manifest/configChanges |
| Overlap or z‑order issues | Incorrect ConstraintLayout chains or FrameLayout stacking | Inspect hierarchy in Layout Inspector |
| Jank > 16 ms per move | Expensive layout pass | Enable Systrace view tag |
| Touch target too small | Missing responsive scaling | Run Accessibility Scanner |
| Crash on resize | Null pointer after config change | Check logcat for Exception stack trace |
If more than one symptom appears, treat them as separate issues unless they share a common trigger (e.g., a missing android:configChanges).
2. Capture Baseline Metrics
- Record the app’s window size in full‑screen (
adb shell dumpsys window windows | grep mCurrentFocus). - Note the smallest width (
smallestScreenWidthDp) and orientation. - Take a screenshot for visual reference.
3. Reproduce at a Specific Ratio
Use the ADB snippet from the Reproducing section to lock the window to the width where the bug appears. This eliminates the variability of dragging the divider and lets you focus on a static state.
4. Enable Targeted Logging
Add temporary log statements in your activity/fragment:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.d("SplitDebug", "New width=${newConfig.screenWidthDp}dp, smallest=${newConfig.smallestScreenWidthDp}dp")
// Log layout parameters of root view
val params = window.decorView.rootView.layoutParams
Log.d("SplitDebug", "Root layoutParams: width=${params.width}, height=${params.height}")
}
For iOS:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
print("Will transition to size \(size)")
}
5. Inspect Layout Attributes
- Open Layout Inspector, freeze the hierarchy, and select the problematic view.
- Check its
layout_width,layout_height,margin,padding, and anyweightorchainStyle. - If the values are fixed numbers, replace them with
0dpweight or%dimensions.
6. Verify Resource Selection
- In Layout Inspector, note which
layoutfolder is being used (e.g.,layout-w600dp). - Confirm that the qualifier matches the smallest width, not the current width.
- If wrong, move the file to the appropriate
sw*folder.
7. Measure Layout Pass Cost
- Run Systrace with
viewtag for ~5 seconds while you programatically change the width via ADB (see the resize loop below). - Look for
performLayoutdurations > 2 ms per frame; if they exceed the frame budget, consider: - Switching to
ConstraintLayout(flatter hierarchy). - Using
RecyclerViewwithsetItemViewCacheSize. - Deferring expensive work to
View.post { … }.
8. Validate Touch Targets
- Run Accessibility Scanner on the resized state.
- If any view reports
<48dp, increase itsminWidth/minHeightor wrap it in aTouchableOpacity‑style container with padding.
9. Confirm Fix and Regression Test
- Return the window to full‑screen, verify the UI still works.
- Run the test matrix (see earlier) on a device farm.
- Add an automated UI test that sets the window to each ratio and asserts that key views are fully visible (using Espresso’s
isDisplayed()or XCTest’sassertTrue).
10. Document the Resolution
- Add a comment in the layout file explaining why a particular dimension is responsive.
- Update your app’s Split‑Screen Support wiki page with the new qualifier or code pattern.
Fixes for Common Causes
Now that we have a diagnosis flow, let’s look at concrete remedies for each frequent cause.
1. Replace Fixed Dimensions with Responsive Constraints
Before (XML)
<Button
android:id="@+id/confirm"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:text="Confirm"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"/>
Problem – At 30 % width (≈ 250 dp on a 1080 px screen) the button’s left margin pushes it off‑screen.
After (ConstraintLayout)
<Button
android:id="@+id/confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Confirm"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_marginStart="8dp"
app:layout_marginEnd="8dp"
app:layout_marginTop="16dp"/>
0dpwidth lets the button expand to fill the available space minus the margins.horizontal_bias="0.5"centers it; you can adjust bias for left/right alignment.
2. Use Smallest‑Width Qualifiers for Layouts
Create res/layout-sw600dp/activity_main.xml for tablets and split‑screen panes that are at least 600 dp wide. Keep the default layout/ for narrower screens.
If you need a three‑column layout only when the pane is ≥ 800 dp, add layout-sw800dp.
3. Handle Configuration Changes Properly
In the manifest, include all relevant flags:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"/>
In the activity:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Re‑apply any UI state that depends on width
updateUiForWidth(newConfig.screenWidthDp)
}
Avoid re‑inflating the entire layout unless necessary; instead, adjust specific views.
4. Guard Against Orientation‑Locked Manifests
If you locked orientation for a reason (e.g., a camera preview), consider making that restriction conditional:
<activity
android:name=".CameraActivity"
android:screenOrientation="behind"/> <!-- inherits from parent -->
Or handle the preview in a separate fragment that can be recreated without affecting the UI.
5. Fix Resource Qualifier Misplacements
Move any layout that should appear only when the *smallest* width meets a threshold into the sw* folder.
- Example: a two‑pane master/detail layout belongs in
layout-sw600dp. - Keep a single‑pane fallback in
layout/.
6. Ensure Touch Targets Remain ≥ 48 dp
Define a dimension resource:
<!-- res/values/dimens.xml -->
<dimen name="min_touch_target">48dp</dimen>
Use it as a minimum width/height:
<Button
android:id="@+id/cancel"
android:layout_width="@dimen/min_touch_target"
android:layout_height="@dimen/min_touch_target"
android:text="Cancel"/>
If the button’s content is an icon, wrap it in a FrameLayout with padding to hit the minimum size while keeping the icon centered.
7. Reduce Layout Pass Overhead
- Flatten hierarchies: replace nested
LinearLayouts with a singleConstraintLayout. - Use
ViewStubfor infrequently shown panels. - In
RecyclerView, callsetItemViewCacheSize(20)to avoid rebinding off‑screen items on every size change.
8. Address iOS Specific Issues
- Size class changes – Override
traitCollectionDidChange(_:)and update constraints usingNSLayoutConstraint.activate/deactivate. - Safe area – Always pin to
view.safeAreaLayoutGuideto avoid being obscured by the split‑screen divider or home indicator. - Dynamic type – Respect
UIContentSizeCategorychanges; if you hard‑code font sizes, they will not scale when the user prefers larger text in a narrow pane.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {
updateLayoutForWidth(traitCollection.horizontalSizeClass)
}
}
- Presentation style – If you present a view controller modally, set
modalPresentationStyle = .formSheetto allow it to adapt to split‑screen width.
Prevention Strategies and Best Practices
Fixing bugs after they appear is costly. Embedding split‑screen awareness into your development workflow reduces regressions.
1. Design with Breakpoints Early
- During UI mockups, define breakpoints at 320 dp, 480 dp, 600 dp, and 800 dp smallest width.
- Create component variants for each breakpoint and store them in a design system (e.g., using Figma’s “variants”).
2. Automated Split‑Screen Tests
Add an instrumentation test that programmatically changes the window size and asserts UI properties.
Kotlin (Espresso)
@Test fun splitScreenLayout_buttonsVisible() {
// Set window to 400dp width
val activityScenario = ActivityScenario.launch(MainActivity::class.java)
activityScenario.onActivity { activity ->
val params = activity.window.attributes
params.width = 400 * Resources.getSystem().displayDensity.toInt()
activity.window.attributes = params
}
// Verify that the primary action button is at least 50% visible
onView(withId(R.id.confirm)).check(matches(isAtLeastHalfVisible()))
}
// Custom matcher
fun isAtLeastHalfVisible(): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun matchesSafely(item: View): Boolean {
val location = IntArray(2)
item.getLocationOnScreen(location)
val viewRect = Rect(location[0], location[1],
location[0] + item.width, location[1] + item.height)
val windowRect = Rect(0, 0,
Resources.getSystem().displayMetrics.widthPixels,
Resources.getSystem().displayMetrics.heightPixels)
val intersection = Rect()
intersection.setIntersect(viewRect, windowRect)
val visibleArea = intersection.width() * intersection.height()
val totalArea = item.width * item.height.toLong()
return visibleArea >= totalArea / 2
}
override fun describeTo(description: Description) {
description.appendText("at least half of the view is visible on screen")
}
}
}
Swift (XCUITest)
func testSplitScreenButtonVisible() {
let app = XCUIApplication()
app.launch()
// Simulate iPad split-screen: set width to one‑third
let coordinate = app.windows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
let target = coordinate.withOffset(CGVector(dx: 0, dy: 0))
// Use XCUICoordinate to press and drag the divider (simplified)
// For brevity, assume we have a helper that sets the window size via Xcode’s simulator control
// XCUIDevice.shared.orientation = .landscapeLeft
// XCUIScreen.main.bounds provides the current size; we assert after resize
let button = app.buttons["Confirm"]
XCTAssertTrue(button.frame.width >= 40, "Button too narrow in split-screen")
}
Integrate these tests into your CI pipeline (GitHub Actions, Bitrise, etc.) on a device farm that supports resize (Firebase Test Lab, AWS Device Farm).
3. Lint Rules for Hard‑Coded Dimensions
Create a custom lint detector (Android) or SwiftLint rule that flags any dp or pt value greater than 24 used as a width or height without a weight or % alternative.
Example Android lint rule (pseudo‑code):
if (attribute.name == "layout_width" && attribute.value.endsWith("dp") &&
attribute.value.replace(Regex("[^0-9]"), "").toIntOrNull() ?: 0 > 24) {
report("Fixed width >24dp may break split-screen", scope)
}
4. Runtime Assertions in Debug Builds
Add a debug‑only check that logs when a view’s measured width falls below a threshold:
if (BuildConfig.DEBUG) {
view.viewTreeObserver.addOnGlobalLayoutListener {
if view.measuredWidth < 48 {
Log.w("SplitScreen", "View ${view.id} too narrow: ${view.measuredWidth}dp")
}
}
}
5. Documentation and On‑Boarding Checklist
Add a short checklist to your project’s CONTRIBUTING.md:
- [ ] Verify layout uses
0dpweight or%dimensions inConstraintLayout/LinearLayout. - [ ] Confirm all layout resources that depend on width are placed in
sw*folders. - [ ] Ensure
android:configChangesincludessmallestScreenSizeif you handle resizes manually. - [ ] Run the split‑screen UI test suite on every PR.
- [ ] Run Accessibility Scanner on a resized build.
6. Leverage Platform‑Specific Tools
- Android – Enable
androidx.window:windowlibrary to queryWindowMetricsdirectly instead of parsingConfiguration. - iOS – Use
UIScreen.main.boundsandUITraitCollectiontogether; avoid relying onUIView.frameafter a size change without callinglayoutIfNeeded.
How Autonomous Exploration Surfaces Split Screen Issues Early
Modern QA platforms can exercise an app without any test scripts, automatically discovering problems that only appear in unusual window configurations. SUSATest (the autonomous QA platform offered by SUSATest) does exactly this:
- Session‑based exploration – After installing an APK or pointing to a web URL, the agent builds a state graph of screens, gestures, and inputs.
- Persona‑driven variation – Each virtual user (curious, impatient, novice, etc.) applies its own interaction profile, which includes trying to resize windows, drag split‑screen dividers, and rotate the device.
- Automatic window‑size manipulation – The agent issues the same
wm resizecommands we scripted manually, cycling through a matrix of widths (320 dp, 480 dp, 600 dp, 800 dp) and heights while keeping the app in the foreground. - Signal collection – Logcat, GPU metrics, accessibility events, and uncaught exceptions are streamed back to the service. A spike in frame‑render time or a layout‑inspector mismatch triggers a flag.
- Early verdict – If a button becomes invisible or a crash occurs on a specific width, the platform marks the run as *FAIL* for the “Split Screen Resize” scenario and provides a reproducible script (ADB commands + UI actions) that a developer can replay locally.
Because the agent repeats the exploration on every build, regressions are caught before they reach a manual QA cycle. Moreover, the cross‑session memory means the agent remembers which widths caused a layout thrash and focuses subsequent runs on those boundaries, accelerating feedback.
Integrating SUSATest into a CI pipeline is as simple as adding a step:
pip install susatest-agent
susatest run --app-path ./app-release.apk --scenario split-screen-resize --output ./susatest-report.json
The resulting JSON contains a pass/fail flag, a list of observed violations (e.g., “View id=confirm width < 48dp at 400dp window”), and a link to a video of the failing interaction. Teams can treat this as another unit test in their gate, ensuring split‑screen robustness evolves alongside feature work.
Quick Reference Checklist
| Area | Item | How to Verify | ||||
|---|---|---|---|---|---|---|
| Manifest | android:configChanges includes `orientation | screenSize | smallestScreenSize | screenLayout` | `aapt dump xmltree | grep configChanges` |
| Layouts | No fixed dp/pt widths > 24dp without weight/% | Run custom lint or search layout_width/layout_height for \d+dp | ||||
| Resource Qualifiers | Width‑specific layouts in sw* folders | `find res -type f -name "*.xml" | grep -E "layout-sw[0-9]+"` | |||
| State | onConfigurationChanged calls super and updates UI | Set breakpoint or log in method | ||||
| Touch Targets | Minimum 48 dp after resize | Accessibility Scanner on resized build | ||||
| Performance | UI thread < 16 ms per resize | Systrace view tag; look for performLayout > 2 ms | ||||
| Visual | No clipping, overlapping, or missing content | Screenshot diff at each breakpoint (320, 480, 600, 800 dp) | ||||
| Automated | CI includes split‑screen resize test | Verify test job runs and passes on device farm | ||||
| Monitoring | Production metrics flag window‑size anomalies | Observe custom metrics (e.g., screen_width_dp histogram) for outliers |
Closing Takeaways
Split‑screen bugs are deceptive because they hide behind assumptions that work perfectly in full‑screen but break the moment the system hands the app a new window size. The most effective defense combines three habits:
- Design responsively from the start – Use
0dpweights,%dimensions, and smallest‑width qualifiers so layouts fluidly adapt to any width the system may give you. - Validate early and often – Scripted window‑size changes, automated UI tests, and autonomous explorers like SUSATest catch regressions before they reach users.
- Instrument and observe – Log window metrics, track layout pass duration, and run accessibility checks on every resize candidate. When something looks off, the data you’ve collected points directly to the offending view or lifecycle method.
By embedding these practices into your development cycle—manifest checks, lint rules, CI tests, and occasional manual spot‑checks—you turn split‑screen from a source of mysterious crashes into a routine, verified part of your app’s supported configurations. The payoff is fewer embarrassing UI glitches in the wild, happier power users who rely on multitasking, and a QA process that spends less time chasing layout ghosts and more time delivering new features.
---
*Feel free to copy the checklist, test snippets, and lint rules into your own repositories. With them in place, your app will stay solid whether the user is watching a video, replying to a message, or juggling two apps side‑by‑side.*
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free