From a3ea39db04eef2f6a713635ede0889060ab93ed5 Mon Sep 17 00:00:00 2001
From: Camille Simon <43054281+camsim99@users.noreply.github.com>
Date: Fri, 8 Nov 2024 13:53:19 -0800
Subject: [PATCH] [animations] Remove `.flutter-plugins` reference from example
 app (#8002)

Removes `.flutter-plugins` reference from example by (1) deleting the `packages/animations/example/android` directory, (2) running (in the `packages/animations` directory)

```
flutter create example --platforms android -a kotlin --org "dev.flutter.packages.animations"
```
and then (3) manually made the following changes:

- Added back `packages/animations/example/android/.pluginToolsConfig.yaml` (removed by command)
- Updated the Gradle version from 8.7 to 8.3 (downgraded by command)
- Deleted  `example/analysis_options.yaml` (added by command)
- Deleted `example/test/` (added by command)
- Added back artifact hub
- Removed template TODOs
- Bumped Kotlin Gradle version to 1.9.0
- Bumped AGP version from 8.1.0 to 8.5.1.

Part of https://github.com/flutter/flutter/issues/157660.
---
 packages/animations/example/.metadata         | 24 ++++++-
 .../animations/example/android/.gitignore     |  6 ++
 .../example/android/app/build.gradle          | 64 +++++--------------
 .../android/app/src/debug/AndroidManifest.xml |  3 +-
 .../android/app/src/main/AndroidManifest.xml  | 23 ++++++-
 .../animations/example/MainActivity.kt        |  3 +-
 .../res/drawable-v21/launch_background.xml    | 12 ++++
 .../app/src/main/res/values-night/styles.xml  | 18 ++++++
 .../app/src/main/res/values/styles.xml        | 14 +++-
 .../app/src/profile/AndroidManifest.xml       |  3 +-
 .../animations/example/android/build.gradle   | 17 +----
 .../example/android/gradle.properties         |  2 +-
 .../gradle/wrapper/gradle-wrapper.properties  |  1 -
 .../example/android/settings.gradle           | 42 ++++++------
 script/tool/lib/src/gradle_check_command.dart | 18 ++++--
 .../tool/test/gradle_check_command_test.dart  | 37 ++++++++++-
 16 files changed, 182 insertions(+), 105 deletions(-)
 create mode 100644 packages/animations/example/android/app/src/main/res/drawable-v21/launch_background.xml
 create mode 100644 packages/animations/example/android/app/src/main/res/values-night/styles.xml

diff --git a/packages/animations/example/.metadata b/packages/animations/example/.metadata
index 6b34df6b1f37..706ff779c7b7 100644
--- a/packages/animations/example/.metadata
+++ b/packages/animations/example/.metadata
@@ -4,7 +4,27 @@
 # This file should be version controlled and should not be manually edited.
 
 version:
-  revision: 0bced151e44ffa138e608c2e2dbff3309ab8bd75
-  channel: master
+  revision: "603104015dd692ea3403755b55d07813d5cf8965"
+  channel: "stable"
 
 project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 603104015dd692ea3403755b55d07813d5cf8965
+      base_revision: 603104015dd692ea3403755b55d07813d5cf8965
+    - platform: android
+      create_revision: 603104015dd692ea3403755b55d07813d5cf8965
+      base_revision: 603104015dd692ea3403755b55d07813d5cf8965
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/packages/animations/example/android/.gitignore b/packages/animations/example/android/.gitignore
index bc2100d8f75e..55afd919c659 100644
--- a/packages/animations/example/android/.gitignore
+++ b/packages/animations/example/android/.gitignore
@@ -5,3 +5,9 @@ gradle-wrapper.jar
 /gradlew.bat
 /local.properties
 GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/animations/example/android/app/build.gradle b/packages/animations/example/android/app/build.gradle
index 19d11f91e671..fd8903bacb35 100644
--- a/packages/animations/example/android/app/build.gradle
+++ b/packages/animations/example/android/app/build.gradle
@@ -1,33 +1,14 @@
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
-    localPropertiesFile.withReader('UTF-8') { reader ->
-        localProperties.load(reader)
-    }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
-    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
-    flutterVersionCode = '1'
-}
-
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
-    flutterVersionName = '1.0'
+plugins {
+    id "com.android.application"
+    id "kotlin-android"
+    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+    id "dev.flutter.flutter-gradle-plugin"
 }
 
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
 android {
-    namespace 'dev.flutter.packages.animations.example'
-    compileSdk flutter.compileSdkVersion
+    namespace = "dev.flutter.packages.animations.example"
+    compileSdk = flutter.compileSdkVersion
+    ndkVersion = flutter.ndkVersion
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_11
@@ -42,36 +23,21 @@ android {
         main.java.srcDirs += 'src/main/kotlin'
     }
 
-
     defaultConfig {
-        applicationId "dev.flutter.packages.animations.example"
-        minSdkVersion flutter.minSdkVersion
-        targetSdkVersion 32
-        versionCode flutterVersionCode.toInteger()
-        versionName flutterVersionName
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        applicationId = "dev.flutter.packages.animations.example"
+        minSdk = flutter.minSdkVersion
+        targetSdk = flutter.targetSdkVersion
+        versionCode = flutter.versionCode
+        versionName = flutter.versionName
     }
 
     buildTypes {
         release {
-            // TODO: Add your own signing config for the release build.
-            // Signing with the debug keys for now, so `flutter run --release` works.
-            signingConfig signingConfigs.debug
+            signingConfig = signingConfigs.debug
         }
     }
-    namespace 'dev.flutter.packages.animations.example'
-    lint {
-        disable 'InvalidPackage'
-    }
 }
 
 flutter {
-    source '../..'
-}
-
-dependencies {
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'androidx.test:runner:1.1.1'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+    source = "../.."
 }
