Ensure apps can build while using AGP 3.3.0 (#71964) (#72754)

Co-authored-by: Emmanuel Garcia <egarciad@google.com>
diff --git a/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart b/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart
index 469a4d0..e15f02e 100644
--- a/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart
+++ b/dev/devicelab/bin/tasks/android_plugin_example_app_build_test.dart
@@ -15,7 +15,6 @@
 /// Tests that a plugin example app can be built using the current Flutter Gradle plugin.
 Future<void> main() async {
   await task(() async {
-
     section('Find Java');
 
     final String javaHome = await findJavaHome();
@@ -32,22 +31,47 @@
       options: <String>['--android', '--no-ios'],
     );
 
-    final Directory tempDir = Directory.systemTemp.createTempSync('flutter_plugin_test.');
-    final Directory projectDir = Directory(path.join(tempDir.path, 'plugin_test'));
+    final Directory tempDir =
+        Directory.systemTemp.createTempSync('flutter_plugin_test.');
+    final Directory projectDir =
+        Directory(path.join(tempDir.path, 'plugin_test'));
     try {
       await inDirectory(tempDir, () async {
         await flutter(
           'create',
-          options: <String>['--template=plugin', '--platforms=android', 'plugin_test'],
+          options: <String>[
+            '--template=plugin',
+            '--platforms=android',
+            'plugin_test',
+          ],
         );
       });
 
-      final Directory exampleAppDir = Directory(path.join(projectDir.path, 'example'));
+      final Directory exampleAppDir =
+          Directory(path.join(projectDir.path, 'example'));
       if (!exists(exampleAppDir)) {
         return TaskResult.failure('Example app directory doesn\'t exist');
       }
 
-      section('Run flutter build apk');
+      final File buildGradleFile =
+          File(path.join(exampleAppDir.path, 'android', 'build.gradle'));
+
+      if (!exists(buildGradleFile)) {
+        return TaskResult.failure('$buildGradleFile doesn\'t exist');
+      }
+
+      final String buildGradle = buildGradleFile.readAsStringSync();
+      final RegExp androidPluginRegExp =
+          RegExp(r'com\.android\.tools\.build:gradle:(\d+\.\d+\.\d+)');
+
+      section('Use AGP 4.1.0');
+
+      String newBuildGradle = buildGradle.replaceAll(
+          androidPluginRegExp, 'com.android.tools.build:gradle:4.1.0');
+      print(newBuildGradle);
+      buildGradleFile.writeAsString(newBuildGradle);
+
+      section('Run flutter build apk using AGP 4.1.0');
 
       await inDirectory(exampleAppDir, () async {
         await flutter(
@@ -72,6 +96,55 @@
         return TaskResult.failure('Failed to build app-release.apk');
       }
 
+      section('Clean');
+
+      await inDirectory(exampleAppDir, () async {
+        await flutter('clean');
+      });
+
+      section('Remove Gradle wrapper');
+
+      Directory(path.join(exampleAppDir.path, 'android', 'gradle', 'wrapper'))
+          .deleteSync(recursive: true);
+
+      section('Use AGP 3.3.0');
+
+      newBuildGradle = buildGradle.replaceAll(
+          androidPluginRegExp, 'com.android.tools.build:gradle:3.3.0');
+      print(newBuildGradle);
+      buildGradleFile.writeAsString(newBuildGradle);
+
+      section('Enable R8 in gradle.properties');
+
+      final File gradleProperties =
+          File(path.join(exampleAppDir.path, 'android', 'gradle.properties'));
+
+      if (!exists(gradleProperties)) {
+        return TaskResult.failure('$gradleProperties doesn\'t exist');
+      }
+
+      gradleProperties.writeAsString('''
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true''');
+
+      section('Run flutter build apk using AGP 3.3.0');
+
+      await inDirectory(exampleAppDir, () async {
+        await flutter(
+          'build',
+          options: <String>[
+            'apk',
+            '--target-platform=android-arm',
+          ],
+        );
+      });
+
+      if (!exists(File(exampleApk))) {
+        return TaskResult.failure('Failed to build app-release.apk');
+      }
+
       return TaskResult.success(null);
     } on TaskResult catch (taskResult) {
       return taskResult;
diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle
index 579cdea..8ea1ea2 100644
--- a/packages/flutter_tools/gradle/flutter.gradle
+++ b/packages/flutter_tools/gradle/flutter.gradle
@@ -20,6 +20,7 @@
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.bundling.Jar
+import org.gradle.util.VersionNumber
 
 buildscript {
     repositories {
@@ -304,7 +305,7 @@
         project.dependencies {
             implementation pluginProject
         }
-        Closure addEmbeddingCompileOnlyDependency = { buildType ->
+        Closure addEmbeddingDependencyToPlugin = { 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.
@@ -317,17 +318,39 @@
             pluginProject.android.buildTypes {
                 "${buildType.name}" {}
             }
-            pluginProject.dependencies.add(
-                "${buildType.name}CompileOnly",
-                "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion")
+            // The embedding is a compileOnly dependency of a Flutter plugin,
+            // however prior to Gradle 6.0.0, it must be an API dependency.
+            //
+            // Not doing so, causes transitive dependency resolution conflicts.
+            // That is, the embedding dependencies resolved in the plugin are
+            // different than the ones resolved in the app.
+            if (isGradleVersionGraterOrEqualThan('6.0.0')) {
+              addCompileOnlyDependency(
+                pluginProject,
+                buildType.name,
+                "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion"
+              )
+            } else {
+              addApiDependencies(
+                pluginProject,
+                buildType.name,
+                "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion"
+              )
+            }
         }
         // Wait until the Android plugin loaded.
         pluginProject.afterEvaluate {
-            project.android.buildTypes.each addEmbeddingCompileOnlyDependency
-            project.android.buildTypes.whenObjectAdded addEmbeddingCompileOnlyDependency
+            project.android.buildTypes.each addEmbeddingDependencyToPlugin
+            project.android.buildTypes.whenObjectAdded addEmbeddingDependencyToPlugin
         }
     }
 
+    // Returns `true` if the current Gradle version is greater or equal to the given version.
+    private isGradleVersionGraterOrEqualThan(String version) {
+      return VersionNumber.parse(project.gradle.gradleVersion)
+          .compareTo(VersionNumber.parse(version)) >= 0
+    }
+
     // Returns `true` if the given path contains an `android/build.gradle` file.
     //
     // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/39657.