addFeature method

OperationResult addFeature(
  1. String featureName, {
  2. bool required = true,
})

Adds a uses-feature tag to the AndroidManifest.xml file.

Implementation

OperationResult addFeature(String featureName, {bool required = true}) {
  final manifestFile = File(_manifestPath);

  if (!manifestFile.existsSync()) {
    return const Failure(
      'Could not find AndroidManifest.xml. '
      'Are you in a Flutter project root directory?',
    );
  }

  final fullFeature = featureName.contains('.')
      ? featureName
      : 'android.hardware.$featureName';

  try {
    final content = manifestFile.readAsStringSync();
    final document = XmlDocument.parse(content);

    final manifest = document.rootElement;

    if (manifest.name.local != 'manifest') {
      return const Failure(
        'Invalid AndroidManifest.xml: root element is not <manifest>.',
      );
    }

    const androidNs = 'http://schemas.android.com/apk/res/android';
    final existingFeatures = manifest.findAllElements('uses-feature');

    for (final feature in existingFeatures) {
      final nameAttr = feature.getAttribute('name', namespace: androidNs) ??
          feature.getAttribute('android:name');
      if (nameAttr == fullFeature) {
        return Success('Feature $fullFeature already exists.');
      }
    }

    final featureElement = XmlElement(
      XmlName('uses-feature'),
      [
        XmlAttribute(XmlName('android:name'), fullFeature),
        XmlAttribute(XmlName('android:required'), required.toString()),
      ],
    );

    final children = manifest.children.toList();
    int insertIndex = 0;

    for (int i = 0; i < children.length; i++) {
      final child = children[i];
      if (child is XmlElement) {
        if (child.name.local == 'uses-permission' ||
            child.name.local == 'uses-feature') {
          insertIndex = i + 1;
        }
      }
    }

    if (insertIndex == 0) {
      for (int i = 0; i < children.length; i++) {
        if (children[i] is XmlElement &&
            (children[i] as XmlElement).name.local == 'application') {
          insertIndex = i;
          break;
        }
      }
    }

    manifest.children.insert(insertIndex, XmlText('\n    '));
    manifest.children.insert(insertIndex + 1, featureElement);

    final output = document.toXmlString(pretty: true, indent: '    ');
    manifestFile.writeAsStringSync(output);

    return Success(
        'Successfully added feature: $fullFeature (required: $required)');
  } on XmlParserException catch (e) {
    return Failure('Failed to parse AndroidManifest.xml: ${e.message}');
  } on FileSystemException catch (e) {
    return Failure('Failed to update AndroidManifest.xml: ${e.message}');
  }
}