> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userpilot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Auto Capture

> Configure auto capture for the Userpilot MAUI SDK

Configure automatic screen and interaction capture for .NET MAUI applications using the Userpilot MAUI SDK.

The Userpilot MAUI SDK can automatically capture screen views, user interactions, and selected presentation events without adding explicit tracking calls to each view. Auto Capture runs in the MAUI wrapper and forwards captured events to the native Userpilot SDK on Android and iOS. No source generator or navigator observer is required — after setup, the SDK attaches to the current MAUI windows and listens to navigation and supported control events.

***

## SDK Configuration

Auto capture is **off by default**. Pass `UserpilotOptions` to `UserpilotSdk.Setup(...)`. The same options object contains the platform SDK options and the MAUI auto capture options.

```csharp theme={null}
using Userpilot;

var options = new UserpilotOptions
{
    Logging = true,
    UseInAppBrowser = false, // Android only
    DisableRequestPushNotificationsPermission = false,

    EnableScreenAutoCapture = true,
    EnableInteractionAutoCapture = true,
    EnableInteractionAccessibilityLabelCapture = true,
    EnableInteractionTextCapture = true,
    EnableInteractionValueCapture = false,
    MaxHierarchyDepth = 25,
};

#if ANDROID
var initialized = UserpilotSdk.Setup(
    Android.App.Application.Context,
    "<APP_TOKEN>",
    options);
#elif IOS
var initialized = UserpilotSdk.Setup(
    "<APP_TOKEN>",
    options);
#endif
```