diff --git a/packages/animations/example/android/app/src/debug/AndroidManifest.xml b/packages/animations/example/android/app/src/debug/AndroidManifest.xml
index f880684a6a9c..399f6981d5d3 100644
--- a/packages/animations/example/android/app/src/debug/AndroidManifest.xml
+++ b/packages/animations/example/android/app/src/debug/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Flutter needs it to communicate with the running application
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
diff --git a/packages/animations/example/android/app/src/main/AndroidManifest.xml b/packages/animations/example/android/app/src/main/AndroidManifest.xml
index 3adb2679c59a..74a78b939e5e 100644
--- a/packages/animations/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/animations/example/android/app/src/main/AndroidManifest.xml
@@ -1,15 +1,25 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
         android:label="example"
+        android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
         <activity
             android:name=".MainActivity"
+            android:exported="true"
             android:launchMode="singleTop"
+            android:taskAffinity=""
             android:theme="@style/LaunchTheme"
-            android:exported="true"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:hardwareAccelerated="true"
             android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -21,4 +31,15 @@
             android:name="flutterEmbedding"
             android:value="2" />
     </application>
+    <!-- Required to query activities that can process text, see:
+         https://developer.android.com/training/package-visibility and
+         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
+
+         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.PROCESS_TEXT"/>
+            <data android:mimeType="text/plain"/>
+        </intent>
+    </queries>
 </manifest>
diff --git a/packages/animations/example/android/app/src/main/kotlin/dev/flutter/packages/animations/example/MainActivity.kt b/packages/animations/example/android/app/src/main/kotlin/dev/flutter/packages/animations/example/MainActivity.kt
index 98673b425d1f..7bc2b2626200 100644
--- a/packages/animations/example/android/app/src/main/kotlin/dev/flutter/packages/animations/example/MainActivity.kt
+++ b/packages/animations/example/android/app/src/main/kotlin/dev/flutter/packages/animations/example/MainActivity.kt
@@ -1,8 +1,9 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 package dev.flutter.packages.animations.example
 
 import io.flutter.embedding.android.FlutterActivity
 
-class MainActivity : FlutterActivity() {}
+class MainActivity : FlutterActivity()
diff --git a/packages/animations/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/animations/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000000..f74085f3f6a2
--- /dev/null
+++ b/packages/animations/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:colorBackground" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>
diff --git a/packages/animations/example/android/app/src/main/res/values-night/styles.xml b/packages/animations/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000000..06952be745f9
--- /dev/null
+++ b/packages/animations/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>
diff --git a/packages/animations/example/android/app/src/main/res/values/styles.xml b/packages/animations/example/android/app/src/main/res/values/styles.xml
index 00fa4417cfbe..cb1ef88056ed 100644
--- a/packages/animations/example/android/app/src/main/res/values/styles.xml
+++ b/packages/animations/example/android/app/src/main/res/values/styles.xml
@@ -1,8 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
         <!-- Show a splash screen on the activity. Automatically removed when
-             Flutter draws its first frame -->
+             the Flutter engine draws its first frame -->
         <item name="android:windowBackground">@drawable/launch_background</item>
     </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
 </resources>
