appBuildGradleEditFunc method

void appBuildGradleEditFunc()

Implementation

void appBuildGradleEditFunc() {
  try {
    // Check for both Groovy and Kotlin DSL build files
    final groovyPath = "${Directory.current.path}/android/app/build.gradle";
    final kotlinPath =
        "${Directory.current.path}/android/app/build.gradle.kts";

    File? file;
    bool isKotlinDsl = false;

    if (File(kotlinPath).existsSync()) {
      file = File(kotlinPath);
      isKotlinDsl = true;
    } else if (File(groovyPath).existsSync()) {
      file = File(groovyPath);
      isKotlinDsl = false;
    } else {
      'Error: Neither build.gradle nor build.gradle.kts found.'
          .printWithColor(status: PrintType.error);
      return;
    }

    List<String> lines = file.readAsLinesSync();

    // Find the plugin declaration line
    final int applyFormIndex = isKotlinDsl
        ? lines.indexWhere((line) =>
            line.contains('id("dev.flutter.flutter-gradle-plugin")'))
        : lines.indexWhere((line) =>
            line.contains('id "dev.flutter.flutter-gradle-plugin"'));

    if (applyFormIndex != -1) {
      // Different code for Kotlin DSL vs Groovy
      final additionalCode = isKotlinDsl ? '''

import java.util.Base64
import java.util.Properties
import java.io.FileInputStream

// Load key.properties for signing configuration
val keystorePropertiesFile = rootProject.file("key.properties")
val keystoreProperties = Properties()
if (keystorePropertiesFile.exists()) {
  keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}

// Dart environment variables setup
val dartEnvironmentVariables = mutableMapOf<String, String>()

if (project.hasProperty("dart-defines")) {
  val dartDefines = project.property("dart-defines") as String
  dartDefines.split(",").forEach { entry ->
      val pair = String(Base64.getDecoder().decode(entry)).split("=")
      if (pair.size == 2) {
          dartEnvironmentVariables[pair[0]] = pair[1]
          if (pair[0] == "flavorType") {
              project.extra["APP_FLAVOR"] = pair[1]
          }
      }
  }
}

fun getAppFlavor(): String {
  return if (project.extra.has("APP_FLAVOR")) {
      "\${project.extra["APP_FLAVOR"]}_"
  } else {
      ""
  }
}
''' : '''

// Load key.properties for signing configuration
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
if (keystorePropertiesFile.exists()) {
  keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

def dartEnvironmentVariables = [
  APP_FLAVOR: project.hasProperty('flavorType')
]

if (project.hasProperty('dart-defines')) {
  dartEnvironmentVariables = dartEnvironmentVariables +
      project.property('dart-defines')
          .split(',')
          .collectEntries { entry ->
              def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
              if (pair.first() == 'flavorType') {
                project.ext.APP_FLAVOR = pair.last()
              }
              [(pair.first()): pair.last()]
          }
}

def appFlavor() {
if (project.hasProperty('APP_FLAVOR')) {
  return "\${project.ext.APP_FLAVOR}_"
}
}
''';

      // Insert after the plugins block (after closing brace)
      int insertIndex = applyFormIndex;
      // Find the closing brace of plugins block
      for (int i = applyFormIndex; i < lines.length; i++) {
        if (lines[i].trim() == '}') {
          insertIndex = i;
          break;
        }
      }

      lines.insert(insertIndex + 1, additionalCode);
      file.writeAsStringSync(lines.join('\n'));

      // Reload lines after first modification
      lines = file.readAsLinesSync();

      // Find defaultConfig closing brace to add signingConfigs
      int defaultConfigEnd = -1;

      for (int i = 0; i < lines.length; i++) {
        if (lines[i].contains('defaultConfig')) {
          int braceCount = 0;
          for (int j = i; j < lines.length; j++) {
            if (lines[j].contains('{')) braceCount++;
            if (lines[j].contains('}')) {
              braceCount--;
              if (braceCount == 0) {
                defaultConfigEnd = j;
                break;
              }
            }
          }
          break;
        }
      }

      if (defaultConfigEnd != -1) {
        final signingConfigCode = isKotlinDsl ? '''

  signingConfigs {
      create("release") {
          keyAlias = keystoreProperties["keyAlias"] as String?
          keyPassword = keystoreProperties["keyPassword"] as String?
          storeFile = keystoreProperties["storeFile"]?.let { file(it) }
          storePassword = keystoreProperties["storePassword"] as String?
      }
  }
''' : '''

  signingConfigs {
      release {
          keyAlias keystoreProperties['keyAlias']
          keyPassword keystoreProperties['keyPassword']
          storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
          storePassword keystoreProperties['storePassword']
      }
  }
''';

        lines.insert(defaultConfigEnd + 1, signingConfigCode);
        file.writeAsStringSync(lines.join('\n'));
        lines = file.readAsLinesSync();
      }

      // Find and replace signingConfig line in buildTypes
      int signingConfigIndex = isKotlinDsl
          ? lines.indexWhere((line) =>
              line.contains('signingConfig') &&
              line.contains('=') &&
              line.contains('debug'))
          : lines.indexWhere((line) =>
              line.contains('signingConfig') && line.contains('debug'));

      if (signingConfigIndex != -1) {
        final newSigningConfig = isKotlinDsl
            ? '''            // Use release signing config if key.properties exists, otherwise use debug
          signingConfig = if (keystorePropertiesFile.exists()) {
              signingConfigs.getByName("release")
          } else {
              signingConfigs.getByName("debug")
          }'''
            : '''            // Use release signing config if key.properties exists, otherwise use debug
          signingConfig keystorePropertiesFile.exists() ? signingConfigs.release : signingConfigs.debug''';

        lines[signingConfigIndex] = newSigningConfig;
        file.writeAsStringSync(lines.join('\n'));
        lines = file.readAsLinesSync();
      }

      // Find buildTypes closing brace to add APK renaming logic
      int buildTypesEnd = -1;
      for (int i = 0; i < lines.length; i++) {
        if (lines[i].contains('buildTypes')) {
          int braceCount = 0;
          for (int j = i; j < lines.length; j++) {
            if (lines[j].contains('{')) braceCount++;
            if (lines[j].contains('}')) {
              braceCount--;
              if (braceCount == 0) {
                buildTypesEnd = j;
                break;
              }
            }
          }
          break;
        }
      }

      if (buildTypesEnd != -1) {
        final apkRenameCode = isKotlinDsl ? '''
}

// Copy and rename APK based on flavor after build
gradle.buildFinished {
  val flavor = getAppFlavor()
  if (flavor.isNotEmpty()) {
      val flutterApkDir = File("\${project.buildDir}/outputs/flutter-apk")
      val apkOutputDir = File("\${project.buildDir}/outputs/apk")

      listOf(flutterApkDir, apkOutputDir).forEach { dir ->
          if (dir.exists()) {
              dir.walk().forEach { file ->
                  if (file.isFile && file.extension == "apk" && !file.name.contains("_\${flavor}")) {
                      val appName = android.namespace?.substringAfterLast(".") ?: "app"
                      val versionName = android.defaultConfig.versionName ?: "1.0.0"
                      val versionCode = android.defaultConfig.versionCode ?: 1
                      val newName = "\${appName}_\${flavor}\${versionName}(\${versionCode}).apk"
                      val newFile = File(file.parent, newName)
                      file.copyTo(newFile, overwrite = true)
                  }
              }
          }
      }
  }
}''' : '''
}

  android.applicationVariants.all { variant ->
      variant.outputs.all {
          if(appFlavor() != null){
               def appName = variant.getMergedFlavor().applicationId
               int lastIndex = appName.lastIndexOf('.')
               def modifiedAppName = lastIndex != -1 ? appName.substring(lastIndex + 1) : appName
               outputFileName = "\${modifiedAppName}_\${appFlavor()}\${versionName}(\${versionCode}).apk"
               renamePath(outputFileName)
          }
      }
  }
''';

        lines[buildTypesEnd] = apkRenameCode;
        file.writeAsStringSync(lines.join('\n'));
        'ssl_cli build setup successfully.'
            .printWithColor(status: PrintType.success);
      } else {
        'Error: buildTypes block not found in the specified file.'
            .printWithColor(status: PrintType.error);
      }
    } else {
      'Error: Flutter Gradle Plugin declaration not found.'
          .printWithColor(status: PrintType.error);
    }
  } catch (e) {
    print('Error: $e');
  }
}