| **Option**                                   | **Type** | **Default** | **Description**                                                                                                                                                             |
| -------------------------------------------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EnableScreenAutoCapture`                    | `bool`   | `false`     | Enables automatic screen tracking from MAUI navigation and page lifecycle events.                                                                                           |
| `EnableInteractionAutoCapture`               | `bool`   | `false`     | Enables automatic interaction capture: taps, text changes, value changes, selections, and presentation events.                                                              |
| `EnableInteractionTextCapture`               | `bool`   | `true`      | Captures visible labels, placeholders, and titles such as button text, picker titles, page titles, and presentation titles. Typed input values are never captured verbatim. |
| `EnableInteractionAccessibilityLabelCapture` | `bool`   | `true`      | Captures accessibility metadata: label, hint, identifier, and accessibility value. Does not control `view_tag`.                                                             |
| `EnableInteractionValueCapture`              | `bool`   | `false`     | Captures user-input values such as toggle state, slider/stepper values, picked date/time, and selected dropdown or collection values. Off by default for privacy.           |
| `MaxHierarchyDepth`                          | `int`    | `25`        | Maximum number of ancestor elements included in the interaction hierarchy. Clamped between `1` and `100`.                                                                   |
| `Logging`                                    | `bool`   | `false`     | Enables native SDK debug logging.                                                                                                                                           |
| `UseInAppBrowser`                            | `bool`   | `false`     | Android only. Opens links inside the in-app browser/custom tabs flow.                                                                                                       |
| `DisableRequestPushNotificationsPermission`  | `bool`   | `false`     | Disables the SDK's automatic push notification permission request.                                                                                                          |

Call setup once after the app has created a MAUI application/window — for example from a startup, splash, or initialize screen. If setup runs before any window is available, call `UserpilotAutoCapture.ResumeAutoCapture()` after the first window/page is available so the wrapper can attach to the current windows.

<Tip>
  **Note**

  Auto Capture automatically tracks screen views and ignores manually sent `screen` events while enabled.

  If you are migrating from manual screen tracking, please review the migration guide [here](https://docs.userpilot.com/data-events/mobile-screen-tracking/mobile-screen-auto-capture#important).
</Tip>

***

## Screen Capture

When `EnableScreenAutoCapture` is `true`, the SDK emits `$screen` for MAUI navigation and page lifecycle changes:

| **Source**                            | **Captured as**                |
| ------------------------------------- | ------------------------------ |
| `Shell.Navigated`                     | `$screen`                      |
| `NavigationPage` push/pop/pop-to-root | `$screen`                      |
| `Page.Appearing` / `Page.NavigatedTo` | `$screen`                      |
| `PushModalAsync(...)` modal page      | `$screen` and `view_presented` |

Screen titles are resolved in this order:

1. Current page `Title`
2. Current Shell route/location
3. Current page type name

Duplicate screen emissions for the same page fingerprint are throttled for a short window.

<Tip>
  **Manual screens**

  `UserpilotSdk.Screen("<title>")` is ignored while screen auto capture is enabled. To send screens manually, leave `EnableScreenAutoCapture = false`.
</Tip>

***

## Interaction Capture

When `EnableInteractionAutoCapture` is `true`, the SDK captures the following MAUI interactions:

| **Interaction type**    | **Emitted for**                                                                                                                    |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `tap`                   | `Button`, `ImageButton`, `SearchBar.SearchButtonPressed`, and any non-authoritative `View` with an existing `TapGestureRecognizer` |
| `checkbox_selected`     | `CheckBox.CheckedChanged`                                                                                                          |
| `switch_changed`        | `Switch.Toggled`                                                                                                                   |
| `radio_button_selected` | `RadioButton.CheckedChanged` when selected                                                                                         |
| `text_field_changed`    | `Entry`, `Editor`, `SearchBar` text changes — debounced; typed text is never captured                                              |
| `spinner_selected`      | `Picker.SelectedIndexChanged`                                                                                                      |
| `slider_changed`        | `Slider.ValueChanged` — debounced                                                                                                  |
| `stepper_changed`       | `Stepper.ValueChanged` — debounced                                                                                                 |
| `date_picker_changed`   | `DatePicker.DateSelected`                                                                                                          |
| `time_picker_changed`   | `TimePicker.Time` changes                                                                                                          |
| `list_item_selected`    | `CollectionView.SelectionChanged`, `ListView.ItemTapped`                                                                           |
| `page_selected`         | `CarouselView.PositionChanged`                                                                                                     |
| `tab_selected`          | `TabbedPage.CurrentPageChanged`                                                                                                    |
| `menu_item_selected`    | `MenuFlyoutItem.Clicked`                                                                                                           |
| `view_presented`        | Modal pages and visible custom presentation surfaces                                                                               |

Each interaction maps to a high-level analytics category (`tap`, `text_change`, `selection_change`, `value_change`, `view_presented`) — that is the event name that reaches your analytics backend.

Text fields are debounced and never send typed text. Text-field payloads contain `has_text`, `text_length`, `read_only`, and `obscured`. Continuous controls such as `Slider` and `Stepper` are debounced so drag/update bursts send one settled event.

Every debounced interaction — text changes plus `Slider`/`Stepper` values — is flushed before a screen transition and before its control or page is torn down, so it is emitted ahead of the new screen event and keeps the previous screen in its hierarchy. Payloads are snapshotted at interaction time, so a flush that runs after the page has left its parent still reports the hierarchy the user actually interacted with.

### Tab Selection

`tab_selected` uses the shared selection fields, so a tab switch is described like any other selection:

| **Field**        | **Value**                                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `selected_index` | Zero-based index of the selected tab within `TabbedPage.Children`                                             |
| `entry_count`    | Number of tabs                                                                                                |
| `target_text`    | `Title` of the newly selected child page, when text capture is enabled. `RedactText` replaces it with `****`. |
| `selected_value` | Same title, when value capture is enabled                                                                     |

`DatePicker` and `TimePicker` are driven entirely by the MAUI change event, so their capture timing follows the platform. On Android the dialog commits the value synchronously when **OK** is tapped, so the event always lands. On iOS the picker is a wheel (`UIDatePickerStyle.Wheels`) and MAUI commits `Date`/`Time` from `UIDatePicker.ValueChanged`, which UIKit raises only once the wheel settles — see [Limitations](#limitations).

### List Selection

`CollectionView` and `ListView` both emit `list_item_selected` with the shared selection fields, so one dashboard rule matches either control. `target_class` distinguishes them (`CollectionView` / `ListView`).

| **Field**                                            | **Value**                                                                                    |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `selected_index`                                     | Zero-based position of the row in `ItemsSource`. Omitted when the item cannot be located.    |
| `entry_count`                                        | Number of rows in `ItemsSource`                                                              |
| `target_text`                                        | The item's `ToString()`, when text capture is enabled. `RedactText` replaces it with `****`. |
| `selected_value`                                     | The same string, when value capture is enabled                                               |
| `values`                                             | `CollectionView` only — every selected item, when value capture is enabled                   |
| `multi_selection_enabled`, `empty_selection_allowed` | `CollectionView` only                                                                        |

Both text fields come from the item's own `ToString()`, so give list items a meaningful override. Two gates control this:

* `target_text` needs `EnableInteractionTextCapture` (on by default) and honours `RedactText`.
* `selected_value` needs `EnableInteractionValueCapture` (**off** by default) and is **not** affected by `RedactText`.

On a list holding sensitive items, `RedactText` alone is not enough once value capture is on. Use `IgnoreInteractions="True"` to drop the event entirely, or leave `EnableInteractionValueCapture` off.

**`ListView` is deprecated by MAUI** (obsolete from `Microsoft.Maui.Controls` 10) and is captured for apps ported from Xamarin.Forms. Two differences from `CollectionView`:

* Capture is driven by `ItemTapped`, so it works with `SelectionMode="None"` — the pattern where a row only navigates — and a programmatic `SelectedItem` assignment is **not** captured. `CollectionView` uses `SelectionChanged`, which does report programmatic selection.
* Re-tapping the already-selected row emits another event, because it is another tap. `CollectionView` emits nothing, because the selection did not change.

With `IsGroupingEnabled="True"`, `selected_index` and `entry_count` are resolved across the flattened rows, so they describe the row's position among all rows rather than MAUI's rendered row index, which counts group headers.

### Component Value Gating

By default, user-input values are stripped from event payloads. Position and configuration metadata such as `selected_index`, `entry_count`, `min`, and `max` can still be sent because they describe control structure, not the selected value.

Set `EnableInteractionValueCapture = true` to include value fields:

| **Control**                                                                                  | **Value fields when enabled** |
| -------------------------------------------------------------------------------------------- | ----------------------------- |
| `CheckBox`, `Switch`                                                                         | `is_checked`                  |
| `RadioButton`, `Picker`, `Slider`, `Stepper`, `CarouselView`, `TabbedPage`, `MenuFlyoutItem` | `selected_value`              |
| `DatePicker`                                                                                 | `selected_date`               |
| `TimePicker`                                                                                 | `selected_time`               |
| `CollectionView`                                                                             | `selected_value`, `values`    |
| `ListView`                                                                                   | `selected_value`              |

Text input content is not captured even when value capture is enabled.

### Presentation Capture

Modal pages pushed with `Navigation.PushModalAsync(...)` emit `view_presented` with:

| **Field**                      | **Value**                                                   |
| ------------------------------ | ----------------------------------------------------------- |
| `presentation_surface`         | `ModalPage`                                                 |
| `target_text` / `dialog_title` | Page `Title` or page type name when text capture is enabled |

For custom overlays such as bottom sheets, mark the overlay view with `PresentationSurface`. The SDK emits `view_presented` when the marked view changes from hidden to visible:

```xml theme={null}
xmlns:up="clr-namespace:Userpilot;assembly=Userpilot.Maui"

