| // Copyright 2019 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | import static groovy.io.FileType.FILES | 
 |  | 
 | import com.android.builder.model.AndroidProject | 
 | import com.android.build.OutputFile | 
 | import java.nio.file.Path | 
 | import java.nio.file.Paths | 
 | import java.util.regex.Matcher | 
 | import java.util.regex.Pattern | 
 | import org.apache.tools.ant.taskdefs.condition.Os | 
 | import org.gradle.api.DefaultTask | 
 | import org.gradle.api.GradleException | 
 | import org.gradle.api.Project | 
 | import org.gradle.api.Plugin | 
 | import org.gradle.api.Task | 
 | import org.gradle.api.file.CopySpec | 
 | import org.gradle.api.file.FileCollection | 
 | import org.gradle.api.tasks.Copy | 
 | import org.gradle.api.tasks.InputFiles | 
 | import org.gradle.api.tasks.OutputDirectory | 
 | import org.gradle.api.tasks.TaskAction | 
 | import org.gradle.api.tasks.bundling.Jar | 
 |  | 
 | buildscript { | 
 |     repositories { | 
 |         google() | 
 |         jcenter() | 
 |     } | 
 |     dependencies { | 
 |         classpath 'com.android.tools.build:gradle:3.5.0' | 
 |     } | 
 | } | 
 |  | 
 | android { | 
 |     compileOptions { | 
 |         sourceCompatibility 1.8 | 
 |         targetCompatibility 1.8 | 
 |     } | 
 | } | 
 |  | 
 | apply plugin: FlutterPlugin | 
 |  | 
 | class FlutterPlugin implements Plugin<Project> { | 
 |     private static final String MAVEN_REPO      = "http://download.flutter.io"; | 
 |  | 
 |     // The platforms that can be passed to the `--Ptarget-platform` flag. | 
 |     private static final String PLATFORM_ARM32  = "android-arm"; | 
 |     private static final String PLATFORM_ARM64  = "android-arm64"; | 
 |     private static final String PLATFORM_X86    = "android-x86"; | 
 |     private static final String PLATFORM_X86_64 = "android-x64"; | 
 |  | 
 |     // The ABI architectures. | 
 |     private static final String ARCH_ARM32      = "armeabi-v7a"; | 
 |     private static final String ARCH_ARM64      = "arm64-v8a"; | 
 |     private static final String ARCH_X86        = "x86"; | 
 |     private static final String ARCH_X86_64     = "x86_64"; | 
 |  | 
 |     // Maps platforms to ABI architectures. | 
 |     private static final Map PLATFORM_ARCH_MAP = [ | 
 |         (PLATFORM_ARM32)    : ARCH_ARM32, | 
 |         (PLATFORM_ARM64)    : ARCH_ARM64, | 
 |         (PLATFORM_X86)      : ARCH_X86, | 
 |         (PLATFORM_X86_64)   : ARCH_X86_64, | 
 |     ] | 
 |  | 
 |     // The version code that gives each ABI a value. | 
 |     // For each APK variant, use the following versions to override the version of the Universal APK. | 
 |     // Otherwise, the Play Store will complain that the APK variants have the same version. | 
 |     private static final Map ABI_VERSION = [ | 
 |         (ARCH_ARM32)        : 1, | 
 |         (ARCH_ARM64)        : 2, | 
 |         (ARCH_X86)          : 3, | 
 |         (ARCH_X86_64)       : 4, | 
 |     ] | 
 |  | 
 |     // When split is enabled, multiple APKs are generated per each ABI. | 
 |     private static final List DEFAULT_PLATFORMS = [ | 
 |         PLATFORM_ARM32, | 
 |         PLATFORM_ARM64, | 
 |     ] | 
 |  | 
 |     // The name prefix for flutter builds.  This is used to identify gradle tasks | 
 |     // where we expect the flutter tool to provide any error output, and skip the | 
 |     // standard Gradle error output in the FlutterEventLogger. If you change this, | 
 |     // be sure to change any instances of this string in symbols in the code below | 
 |     // to match. | 
 |     static final String FLUTTER_BUILD_PREFIX = "flutterBuild" | 
 |  | 
 |     private Project project | 
 |     private Map baseJar = [:] | 
 |     private File flutterRoot | 
 |     private File flutterExecutable | 
 |     private String localEngine | 
 |     private String localEngineSrcPath | 
 |     private Properties localProperties | 
 |     private File flutterJar | 
 |     private String engineVersion | 
 |  | 
 |     @Override | 
 |     void apply(Project project) { | 
 |         this.project = project | 
 |  | 
 |         project.extensions.create("flutter", FlutterExtension) | 
 |         project.afterEvaluate this.&addFlutterTasks | 
 |         // By default, assembling APKs generates fat APKs if multiple platforms are passed. | 
 |         // Configuring split per ABI allows to generate separate APKs for each abi. | 
 |         // This is a noop when building a bundle. | 
 |         if (shouldSplitPerAbi()) { | 
 |             project.android { | 
 |                 splits { | 
 |                     abi { | 
 |                         // Enables building multiple APKs per ABI. | 
 |                         enable true | 
 |                         // Resets the list of ABIs that Gradle should create APKs for to none. | 
 |                         reset() | 
 |                         // Specifies that we do not want to also generate a universal APK that includes all ABIs. | 
 |                         universalApk false | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |         getTargetPlatforms().each { targetArch -> | 
 |             String abiValue = PLATFORM_ARCH_MAP[targetArch] | 
 |             project.android { | 
 |                 if (shouldSplitPerAbi()) { | 
 |                     splits { | 
 |                         abi { | 
 |                             include abiValue | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         String flutterRootPath = resolveProperty("flutter.sdk", System.env.FLUTTER_ROOT) | 
 |         if (flutterRootPath == null) { | 
 |             throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file or with a FLUTTER_ROOT environment variable.") | 
 |         } | 
 |         flutterRoot = project.file(flutterRootPath) | 
 |         if (!flutterRoot.isDirectory()) { | 
 |             throw new GradleException("flutter.sdk must point to the Flutter SDK directory") | 
 |         } | 
 |  | 
 |         engineVersion = Paths.get(flutterRoot.absolutePath, "bin", "internal", "engine.version") | 
 |                 .toFile().text.trim() | 
 |  | 
 |         String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter" | 
 |         flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile(); | 
 |  | 
 |         // Add custom build types. | 
 |         project.android.buildTypes { | 
 |             profile { | 
 |                 initWith debug | 
 |                 if (it.hasProperty("matchingFallbacks")) { | 
 |                     matchingFallbacks = ["debug", "release"] | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         if (shouldShrinkResources(project)) { | 
 |             String flutterProguardRules = Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", | 
 |                     "gradle", "flutter_proguard_rules.pro") | 
 |             project.android.buildTypes { | 
 |                 release { | 
 |                     // Enables code shrinking, obfuscation, and optimization for only | 
 |                     // your project's release build type. | 
 |                     minifyEnabled true | 
 |                     // Enables resource shrinking, which is performed by the | 
 |                     // Android Gradle plugin. | 
 |                     // NOTE: The resource shrinker can't be used for libraries. | 
 |                     shrinkResources isBuiltAsApp(project) | 
 |                     // Fallback to `android/app/proguard-rules.pro`. | 
 |                     // This way, custom Proguard rules can be configured as needed. | 
 |                     proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro" | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         if (useLocalEngine()) { | 
 |             String engineOutPath = project.property('localEngineOut') | 
 |             File engineOut = project.file(engineOutPath) | 
 |             if (!engineOut.isDirectory()) { | 
 |                 throw new GradleException('localEngineOut must point to a local engine build') | 
 |             } | 
 |             Path baseEnginePath = Paths.get(engineOut.absolutePath) | 
 |             flutterJar = baseEnginePath.resolve("flutter.jar").toFile() | 
 |             if (!flutterJar.isFile()) { | 
 |                 throw new GradleException("Local engine jar not found: $flutterJar") | 
 |             } | 
 |             localEngine = engineOut.name | 
 |             localEngineSrcPath = engineOut.parentFile.parent | 
 |             // The local engine is built for one of the build type. | 
 |             // However, we use the same engine for each of the build types. | 
 |             project.android.buildTypes.each { | 
 |                 addApiDependencies(project, it.name, project.files { | 
 |                     flutterJar | 
 |                 }) | 
 |             } | 
 |         } else { | 
 |             project.android.buildTypes.each this.&addFlutterDependencies | 
 |             project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Adds the dependencies required by the Flutter project. | 
 |      * This includes: | 
 |      *    1. The embedding | 
 |      *    2. libflutter.so | 
 |      */ | 
 |     void addFlutterDependencies(buildType) { | 
 |         project.rootProject.allprojects { | 
 |             repositories { | 
 |                 maven { | 
 |                     url MAVEN_REPO | 
 |                 } | 
 |             } | 
 |         } | 
 |         String flutterBuildMode = buildModeFor(buildType) | 
 |         // Add the embedding dependency. | 
 |         addApiDependencies(project, buildType.name, | 
 |                 "io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion") | 
 |  | 
 |         List<String> platforms = getTargetPlatforms().collect() | 
 |         // Debug mode includes x86 and x64, which are commonly used in emulators. | 
 |         if (flutterBuildMode == "debug") { | 
 |             platforms.add("android-x86") | 
 |             platforms.add("android-x64") | 
 |         } | 
 |         platforms.each { platform -> | 
 |             String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_") | 
 |             // Add the `libflutter.so` dependency. | 
 |             addApiDependencies(project, buildType.name, | 
 |                     "io.flutter:${arch}_$flutterBuildMode:1.0.0-$engineVersion") | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * Returns the directory where the plugins are built. | 
 |      */ | 
 |     private File getPluginBuildDir() { | 
 |         // Module projects specify this flag to include plugins in the same repo as the module project. | 
 |         if (project.ext.has("pluginBuildDir")) { | 
 |             return project.ext.get("pluginBuildDir") | 
 |         } | 
 |         return project.buildDir | 
 |     } | 
 |  | 
 |     /** | 
 |      * Configures the Flutter plugin dependencies. | 
 |      * | 
 |      * The plugins are added to pubspec.yaml. Then, upon running `flutter pub get`, | 
 |      * the tool generates a `.flutter-plugins` file, which contains a 1:1 map to each plugin location. | 
 |      * Finally, the project's `settings.gradle` loads each plugin's android directory as a subproject. | 
 |      */ | 
 |     private void configurePlugins() { | 
 |         if (!buildPluginAsAar()) { | 
 |             getPluginList().each this.&configurePluginProject | 
 |             return | 
 |         } | 
 |         if (useLocalEngine()) { | 
 |             throw new GradleException("Local engine isn't supported when building the plugins as AAR") | 
 |         } | 
 |         List<Project> projects = [project] | 
 |         // Module projects set the `hostProjects` extra property in `include_flutter.groovy`. | 
 |         // This is required to set the local repository in each host app project. | 
 |         if (project.ext.has("hostProjects")) { | 
 |             projects.addAll(project.ext.get("hostProjects")) | 
 |         } | 
 |         // Configure the repository for the plugins. | 
 |         projects.each { hostProject -> | 
 |             hostProject.repositories { | 
 |                 maven { | 
 |                     url "${getPluginBuildDir()}/outputs/repo" | 
 |                 } | 
 |             } | 
 |         } | 
 |         getPluginList().each { pluginName, pluginPath -> | 
 |             configurePluginAar(pluginName, pluginPath, project) | 
 |         } | 
 |     } | 
 |  | 
 |     private static final Pattern GROUP_PATTERN = ~/group\s+\'(.+)\'/ | 
 |     private static final Pattern PROJECT_NAME_PATTERN = ~/rootProject\.name\s+=\s+\'(.+)\'/ | 
 |  | 
 |     // Adds the plugin AAR dependency to the app project. | 
 |     private void configurePluginAar(String pluginName, String pluginPath, Project project) { | 
 |         // Extract the group id from the plugin's build.gradle. | 
 |         // This is `group '<group-id>'` | 
 |         File pluginBuildFile = project.file(Paths.get(pluginPath, "android", "build.gradle")); | 
 |         if (!pluginBuildFile.exists()) { | 
 |             throw new GradleException("Plugin $pluginName doesn't have the required file $pluginBuildFile.") | 
 |         } | 
 |  | 
 |         Matcher groupParts = GROUP_PATTERN.matcher(pluginBuildFile.text) | 
 |         assert groupParts.count == 1 | 
 |         assert groupParts.hasGroup() | 
 |         String groupId = groupParts[0][1] | 
 |  | 
 |         // Extract the artifact name from the plugin's settings.gradle. | 
 |         // This is `rootProject.name = '<artifact-name>'` | 
 |         File pluginSettings = project.file(Paths.get(pluginPath, "android", "settings.gradle")); | 
 |         if (!pluginSettings.exists()) { | 
 |             throw new GradleException("Plugin $pluginName doesn't have the required file $pluginSettings.") | 
 |         } | 
 |         Matcher projectNameParts = PROJECT_NAME_PATTERN.matcher(pluginSettings.text) | 
 |         assert projectNameParts.count == 1 | 
 |         assert projectNameParts.hasGroup() | 
 |         String artifactId = "${projectNameParts[0][1]}_release" | 
 |  | 
 |         assert !groupId.empty | 
 |         project.dependencies.add("api", "$groupId:$artifactId:+") | 
 |     } | 
 |  | 
 |     // Adds the plugin project dependency to the app project . | 
 |     private void configurePluginProject(String name, String _) { | 
 |         Project pluginProject = project.rootProject.findProject(":$name") | 
 |         if (pluginProject == null) { | 
 |             project.logger.error("Plugin project :$name not found. Please update settings.gradle.") | 
 |             return | 
 |         } | 
 |         // Add plugin dependency to the app project. | 
 |         project.dependencies { | 
 |             if (project.getConfigurations().findByName("implementation")) { | 
 |                 implementation pluginProject | 
 |             } else { | 
 |                 compile pluginProject | 
 |             } | 
 |         } | 
 |         Closure addEmbeddingCompileOnlyDependency = { buildType -> | 
 |             String flutterBuildMode = buildModeFor(buildType) | 
 |             // In AGP 3.5, the embedding must be added as an API implementation, | 
 |             // so java8 features are desugared against the runtime classpath. | 
 |             // For more, see https://github.com/flutter/flutter/issues/40126 | 
 |             if (flutterJar) { | 
 |                 addApiDependencies( | 
 |                     pluginProject, | 
 |                     buildType.name, | 
 |                     project.files { | 
 |                         flutterJar | 
 |                     } | 
 |                 ) | 
 |                 return | 
 |             } | 
 |             addApiDependencies( | 
 |                 pluginProject, | 
 |                 buildType.name, | 
 |                 "io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion", | 
 |                 { | 
 |                     // Include the embedding transitive dependencies since plugins may depend on them. | 
 |                     transitive = true | 
 |                 } | 
 |             ) | 
 |         } | 
 |         pluginProject.afterEvaluate { | 
 |             pluginProject.android.buildTypes { | 
 |                 profile { | 
 |                     initWith debug | 
 |                 } | 
 |             } | 
 |             pluginProject.android.buildTypes.each addEmbeddingCompileOnlyDependency | 
 |             pluginProject.android.buildTypes.whenObjectAdded addEmbeddingCompileOnlyDependency | 
 |         } | 
 |     } | 
 |  | 
 |     private Properties getPluginList() { | 
 |         File pluginsFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins') | 
 |         Properties allPlugins = readPropertiesIfExist(pluginsFile) | 
 |         Properties androidPlugins = new Properties() | 
 |         allPlugins.each { name, path -> | 
 |             File editableAndroidProject = new File(path, 'android' + File.separator + 'build.gradle') | 
 |             if (editableAndroidProject.exists()) { | 
 |                 androidPlugins.setProperty(name, path) | 
 |             } | 
 |             // TODO(amirh): log an error if this plugin was specified to be an Android | 
 |             // plugin according to the new schema, and was missing a build.gradle file. | 
 |             // https://github.com/flutter/flutter/issues/40784 | 
 |         } | 
 |         return androidPlugins | 
 |     } | 
 |  | 
 |     private static String toCammelCase(List<String> parts) { | 
 |         if (parts.empty) { | 
 |             return "" | 
 |         } | 
 |         return "${parts[0]}${parts[1..-1].collect { it.capitalize() }.join('')}" | 
 |     } | 
 |  | 
 |     private String resolveProperty(String name, String defaultValue) { | 
 |         if (localProperties == null) { | 
 |             localProperties = readPropertiesIfExist(new File(project.projectDir.parentFile, "local.properties")) | 
 |         } | 
 |         String result | 
 |         if (project.hasProperty(name)) { | 
 |             result = project.property(name) | 
 |         } | 
 |         if (result == null) { | 
 |             result = localProperties.getProperty(name) | 
 |         } | 
 |         if (result == null) { | 
 |             result = defaultValue | 
 |         } | 
 |         return result | 
 |     } | 
 |  | 
 |     private static Properties readPropertiesIfExist(File propertiesFile) { | 
 |         Properties result = new Properties() | 
 |         if (propertiesFile.exists()) { | 
 |             propertiesFile.withReader('UTF-8') { reader -> result.load(reader) } | 
 |         } | 
 |         return result | 
 |     } | 
 |  | 
 |     private List<String> getTargetPlatforms() { | 
 |         if (!project.hasProperty('target-platform')) { | 
 |             return DEFAULT_PLATFORMS | 
 |         } | 
 |         return project.property('target-platform').split(',').collect { | 
 |             if (!PLATFORM_ARCH_MAP[it]) { | 
 |                 throw new GradleException("Invalid platform: $it.") | 
 |             } | 
 |             return it | 
 |         } | 
 |     } | 
 |  | 
 |     private Boolean shouldSplitPerAbi() { | 
 |         if (project.hasProperty('split-per-abi')) { | 
 |             return project.property('split-per-abi').toBoolean() | 
 |         } | 
 |         return false; | 
 |     } | 
 |  | 
 |     private Boolean useLocalEngine() { | 
 |         return project.hasProperty('localEngineOut') | 
 |     } | 
 |  | 
 |     private Boolean isVerbose() { | 
 |         if (project.hasProperty('verbose')) { | 
 |             return project.property('verbose').toBoolean() | 
 |         } | 
 |         return false | 
 |     } | 
 |  | 
 |     private static Boolean shouldShrinkResources(Project project) { | 
 |         if (project.hasProperty("shrink")) { | 
 |             return project.property("shrink").toBoolean() | 
 |         } | 
 |         return false | 
 |     } | 
 |  | 
 |     private static Boolean isBuiltAsApp(Project project) { | 
 |         // Projects are built as applications when the they use the `com.android.application` | 
 |         // plugin. | 
 |         return project.plugins.hasPlugin("com.android.application"); | 
 |     } | 
 |  | 
 |     private static Boolean buildPluginAsAar() { | 
 |         return System.getProperty('build-plugins-as-aars') == 'true' | 
 |     } | 
 |  | 
 |     private void addCompileOnlyDependency(Project project, String variantName, Object dependency, Closure config = null) { | 
 |         if (project.state.failure) { | 
 |             return | 
 |         } | 
 |         String configuration; | 
 |         if (project.getConfigurations().findByName("compileOnly")) { | 
 |             configuration = "${variantName}CompileOnly"; | 
 |         } else { | 
 |             configuration = "${variantName}Provided"; | 
 |         } | 
 |         project.dependencies.add(configuration, dependency, config) | 
 |     } | 
 |  | 
 |     private static void addApiDependencies(Project project, String variantName, Object dependency, Closure config = null) { | 
 |         String configuration; | 
 |         // `compile` dependencies are now `api` dependencies. | 
 |         if (project.getConfigurations().findByName("api")) { | 
 |             configuration = "${variantName}Api"; | 
 |         } else { | 
 |             configuration = "${variantName}Compile"; | 
 |         } | 
 |         project.dependencies.add(configuration, dependency, config) | 
 |     } | 
 |  | 
 |     /** | 
 |      * Returns a Flutter build mode suitable for the specified Android buildType. | 
 |      * | 
 |      * Note: The BuildType DSL type is not public, and is therefore omitted from the signature. | 
 |      * | 
 |      * @return "debug", "profile", or "release" (fall-back). | 
 |      */ | 
 |     private static String buildModeFor(buildType) { | 
 |         if (buildType.name == "profile") { | 
 |             return "profile" | 
 |         } else if (buildType.debuggable) { | 
 |             return "debug" | 
 |         } | 
 |         return "release" | 
 |     } | 
 |  | 
 |     private static String getEngineArtifactDirName(buildType, targetArch) { | 
 |         if (buildType.name == "profile") { | 
 |             return "${targetArch}-profile" | 
 |         } else if (buildType.debuggable) { | 
 |             return "${targetArch}" | 
 |         } | 
 |         return "${targetArch}-release" | 
 |     } | 
 |  | 
 |     private void addFlutterTasks(Project project) { | 
 |         if (project.state.failure) { | 
 |             return | 
 |         } | 
 |         if (project.flutter.source == null) { | 
 |             throw new GradleException("Must provide Flutter source directory") | 
 |         } | 
 |         String target = project.flutter.target | 
 |         if (target == null) { | 
 |             target = 'lib/main.dart' | 
 |         } | 
 |         if (project.hasProperty('target')) { | 
 |             target = project.property('target') | 
 |         } | 
 |         String[] fileSystemRootsValue = null | 
 |         if (project.hasProperty('filesystem-roots')) { | 
 |             fileSystemRootsValue = project.property('filesystem-roots').split('\\|') | 
 |         } | 
 |         String fileSystemSchemeValue = null | 
 |         if (project.hasProperty('filesystem-scheme')) { | 
 |             fileSystemSchemeValue = project.property('filesystem-scheme') | 
 |         } | 
 |         Boolean trackWidgetCreationValue = false | 
 |         if (project.hasProperty('track-widget-creation')) { | 
 |             trackWidgetCreationValue = project.property('track-widget-creation').toBoolean() | 
 |         } | 
 |         String compilationTraceFilePathValue = null | 
 |         if (project.hasProperty('compilation-trace-file')) { | 
 |             compilationTraceFilePathValue = project.property('compilation-trace-file') | 
 |         } | 
 |         Boolean createPatchValue = false | 
 |         if (project.hasProperty('patch')) { | 
 |             createPatchValue = project.property('patch').toBoolean() | 
 |         } | 
 |         Integer buildNumberValue = null | 
 |         if (project.hasProperty('build-number')) { | 
 |             buildNumberValue = project.property('build-number').toInteger() | 
 |         } | 
 |         String baselineDirValue = null | 
 |         if (project.hasProperty('baseline-dir')) { | 
 |             baselineDirValue = project.property('baseline-dir') | 
 |         } | 
 |         String extraFrontEndOptionsValue = null | 
 |         if (project.hasProperty('extra-front-end-options')) { | 
 |             extraFrontEndOptionsValue = project.property('extra-front-end-options') | 
 |         } | 
 |         String extraGenSnapshotOptionsValue = null | 
 |         if (project.hasProperty('extra-gen-snapshot-options')) { | 
 |             extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options') | 
 |         } | 
 |         def targetPlatforms = getTargetPlatforms() | 
 |         def addFlutterDeps = { variant -> | 
 |             if (shouldSplitPerAbi()) { | 
 |                 variant.outputs.each { output -> | 
 |                     // Assigns the new version code to versionCodeOverride, which changes the version code | 
 |                     // for only the output APK, not for the variant itself. Skipping this step simply | 
 |                     // causes Gradle to use the value of variant.versionCode for the APK. | 
 |                     // For more, see https://developer.android.com/studio/build/configure-apk-splits | 
 |                     def abiVersionCode = ABI_VERSION.get(output.getFilter(OutputFile.ABI)) | 
 |                     if (abiVersionCode != null) { | 
 |                         output.versionCodeOverride = | 
 |                             abiVersionCode * 1000 + variant.versionCode | 
 |                     } | 
 |                 } | 
 |             } | 
 |             String variantBuildMode = buildModeFor(variant.buildType) | 
 |             List<FlutterTask> compileTasks = targetPlatforms.collect { targetArch -> | 
 |                 String taskName = toCammelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name, targetArch.replace('android-', '')]) | 
 |                 project.tasks.create(name: taskName, type: FlutterTask) { | 
 |                     flutterRoot this.flutterRoot | 
 |                     flutterExecutable this.flutterExecutable | 
 |                     buildMode variantBuildMode | 
 |                     localEngine this.localEngine | 
 |                     localEngineSrcPath this.localEngineSrcPath | 
 |                     abi PLATFORM_ARCH_MAP[targetArch] | 
 |                     targetPath target | 
 |                     verbose isVerbose() | 
 |                     fileSystemRoots fileSystemRootsValue | 
 |                     fileSystemScheme fileSystemSchemeValue | 
 |                     trackWidgetCreation trackWidgetCreationValue | 
 |                     compilationTraceFilePath compilationTraceFilePathValue | 
 |                     createPatch createPatchValue | 
 |                     buildNumber buildNumberValue | 
 |                     baselineDir baselineDirValue | 
 |                     targetPlatform targetArch | 
 |                     sourceDir project.file(project.flutter.source) | 
 |                     intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/${targetArch}") | 
 |                     extraFrontEndOptions extraFrontEndOptionsValue | 
 |                     extraGenSnapshotOptions extraGenSnapshotOptionsValue | 
 |                 } | 
 |             } | 
 |             File libJar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar") | 
 |             Task packFlutterAppAotTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) { | 
 |                 destinationDir libJar.parentFile | 
 |                 archiveName libJar.name | 
 |                 dependsOn compileTasks | 
 |                 compileTasks.each { compileTask -> | 
 |                     from(compileTask.intermediateDir) { | 
 |                         include '*.so' | 
 |                         // Move `app.so` to `lib/<abi>/libapp.so` | 
 |                         rename { String filename -> | 
 |                             return "lib/${compileTask.abi}/lib${filename}" | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |             addApiDependencies(project, variant.name, project.files { | 
 |                 packFlutterAppAotTask | 
 |             }) | 
 |             // We build an AAR when this property is defined. | 
 |             boolean isBuildingAar = project.hasProperty('is-plugin') | 
 |             // In add to app scenarios, a Gradle project contains a `:flutter` and `:app` project. | 
 |             // We know that `:flutter` is used as a subproject when these tasks exists and we aren't building an AAR. | 
 |             Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets") | 
 |             Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets") | 
 |             boolean isUsedAsSubproject = packageAssets && cleanPackageAssets && !isBuildingAar | 
 |             Task copyFlutterAssetsTask = project.tasks.create( | 
 |                 name: "copyFlutterAssets${variant.name.capitalize()}", | 
 |                 type: Copy, | 
 |             ) { | 
 |                 dependsOn compileTasks | 
 |                 compileTasks.each { flutterTask -> | 
 |                     // Add flutter_assets. | 
 |                     with flutterTask.assets | 
 |                 } | 
 |                 if (isUsedAsSubproject) { | 
 |                     dependsOn packageAssets | 
 |                     dependsOn cleanPackageAssets | 
 |                     into packageAssets.outputDir | 
 |                     return | 
 |                 } | 
 |                 // `variant.mergeAssets` will be removed at the end of 2019. | 
 |                 def mergeAssets = variant.hasProperty("mergeAssetsProvider") ? | 
 |                     variant.mergeAssetsProvider.get() : variant.mergeAssets | 
 |                 dependsOn mergeAssets | 
 |                 dependsOn "clean${mergeAssets.name.capitalize()}" | 
 |                 mergeAssets.mustRunAfter("clean${mergeAssets.name.capitalize()}") | 
 |                 into mergeAssets.outputDir | 
 |             } | 
 |             if (!isUsedAsSubproject) { | 
 |                 def variantOutput = variant.outputs.first() | 
 |                 def processResources = variantOutput.hasProperty("processResourcesProvider") ? | 
 |                     variantOutput.processResourcesProvider.get() : variantOutput.processResources | 
 |                 processResources.dependsOn(copyFlutterAssetsTask) | 
 |                 return | 
 |             } | 
 |             // Flutter module included as a subproject in add to app. | 
 |             Project appProject = project.rootProject.findProject(':app') | 
 |             assert appProject != null | 
 |             appProject.afterEvaluate { | 
 |                 assert appProject.android != null | 
 |                 appProject.android.applicationVariants.all { appProjectVariant -> | 
 |                     // Find a compatible application variant in the host app. | 
 |                     // | 
 |                     // For example, consider a host app that defines the following variants: | 
 |                     // | ----------------- | ----------------------------- | | 
 |                     // |   Build Variant   |   Flutter Equivalent Variant  | | 
 |                     // | ----------------- | ----------------------------- | | 
 |                     // |   freeRelease     |   relese                      | | 
 |                     // |   freeDebug       |   debug                       | | 
 |                     // |   freeDevelop     |   debug                       | | 
 |                     // |   profile         |   profile                     | | 
 |                     // | ----------------- | ----------------------------- | | 
 |                     // | 
 |                     // This mapping is based on the following rules: | 
 |                     // 1. If the host app build variant name is `profile` then the equivalent | 
 |                     //    Flutter variant is `profile`. | 
 |                     // 2. If the host app build variant is debuggable | 
 |                     //    (e.g. `buildType.debuggable = true`), then the equivalent Flutter | 
 |                     //    variant is `debug`. | 
 |                     // 3. Otherwise, the equivalent Flutter variant is `release`. | 
 |                     if (buildModeFor(appProjectVariant.buildType) != variantBuildMode) { | 
 |                         return | 
 |                     } | 
 |                     Task mergeAssets = project | 
 |                         .tasks | 
 |                         .findByPath(":app:merge${appProjectVariant.name.capitalize()}Assets") | 
 |                     assert mergeAssets | 
 |                     mergeAssets.dependsOn(copyFlutterAssetsTask) | 
 |                 } | 
 |             } | 
 |         } | 
 |         if (project.android.hasProperty("applicationVariants")) { | 
 |             project.android.applicationVariants.all addFlutterDeps | 
 |         } else { | 
 |             project.android.libraryVariants.all addFlutterDeps | 
 |         } | 
 |         configurePlugins() | 
 |     } | 
 | } | 
 |  | 
 | class FlutterExtension { | 
 |     String source | 
 |     String target | 
 | } | 
 |  | 
 | abstract class BaseFlutterTask extends DefaultTask { | 
 |     File flutterRoot | 
 |     File flutterExecutable | 
 |     String buildMode | 
 |     String localEngine | 
 |     String localEngineSrcPath | 
 |     @Input | 
 |     String targetPath | 
 |     @Optional @Input | 
 |     Boolean verbose | 
 |     @Optional @Input | 
 |     String[] fileSystemRoots | 
 |     @Optional @Input | 
 |     String fileSystemScheme | 
 |     @Input | 
 |     Boolean trackWidgetCreation | 
 |     @Optional @Input | 
 |     String compilationTraceFilePath | 
 |     @Optional @Input | 
 |     Boolean createPatch | 
 |     @Optional @Input | 
 |     Integer buildNumber | 
 |     @Optional @Input | 
 |     String baselineDir | 
 |     @Optional @Input | 
 |     String targetPlatform | 
 |     @Input | 
 |     String abi | 
 |     File sourceDir | 
 |     File intermediateDir | 
 |     @Optional @Input | 
 |     String extraFrontEndOptions | 
 |     @Optional @Input | 
 |     String extraGenSnapshotOptions | 
 |  | 
 |     @OutputFiles | 
 |     FileCollection getDependenciesFiles() { | 
 |         FileCollection depfiles = project.files() | 
 |  | 
 |         // Include the kernel compiler depfile, since kernel compile is the | 
 |         // first stage of AOT build in this mode, and it includes all the Dart | 
 |         // sources. | 
 |         depfiles += project.files("${intermediateDir}/kernel_compile.d") | 
 |  | 
 |         // Include Core JIT kernel compiler depfile, since kernel compile is | 
 |         // the first stage of JIT builds in this mode, and it includes all the | 
 |         // Dart sources. | 
 |         depfiles += project.files("${intermediateDir}/snapshot_blob.bin.d") | 
 |         return depfiles | 
 |     } | 
 |  | 
 |     void buildBundle() { | 
 |         if (!sourceDir.isDirectory()) { | 
 |             throw new GradleException("Invalid Flutter source directory: ${sourceDir}") | 
 |         } | 
 |  | 
 |         intermediateDir.mkdirs() | 
 |  | 
 |         if (buildMode == "profile" || buildMode == "release") { | 
 |             project.exec { | 
 |                 executable flutterExecutable.absolutePath | 
 |                 workingDir sourceDir | 
 |                 if (localEngine != null) { | 
 |                     args "--local-engine", localEngine | 
 |                     args "--local-engine-src-path", localEngineSrcPath | 
 |                 } | 
 |                 args "build", "aot" | 
 |                 args "--suppress-analytics" | 
 |                 args "--quiet" | 
 |                 args "--target", targetPath | 
 |                 args "--output-dir", "${intermediateDir}" | 
 |                 args "--target-platform", "${targetPlatform}" | 
 |                 if (trackWidgetCreation) { | 
 |                     args "--track-widget-creation" | 
 |                 } | 
 |                 if (extraFrontEndOptions != null) { | 
 |                     args "--extra-front-end-options", "${extraFrontEndOptions}" | 
 |                 } | 
 |                 if (extraGenSnapshotOptions != null) { | 
 |                     args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}" | 
 |                 } | 
 |                 args "--${buildMode}" | 
 |             } | 
 |         } | 
 |  | 
 |         project.exec { | 
 |             executable flutterExecutable.absolutePath | 
 |             workingDir sourceDir | 
 |  | 
 |             if (localEngine != null) { | 
 |                 args "--local-engine", localEngine | 
 |                 args "--local-engine-src-path", localEngineSrcPath | 
 |             } | 
 |             args "build", "bundle" | 
 |             args "--target", targetPath | 
 |             args "--target-platform", "${targetPlatform}" | 
 |             if (verbose) { | 
 |                 args "--verbose" | 
 |             } | 
 |             if (fileSystemRoots != null) { | 
 |                 for (root in fileSystemRoots) { | 
 |                     args "--filesystem-root", root | 
 |                 } | 
 |             } | 
 |             if (fileSystemScheme != null) { | 
 |                 args "--filesystem-scheme", fileSystemScheme | 
 |             } | 
 |             if (trackWidgetCreation) { | 
 |                 args "--track-widget-creation" | 
 |             } | 
 |             if (compilationTraceFilePath != null) { | 
 |                 args "--compilation-trace-file", compilationTraceFilePath | 
 |             } | 
 |             if (createPatch) { | 
 |                 args "--patch" | 
 |                 args "--build-number", project.android.defaultConfig.versionCode | 
 |                 if (buildNumber != null) { | 
 |                     assert buildNumber == project.android.defaultConfig.versionCode | 
 |                 } | 
 |             } | 
 |             if (baselineDir != null) { | 
 |                 args "--baseline-dir", baselineDir | 
 |             } | 
 |             if (extraFrontEndOptions != null) { | 
 |                 args "--extra-front-end-options", "${extraFrontEndOptions}" | 
 |             } | 
 |             if (extraGenSnapshotOptions != null) { | 
 |                 args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}" | 
 |             } | 
 |             if (buildMode == "release" || buildMode == "profile") { | 
 |                 args "--precompiled" | 
 |             } else { | 
 |                 args "--depfile", "${intermediateDir}/snapshot_blob.bin.d" | 
 |             } | 
 |             args "--asset-dir", "${intermediateDir}/flutter_assets" | 
 |             if (buildMode == "debug") { | 
 |                 args "--debug" | 
 |             } | 
 |             if (buildMode == "profile") { | 
 |                 args "--profile" | 
 |             } | 
 |             if (buildMode == "release") { | 
 |                 args "--release" | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | class FlutterTask extends BaseFlutterTask { | 
 |     @OutputDirectory | 
 |     File getOutputDirectory() { | 
 |         return intermediateDir | 
 |     } | 
 |  | 
 |     CopySpec getAssets() { | 
 |         return project.copySpec { | 
 |             from "${intermediateDir}" | 
 |             include "flutter_assets/**" // the working dir and its files | 
 |         } | 
 |     } | 
 |  | 
 |     CopySpec getSnapshots() { | 
 |         return project.copySpec { | 
 |             from "${intermediateDir}" | 
 |  | 
 |             if (buildMode == 'release' || buildMode == 'profile') { | 
 |                 include "app.so" | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     FileCollection readDependencies(File dependenciesFile) { | 
 |         if (dependenciesFile.exists()) { | 
 |             try { | 
 |                 // Dependencies file has Makefile syntax: | 
 |                 //   <target> <files>: <source> <files> <separated> <by> <non-escaped space> | 
 |                 String depText = dependenciesFile.text | 
 |                 // So we split list of files by non-escaped(by backslash) space, | 
 |                 def matcher = depText.split(': ')[1] =~ /(\\ |[^\s])+/ | 
 |                 // then we replace all escaped spaces with regular spaces | 
 |                 def depList = matcher.collect{it[0].replaceAll("\\\\ ", " ")} | 
 |                 return project.files(depList) | 
 |             } catch (Exception e) { | 
 |                 logger.error("Error reading dependency file ${dependenciesFile}: ${e}") | 
 |             } | 
 |         } | 
 |         return project.files() | 
 |     } | 
 |  | 
 |     @InputFiles | 
 |     FileCollection getSourceFiles() { | 
 |         FileCollection sources = project.files() | 
 |         for (File depfile in getDependenciesFiles()) { | 
 |           sources += readDependencies(depfile) | 
 |         } | 
 |         if (!sources.isEmpty()) { | 
 |             // We have a dependencies file. Add a dependency on gen_snapshot as well, since the | 
 |             // snapshots have to be rebuilt if it changes. | 
 |             sources += readDependencies(project.file("${intermediateDir}/gen_snapshot.d")) | 
 |             sources += readDependencies(project.file("${intermediateDir}/frontend_server.d")) | 
 |             if (localEngineSrcPath != null) { | 
 |                 sources += project.files("$localEngineSrcPath/$localEngine") | 
 |             } | 
 |             // Finally, add a dependency on pubspec.yaml as well. | 
 |             return sources + project.files('pubspec.yaml') | 
 |         } | 
 |         // No dependencies file (or problems parsing it). Fall back to source files. | 
 |         return project.fileTree( | 
 |                 dir: sourceDir, | 
 |                 exclude: ['android', 'ios'], | 
 |                 include: ['**/*.dart', 'pubspec.yaml'] | 
 |         ) | 
 |     } | 
 |  | 
 |     @TaskAction | 
 |     void build() { | 
 |         buildBundle() | 
 |     } | 
 | } |