formatNativeTapResult function

int formatNativeTapResult(
  1. NativeTapResult result
)

Writes the stdout tokens / stderr lines for result and returns the exit code.

Implementation

int formatNativeTapResult(NativeTapResult result) {
  switch (result) {
    case NativeTapAndroid(:final x, :final y, :final text):
      final textSuffix = text != null ? ' TEXT="${_escapeQuoted(text)}"' : '';
      stdout.writeln('NATIVE_TAPPED=android X=$x Y=$y$textSuffix');
      return 0;
    case NativeTapIosSimulator(:final x, :final y, :final text):
      final textSuffix = text != null ? ' TEXT="${_escapeQuoted(text)}"' : '';
      stdout.writeln('NATIVE_TAPPED=ios-simulator X=$x Y=$y$textSuffix');
      return 0;
    case NativeTapIosSimulatorFallback(:final x, :final y, :final reason, :final tapResult):
      stderr.writeln(
        'WARNING: iOS simulator HID tap unavailable ($reason); fell back to in-process tap '
        '(UIApplication.sendEvent), which cannot reach SpringBoard system dialogs.',
      );
      final tapExitCode = formatTapResult(tapResult);
      if (tapExitCode != 0) return tapExitCode;
      stdout.writeln('NATIVE_TAPPED=ios-simulator X=$x Y=$y');
      return 0;
    case NativeTapIosSimulatorOutOfBounds(:final message):
      stderr.writeln('ERROR: $message');
      return 1;
    case NativeTapIosSimulatorOrientationUnknown(:final message):
      stderr.writeln('ERROR: $message');
      return 1;
    case NativeTapIosSimulatorFailed(:final message):
      stderr.writeln('ERROR: $message');
      return 1;
    case NativeTapIosSimulatorAccessibilityUnavailable(:final reason):
      stderr.writeln(
        'ERROR: native-tap --text needs the iOS simulator accessibility API, which is not available ($reason). '
        'Tap by coordinates with --at x,y instead.',
      );
      return 1;
    case NativeTapIosSimulatorTreeUnreadable(:final reason):
      stderr.writeln('ERROR: native-tap --text could not read the iOS simulator accessibility tree ($reason).');
      return 1;
    case NativeTapIosSimulatorTapUnavailable(:final reason):
      stderr.writeln('ERROR: native-tap --text could not tap on the iOS simulator ($reason).');
      return 1;
    case NativeTapIosSimulatorFramesUnmapped(:final query, :final orientation, :final labels):
      stderr.writeln(
        'ERROR: native-tap --text could not place the native elements on the iOS simulator screen '
        '($orientation), so "$query" can\'t be tapped by label. Rotate the simulator to portrait, or tap with '
        '--at x,y. Labels: ${_labelList(labels)}',
      );
      return 1;
    case NativeTapNoSession():
      stderr.writeln('ERROR: No active fdb session found. Run fdb launch first.');
      return 1;
    case NativeTapPhysicalIosUnsupported(:final x, :final y):
      final at = _atArg(x, y);
      stderr.writeln(
        'ERROR: native-tap is not yet supported on physical iOS devices.\n'
        '  Use `fdb tap --at $at` instead — it performs in-process tap\n'
        '  injection via fdb_helper, which reaches UIAlertController and other\n'
        '  in-app native overlays on physical iOS devices.\n'
        '\n'
        '  Why: out-of-process tap injection on physical iOS requires\n'
        '  WebDriverAgent (a signed XCUITest runner installed on the device).\n'
        '  Tracking implementation in beads issue fdb-6sz.',
      );
      return 1;
    case NativeTapMacosUnsupported(:final x, :final y):
      final instead = x != null && y != null ? 'fdb tap --at ${_atArg(x, y)}' : 'fdb tap --text <label>';
      stderr.writeln(
        'ERROR: native-tap is not supported on macOS.\n'
        '  Use `$instead` instead — it performs in-process tap injection\n'
        '  via fdb_helper and does not require Accessibility permission.\n'
        '\n'
        '  Why: cross-process tap injection on macOS requires Accessibility\n'
        '  permission, which is only grantable to signed .app bundles. Homebrew\n'
        '  CLIs are unsigned and cannot be added to the Accessibility list.',
      );
      return 1;
    case NativeTapPlatformUnsupported(:final platform):
      stderr.writeln('ERROR: native-tap is not supported on platform "$platform".');
      return 1;
    case NativeTapAdbFailed(:final details):
      stderr.writeln('ERROR: adb input tap failed: $details');
      return 1;
    case NativeTapInputInjectionBlocked(:final details):
      stderr.writeln(
        'ERROR: Android blocked input injection (INJECT_EVENTS). Enable it in Developer options: '
        'Xiaomi/HyperOS "USB debugging (Security settings)", OPPO/OnePlus/Realme "Disable permission monitoring".\n'
        '  adb said: ${_firstLine(details)}',
      );
      return 1;
    case NativeTapNoMatch(:final query, :final visibleLabels):
      stderr.writeln('ERROR: No native element matching "$query". Visible labels: ${_labelList(visibleLabels)}');
      return 1;
    case NativeTapAmbiguous(:final query, :final candidates):
      final lines = [
        for (var i = 0; i < candidates.length; i++)
          '  [$i] "${candidates[i].label}" at ${candidates[i].x},${candidates[i].y}',
      ];
      stderr.writeln(
        'ERROR: Found ${candidates.length} native elements matching "$query". '
        'Use --index to specify which one (0-based):\n'
        '${lines.join('\n')}',
      );
      return 1;
    case NativeTapIndexOutOfRange(:final query, :final index, :final count):
      stderr.writeln(
        'ERROR: --index $index is out of range: found $count native element${count == 1 ? '' : 's'} '
        'matching "$query" (0-based).',
      );
      return 1;
    case NativeTapUiDumpFailed(:final reason):
      final idle = reason.contains('idle state');
      stderr.writeln(
        'ERROR: Could not read the Android UI hierarchy (uiautomator dump): $reason'
        '${idle ? '\n  uiautomator needs the screen to stop animating. Retry, or tap by coordinates with --at x,y.' : ''}',
      );
      return 1;
    case NativeTapDevicePixelRatioUnavailable(:final reason):
      stderr.writeln('ERROR: Could not determine the device pixel ratio for --logical: $reason');
      return 1;
    case NativeTapAdbExecutionFailed(:final error):
      stderr.writeln(
        'ERROR: Failed to run adb: $error\n'
        '  Install adb: https://developer.android.com/studio/command-line/adb',
      );
      return 1;
  }
}