<Grid
    x:Name="BottomSheetOverlay"
    IsVisible="False"
    up:UserpilotAutoCapture.PresentationSurface="BottomSheet"
    up:UserpilotAutoCapture.PresentationTitle="MAUI bottom sheet">
    <!-- sheet content -->
</Grid>
```

`PresentationTitle` is used for `target_text` and either `dialog_title` or `bottom_sheet_title` when text capture is enabled.

***

## Privacy Controls

Global flags govern what is captured:

* **Text capture** — `EnableInteractionTextCapture`
* **Accessibility label capture** — `EnableInteractionAccessibilityLabelCapture`
* **Value payload capture** — `EnableInteractionValueCapture`

Disabling text capture removes text-derived properties from the payload. Redaction replaces `target_text` with `****`; accessibility fields, hierarchy attributes, placeholders, and captured values are not masked by redaction.

Auto capture never sends typed input values verbatim — only field metadata such as `has_text` and `text_length`.

Add the Userpilot namespace to XAML before using attached properties:

```xml theme={null}
xmlns:up="clr-namespace:Userpilot;assembly=Userpilot.Maui"
```

### `UserpilotAutoCapture.RedactText`

Redaction replaces the captured `target_text` with `****`. The interaction event still fires — the control is still identified by type, position, and structure — only the human-readable text is masked.

```xml theme={null}
<VerticalStackLayout up:UserpilotAutoCapture.RedactText="True">
    <Button
        Text="Account 4242 4242 4242 4242"
        AutomationId="account.pay"
        SemanticProperties.Description="Sensitive account button" />