diff --git a/packages/animations/example/android/app/src/profile/AndroidManifest.xml b/packages/animations/example/android/app/src/profile/AndroidManifest.xml
index f880684a6a9c..399f6981d5d3 100644
--- a/packages/animations/example/android/app/src/profile/AndroidManifest.xml
+++ b/packages/animations/example/android/app/src/profile/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Flutter needs it to communicate with the running application
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
diff --git a/packages/animations/example/android/build.gradle b/packages/animations/example/android/build.gradle
index 4a9ad584b421..c3c4e403ee38 100644
--- a/packages/animations/example/android/build.gradle
+++ b/packages/animations/example/android/build.gradle
@@ -1,16 +1,3 @@
-buildscript {
-    ext.kotlin_version = '1.9.0'
-    repositories {
-        google()
-        mavenCentral()
-    }
-
-    dependencies {
-        classpath 'com.android.tools.build:gradle:8.5.1'
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-    }
-}
-
 allprojects {
     repositories {
         // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info.
@@ -24,12 +11,12 @@ allprojects {
     }
 }
 
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
 subprojects {
     project.buildDir = "${rootProject.buildDir}/${project.name}"
 }
 subprojects {
-    project.evaluationDependsOn(':app')
+    project.evaluationDependsOn(":app")
 }
 
 tasks.register("clean", Delete) {
diff --git a/packages/animations/example/android/gradle.properties b/packages/animations/example/android/gradle.properties
index 94adc3a3f97a..259717082164 100644
--- a/packages/animations/example/android/gradle.properties
+++ b/packages/animations/example/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
 android.useAndroidX=true
 android.enableJetifier=true
diff --git a/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties
index 7aeeb11c6ee5..3c85cfe057a1 100644
--- a/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,3 @@
-#Fri Jun 23 08:50:38 CEST 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/animations/example/android/settings.gradle b/packages/animations/example/android/settings.gradle
index 32735a3cfd4b..c223212e4e05 100644
--- a/packages/animations/example/android/settings.gradle
+++ b/packages/animations/example/android/settings.gradle
@@ -1,28 +1,28 @@
-include ':app'
+pluginManagement {
+    def flutterSdkPath = {
+        def properties = new Properties()
+        file("local.properties").withInputStream { properties.load(it) }
+        def flutterSdkPath = properties.getProperty("flutter.sdk")
+        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+        return flutterSdkPath
+    }()
 
-def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
 
-def plugins = new Properties()
-def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
-if (pluginsFile.exists()) {
-    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }
 }
 
-plugins.each { name, path ->
-    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
-    include ":$name"
-    project(":$name").projectDir = pluginDirectory
-}
 
 // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info.
-buildscript {
-  repositories {
-    maven {
-      url "https://plugins.gradle.org/m2/"
-    }
-  }
-  dependencies {
-    classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1"
-  }
+plugins {
+    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+    id "com.android.application" version "8.5.1" apply false
+    id "org.jetbrains.kotlin.android" version "1.9.0" apply false
+    id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1"
 }
-apply plugin: "com.google.cloud.artifactregistry.gradle-plugin"
+
+include ":app"
diff --git a/script/tool/lib/src/gradle_check_command.dart b/script/tool/lib/src/gradle_check_command.dart
index ab6545054c31..97f818015a28 100644
--- a/script/tool/lib/src/gradle_check_command.dart
+++ b/script/tool/lib/src/gradle_check_command.dart
@@ -327,12 +327,16 @@ plugins {
   /// compatibility with apps that use AGP 8+.
   bool _validateNamespace(RepositoryPackage package, String gradleContents,
       {required bool isExample}) {
-    final RegExp namespaceRegex =
-        RegExp('^\\s*namespace\\s+[\'"](.*?)[\'"]', multiLine: true);
-    final RegExpMatch? namespaceMatch =
-        namespaceRegex.firstMatch(gradleContents);
-
-    if (namespaceMatch == null) {
+    // Regex to validate that either of the following namespace definitions
+    // are found (where the single quotes can be single or double):
+    //  - namespace 'dev.flutter.foo'
+    //  - namespace = 'dev.flutter.foo'
+    final RegExp nameSpaceRegex =
+        RegExp('^\\s*namespace\\s+=?\\s*[\'"](.*?)[\'"]', multiLine: true);
+    final RegExpMatch? nameSpaceRegexMatch =
+        nameSpaceRegex.firstMatch(gradleContents);
+
+    if (nameSpaceRegexMatch == null) {
       const String errorMessage = '''
 build.gradle must set a "namespace":
 
@@ -350,7 +354,7 @@ https://developer.android.com/build/publish-library/prep-lib-release#choose-name
       return false;
     } else {
       return _validateNamespaceMatchesManifest(package,
-          isExample: isExample, namespace: namespaceMatch.group(1)!);
+          isExample: isExample, namespace: nameSpaceRegexMatch.group(1)!);
     }
   }
 
diff --git a/script/tool/test/gradle_check_command_test.dart b/script/tool/test/gradle_check_command_test.dart
index 457f3f40b02b..6c1a661e7c9c 100644
--- a/script/tool/test/gradle_check_command_test.dart
+++ b/script/tool/test/gradle_check_command_test.dart
@@ -255,6 +255,7 @@ include ":app"
     RepositoryPackage package, {
     required bool includeNamespace,
     required bool commentNamespace,
+    required bool includeNameSpaceAsDeclaration,
   }) {
     final File buildGradle = package
         .platformDirectory(FlutterPlatform.android)
@@ -263,7 +264,7 @@ include ":app"
     buildGradle.createSync(recursive: true);
 
     final String namespace =
-        "${commentNamespace ? '// ' : ''}namespace '$_defaultFakeNamespace'";
+        "${commentNamespace ? '// ' : ''}namespace ${includeNameSpaceAsDeclaration ? '= ' : ''}'$_defaultFakeNamespace'";
     buildGradle.writeAsStringSync('''
 def flutterRoot = localProperties.getProperty('flutter.sdk')
 if (flutterRoot == null) {
@@ -303,6 +304,7 @@ dependencies {
     required String pluginName,
     bool includeNamespace = true,
     bool commentNamespace = false,
+    bool includeNameSpaceAsDeclaration = false,
     bool warningsConfigured = true,
     String? kotlinVersion,
     bool includeBuildArtifactHub = true,
@@ -317,7 +319,9 @@ dependencies {
       includeArtifactHub: includeBuildArtifactHub,
     );
     writeFakeExampleAppBuildGradle(package,
-        includeNamespace: includeNamespace, commentNamespace: commentNamespace);
+        includeNamespace: includeNamespace,
+        commentNamespace: commentNamespace,
+        includeNameSpaceAsDeclaration: includeNameSpaceAsDeclaration);
     writeFakeExampleTopLevelSettingsGradle(
       package,
       includeArtifactHub: includeSettingsArtifactHub,
@@ -330,6 +334,7 @@ dependencies {
     required String pluginName,
     bool includeNamespace = true,
     bool commentNamespace = false,
+    bool includeNameSpaceAsDeclaration = false,
     bool warningsConfigured = true,
     String? kotlinVersion,
     required bool includeBuildArtifactHub,
@@ -344,7 +349,9 @@ dependencies {
       includeArtifactHub: includeBuildArtifactHub,
     );
     writeFakeExampleAppBuildGradle(package,
-        includeNamespace: includeNamespace, commentNamespace: commentNamespace);
+        includeNamespace: includeNamespace,
+        commentNamespace: commentNamespace,
+        includeNameSpaceAsDeclaration: includeNameSpaceAsDeclaration);
     writeFakeExampleSettingsGradle(
       package,
       includeArtifactHub: includeSettingsArtifactHub,
@@ -659,6 +666,30 @@ dependencies {
     );
   });
 
+  test('passes when namespace is declared with "=" declaration', () async {
+    const String pluginName = 'a_plugin';
+    final RepositoryPackage package = createFakePlugin(pluginName, packagesDir);
+    writeFakePluginBuildGradle(package, includeLanguageVersion: true);
+    writeFakeManifest(package);
+    final RepositoryPackage example = package.getExamples().first;
+    writeFakeExampleBuildGradles(example,
+        pluginName: pluginName, includeNameSpaceAsDeclaration: true);
+    writeFakeManifest(example, isApp: true);
+
+    final List<String> output = await runCapturingPrint(
+        runner, <String>['gradle-check'], errorHandler: (Error e) {
+      print((e as ToolExit).stackTrace);
+    });
+
+    expect(
+        output,
+        containsAllInOrder(
+          <Matcher>[
+            contains('Validating android/app/build.gradle'),
+          ],
+        ));
+  });
+
   test('fails if gradle-driven lint-warnings-as-errors is missing', () async {
     const String pluginName = 'a_plugin';
     final RepositoryPackage plugin =