updateDarkStylesXml function

Future<void> updateDarkStylesXml({
  1. YamlMap? android12AndAbove,
  2. String? darkColor,
  3. String? darkImage,
  4. String? darkBrandingImage,
  5. String? darkBackgroundImageSource,
})

Generates dark mode splash screen XML styles for both pre-Android 12 and Android 12+ devices.

This function manages two separate dark mode configurations to support platform-specific styling requirements. It creates values-night/styles.xml for pre-Android 12 devices and values-night-v31/styles.xml for Android 12+ devices with their respective styling.

Implementation

Future<void> updateDarkStylesXml({
  YamlMap? android12AndAbove,
  String? darkColor,
  String? darkImage,
  String? darkBrandingImage,
  String? darkBackgroundImageSource,
}) async {
  // Pre-Android 12 dark styles live in values-night/styles.xml, separate from values-night-v31.
  final hasPreAndroid12DarkConfig = darkImage != null ||
      darkBackgroundImageSource != null ||
      darkColor != null;

  if (!hasPreAndroid12DarkConfig && android12AndAbove == null) {
    // No dark configuration provided. Ensure any previously generated
    // dark-style files are removed so they don't reference missing drawables.
    try {
      const preAndroid12Path =
          '${CmdStrings.androidDarkValuesDirectory}/${AndroidStrings.stylesXml}';
      final preFile = File(preAndroid12Path);
      if (await preFile.exists()) {
        await preFile.delete();
        log('Removed $preAndroid12Path because no dark config provided.');
      }

      const v31Path =
          '${CmdStrings.androidDarkValuesV31Directory}/${AndroidStrings.stylesXml}';
      final v31File = File(v31Path);
      if (await v31File.exists()) {
        await v31File.delete();
        log('Removed $v31Path because no dark config provided.');
      }
    } catch (e) {
      log('Error removing old dark styles: $e');
    }

    log('Skipping dark styles update: no dark configuration provided.');
    return;
  }

  // Handle Android 12+ dark styles
  if (android12AndAbove != null) {
    final android12NativeDarkImage =
        android12AndAbove[YamlKeys.imageDarkKey] as String?;

    const v31 = CmdStrings.androidDarkValuesV31Directory;
    await Directory(v31).create(recursive: true);

    const styleV31Path = '$v31/${AndroidStrings.stylesXml}';
    final styleFileV31 = File(styleV31Path);
    if (await styleFileV31.exists()) {
      await styleFileV31.delete();
    }

    await createAndroid12Styles(
      styleFile: styleFileV31,
      color: android12AndAbove[YamlKeys.colorDarkKey] as String?,
      imageSource: android12NativeDarkImage ??
          android12AndAbove[YamlKeys.imageKey] as String?,
      brandingImageSource: darkBrandingImage ??
          android12AndAbove[YamlKeys.brandingImageKey] as String?,
      isDarkBranding: darkBrandingImage != null,
      isDarkImage: android12NativeDarkImage != null,
    );
  }

  // Handle pre-Android 12 dark styles.
  // If no pre-Android 12 dark configuration is provided, ensure any
  // existing `values-night/styles.xml` doesn't reference the missing
  // dark drawable. Prefer updating it to reference the light drawable so
  // builds don't fail.
  if (!hasPreAndroid12DarkConfig) {
    const androidValuesFolder = CmdStrings.androidDarkValuesDirectory;
    const xmlPath = '$androidValuesFolder/${AndroidStrings.stylesXml}';
    final xmlFile = File(xmlPath);

    if (!await xmlFile.exists()) {
      log('Pre-Android 12 values-night/styles.xml not found. Skipping update.');
      return;
    }

    try {
      final xmlDoc = XmlDocument.parse(xmlFile.readAsStringSync());
      for (final itemElement
          in xmlDoc.findAllElements(AndroidStrings.itemElement)) {
        if (itemElement.getAttribute(AndroidStrings.nameAttr) ==
            AndroidStrings.itemNameAttrValue) {
          // Point night styles to the light splash drawable instead of the
          // missing dark drawable so resource linking succeeds.
          itemElement.innerText = AndroidStrings.drawableSplashScreen;
        }
      }
      await xmlFile.writeAsString(xmlDoc.toXmlString(pretty: true));
      log('Pre-Android 12 values-night/styles.xml updated to reference light drawable.');
    } catch (e) {
      log('Error updating values-night/styles.xml: $e');
    }
    return;
  }
  // If we have pre-Android 12 dark configuration, create or update
  // values-night/styles.xml to reference the dark drawable.
  else {
    // Read parent style from existing light styles.xml so we don't hardcode it
    final lightStylesFile = File(
        '${CmdStrings.androidValuesDirectory}/${AndroidStrings.stylesXml}');
    final lightDoc = XmlDocument.parse(lightStylesFile.readAsStringSync());
    final parentAttr = lightDoc
            .findAllElements(AndroidStrings.styleElement)
            .firstWhere((e) =>
                e.getAttribute(AndroidStrings.nameAttr) ==
                AndroidStrings.styleNameAttrVal)
            .getAttribute(AndroidStrings.styleParentAttr) ??
        '';

    const nightDir = CmdStrings.androidDarkValuesDirectory;
    await Directory(nightDir).create(recursive: true);
    final nightFile = File('$nightDir/${AndroidStrings.stylesXml}');
    if (await nightFile.exists()) await nightFile.delete();

    final builder = XmlBuilder();
    builder.processing(AndroidStrings.xml, AndroidStrings.xmlVersion);
    builder.element(AndroidStrings.resourcesElement, nest: () {
      builder.element(AndroidStrings.styleElement, attributes: {
        AndroidStrings.nameAttr: AndroidStrings.styleNameAttrVal,
        AndroidStrings.styleParentAttr: parentAttr,
      }, nest: () {
        builder.element(
          AndroidStrings.itemElement,
          attributes: {
            AndroidStrings.nameAttr: AndroidStrings.itemNameAttrValue
          },
          nest: AndroidStrings.androidDarkDrawable,
        );
      });
    });
    await nightFile
        .writeAsString(builder.buildDocument().toXmlString(pretty: true));
    log('Created values-night/styles.xml referencing dark drawable.');
  }
}