</VerticalStackLayout>
```

Use `RedactText="False"` on a descendant to override a redacted ancestor:

```xml theme={null}
<Button
    up:UserpilotAutoCapture.RedactText="False"
    Text="Public action" />
```

### `UserpilotAutoCapture.IgnoreInteractions`

Ignoring skips **interaction** events for a subtree. The app's own handlers still run — only auto capture ignores those interactions. Route/page-driven **screen** events are **not** suppressed.

```xml theme={null}
<VerticalStackLayout up:UserpilotAutoCapture.IgnoreInteractions="True">
    <Button
        Text="Internal action"
        AutomationId="internal.action"
        Clicked="OnInternalActionClicked" />
</VerticalStackLayout>
```

Use `IgnoreInteractions="False"` on a descendant to override an ignored ancestor.

### View Tag and Accessibility Value

Enrich events without changing visible UI:

```xml theme={null}
<SearchBar
    AutomationId="users.search"
    Placeholder="Search users"
    up:UserpilotAutoCapture.ViewTag="users.search.global" />

<Slider
    x:Name="SatisfactionSlider"
    Minimum="0"
    Maximum="100"
    up:UserpilotAutoCapture.AccessibilityValue="30 percent" />
```

```csharp theme={null}
UserpilotAutoCapture.SetAccessibilityValue(
    SatisfactionSlider,
    $"{Math.Round(SatisfactionSlider.Value)} percent");
```

`ViewTag` is always included when set and is not gated by accessibility capture.

***

## Runtime Controls

Auto capture starts automatically from `Setup` (based on options). To pause or resume collection at runtime without re-passing the config — for example a "private mode" — use:

```csharp theme={null}
UserpilotAutoCapture.PauseAutoCapture();
UserpilotAutoCapture.ResumeAutoCapture();

var isRunning = UserpilotAutoCapture.IsRunning;
```

`StopAutoCapture()` and `Stop()` are aliases for pause. `ResumeAutoCapture()` resumes using the options supplied during setup.

While paused, no screen or interaction events are recorded. Pausing auto capture does not enable manual `Screen()` calls: when `EnableScreenAutoCapture` is configured, manual and automatic screen tracking remain mutually exclusive.

Privacy markers only affect Userpilot auto capture. They do not disable MAUI controls, commands, gestures, or app event handlers.

***

## Limitations

* **Shell bottom tabs** — Captured as `$screen` navigation, not as `tab_selected`. `TabbedPage` emits `tab_selected`.
* **Native `DisplayAlert`, `DisplayActionSheet`, `DisplayPromptAsync`** — Not captured as `view_presented`. The button that opened them is captured.
* **Bottom sheets** — MAUI has no built-in bottom sheet. Mark custom sheet overlays with `PresentationSurface`.
* **`ToolbarItem`, `MenuItem`, context menus, `SwipeView` actions** — Not captured, except `MenuFlyoutItem`.
* **Scroll gestures, pull-to-refresh, pan, pinch, swipe, pointer, drag** — Not captured.
* **`WebView`, `Map`, `GraphicsView`, Skia/custom drawn surfaces** — Not captured internally.
* **Programmatic value changes** — The SDK listens to MAUI events. Text changes are focus-gated; other control change events may be captured if the control raises its normal change event.
* **iOS `DatePicker` / `TimePicker` dismissed before the wheel settles** — Not captured. UIKit raises `UIDatePicker.ValueChanged` only after the wheel settles, and MAUI commits `Date`/`Time` from that event (`UpdateMode.Immediately`, the default) or from the **Done** button, both of which read the picker's already-settled value. Tapping **Done** — or dismissing the picker — before the wheel settles leaves the property unchanged, so MAUI raises no change event and the host app keeps the old value too. Android's dialog commits synchronously on **OK**, so the event always lands there.

<Frame>
  [**For any questions or concerns please reach out to support@userpilot.com**](mailto:support@userpilot.com)
</Frame>
