diff --git a/dev/benchmarks/complex_layout/android/app/src/main/AndroidManifest.xml b/dev/benchmarks/complex_layout/android/app/src/main/AndroidManifest.xml index 8200a24452..f550d95c2a 100644 --- a/dev/benchmarks/complex_layout/android/app/src/main/AndroidManifest.xml +++ b/dev/benchmarks/complex_layout/android/app/src/main/AndroidManifest.xml @@ -7,8 +7,8 @@ found in the LICENSE file. --> - - + + diff --git a/dev/benchmarks/macrobenchmarks/android/app/src/main/AndroidManifest.xml b/dev/benchmarks/macrobenchmarks/android/app/src/main/AndroidManifest.xml index 643354e964..1f764191b6 100644 --- a/dev/benchmarks/macrobenchmarks/android/app/src/main/AndroidManifest.xml +++ b/dev/benchmarks/macrobenchmarks/android/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ found in the LICENSE file. --> additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - - + - + - + additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + + + diff --git a/dev/integration_tests/android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java b/dev/integration_tests/android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java index 325569c98e..e753f4eda6 100644 --- a/dev/integration_tests/android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java +++ b/dev/integration_tests/android_semantics_testing/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java @@ -16,13 +16,16 @@ import android.os.Bundle; import android.util.DisplayMetrics; import android.view.WindowManager; import android.content.Context; +import androidx.annotation.NonNull; -import io.flutter.app.FlutterActivity; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterView; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugins.GeneratedPluginRegistrant; -import io.flutter.view.FlutterView; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; @@ -30,10 +33,10 @@ import android.view.accessibility.AccessibilityNodeInfo; public class MainActivity extends FlutterActivity { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - new MethodChannel(getFlutterView(), "semantics").setMethodCallHandler(new SemanticsTesterMethodHandler()); + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "semantics") + .setMethodCallHandler(new SemanticsTesterMethodHandler()); } class SemanticsTesterMethodHandler implements MethodCallHandler { @@ -41,7 +44,7 @@ public class MainActivity extends FlutterActivity { @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { - FlutterView flutterView = getFlutterView(); + FlutterView flutterView = findViewById(FLUTTER_VIEW_ID); AccessibilityNodeProvider provider = flutterView.getAccessibilityNodeProvider(); DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); diff --git a/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml index 8387ccf580..b3427f9afe 100644 --- a/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ found in the LICENSE file. --> In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + + + diff --git a/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml index e1a37c0da7..664706aee1 100644 --- a/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/flavors/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ found in the LICENSE file. --> + + diff --git a/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java b/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java index 7e6d4fe711..1bdb38564a 100644 --- a/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java +++ b/dev/integration_tests/flavors/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java @@ -6,21 +6,19 @@ package com.yourcompany.flavors; import android.os.Bundle; -import io.flutter.app.FlutterActivity; +import io.flutter.embedding.android.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - new MethodChannel(getFlutterView(), "flavor").setMethodCallHandler(new MethodChannel.MethodCallHandler() { - @Override - public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { - result.success(BuildConfig.FLAVOR); - } - }); + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "flavor") + .setMethodCallHandler( + (call, result) -> { + result.success(BuildConfig.FLAVOR); + } + ); } } diff --git a/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/AndroidManifest.xml index e1a37c0da7..664706aee1 100644 --- a/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ found in the LICENSE file. --> + + diff --git a/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java b/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java index 24f93ca13d..488c7fb7e6 100644 --- a/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java +++ b/dev/integration_tests/gradle_deprecated_settings/android/app/src/main/java/com/yourcompany/flavors/MainActivity.java @@ -4,15 +4,6 @@ package com.yourcompany.flavors; -import android.os.Bundle; +import io.flutter.embedding.android.FlutterActivity; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} +public class MainActivity extends FlutterActivity {} diff --git a/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml index afc37761cd..0e3e9627e4 100644 --- a/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ found in the LICENSE file. --> additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + + + diff --git a/dev/integration_tests/platform_interaction/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java b/dev/integration_tests/platform_interaction/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java index c8bc0f9f87..d4ec979a53 100644 --- a/dev/integration_tests/platform_interaction/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java +++ b/dev/integration_tests/platform_interaction/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java @@ -6,16 +6,10 @@ package com.yourcompany.platforminteraction; import android.os.Bundle; -import io.flutter.app.FlutterActivity; +import io.flutter.embedding.android.FlutterActivity; import io.flutter.plugin.common.*; -import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } public void finish() { BasicMessageChannel channel = new BasicMessageChannel<>(getFlutterView(), "navigation-test", StringCodec.INSTANCE); diff --git a/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml index f93ecc4b9e..988e75c010 100644 --- a/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ found in the LICENSE file. --> additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> boolArg('ignore-deprecation') ? DeprecationBehavior.ignore : DeprecationBehavior.exit; + @override Future> get requiredArtifacts async => { DevelopmentArtifact.androidGenSnapshot, diff --git a/packages/flutter_tools/lib/src/commands/build_appbundle.dart b/packages/flutter_tools/lib/src/commands/build_appbundle.dart index 0cd619c7bf..40611f2a7b 100644 --- a/packages/flutter_tools/lib/src/commands/build_appbundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_appbundle.dart @@ -42,6 +42,7 @@ class BuildAppBundleCommand extends BuildSubCommand { usesAnalyzeSizeFlag(); addAndroidSpecificBuildOptions(hide: !verboseHelp); addMultidexOption(); + addIgnoreDeprecationOption(); argParser.addMultiOption('target-platform', splitCommas: true, defaultsTo: ['android-arm', 'android-arm64', 'android-x64'], @@ -72,6 +73,9 @@ class BuildAppBundleCommand extends BuildSubCommand { @override final String name = 'appbundle'; + @override + DeprecationBehavior get deprecationBehavior => boolArg('ignore-deprecation') ? DeprecationBehavior.ignore : DeprecationBehavior.exit; + @override Future> get requiredArtifacts async => { DevelopmentArtifact.androidGenSnapshot, diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 695541256a..01d34cea6a 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -251,6 +251,7 @@ class RunCommand extends RunCommandBase { // without needing to know the port. addPublishPort(verboseHelp: verboseHelp); addMultidexOption(); + addIgnoreDeprecationOption(); argParser ..addFlag('enable-software-rendering', negatable: false, @@ -342,6 +343,10 @@ class RunCommand extends RunCommandBase { @override final String name = 'run'; + @override + DeprecationBehavior get deprecationBehavior => boolArg('ignore-deprecation') ? DeprecationBehavior.ignore : _deviceDeprecationBehavior; + DeprecationBehavior _deviceDeprecationBehavior = DeprecationBehavior.none; + @override final String description = 'Run your Flutter app on an attached device.'; @@ -475,6 +480,10 @@ class RunCommand extends RunCommandBase { '--${FlutterOptions.kDeviceUser} is only supported for Android. At least one Android device is required.' ); } + + if (devices.any((Device device) => device is AndroidDevice)) { + _deviceDeprecationBehavior = DeprecationBehavior.exit; + } // Only support "web mode" with a single web device due to resident runner // refactoring required otherwise. webMode = featureFlags.isWebEnabled && diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index d13a9a698f..59ec19b3c5 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -20,6 +20,7 @@ import 'flutter_manifest.dart'; import 'flutter_plugins.dart'; import 'globals.dart' as globals; import 'platform_plugins.dart'; +import 'reporting/reporting.dart'; import 'template.dart'; import 'xcode_project.dart'; @@ -282,7 +283,7 @@ class FlutterProject { /// registrants for app and module projects only. /// /// Will not create project platform directories if they do not already exist. - Future regeneratePlatformSpecificTooling() async { + Future regeneratePlatformSpecificTooling({DeprecationBehavior deprecationBehavior = DeprecationBehavior.none}) async { return ensureReadyForPlatformSpecificTooling( androidPlatform: android.existsSync(), iosPlatform: ios.existsSync(), @@ -293,6 +294,7 @@ class FlutterProject { windowsPlatform: featureFlags.isWindowsEnabled && windows.existsSync(), webPlatform: featureFlags.isWebEnabled && web.existsSync(), winUwpPlatform: featureFlags.isWindowsUwpEnabled && windowsUwp.existsSync(), + deprecationBehavior: deprecationBehavior, ); } @@ -306,13 +308,14 @@ class FlutterProject { bool windowsPlatform = false, bool webPlatform = false, bool winUwpPlatform = false, + DeprecationBehavior deprecationBehavior = DeprecationBehavior.none, }) async { if (!directory.existsSync() || hasExampleApp || isPlugin) { return; } await refreshPluginsList(this, iosPlatform: iosPlatform, macOSPlatform: macOSPlatform); if (androidPlatform) { - await android.ensureReadyForPlatformSpecificTooling(); + await android.ensureReadyForPlatformSpecificTooling(deprecationBehavior: deprecationBehavior); } if (iosPlatform) { await ios.ensureReadyForPlatformSpecificTooling(); @@ -344,6 +347,12 @@ class FlutterProject { ); } + void checkForDeprecation({DeprecationBehavior deprecationBehavior = DeprecationBehavior.none}) { + if (android.existsSync()) { + android.checkForDeprecation(deprecationBehavior: deprecationBehavior); + } + } + /// Returns a json encoded string containing the [appName], [version], and [buildNumber] that is used to generate version.json String getVersionInfo() { final String? buildName = manifest.buildName; @@ -478,7 +487,7 @@ class AndroidProject extends FlutterProjectPlatform { return parent.directory.childDirectory('build'); } - Future ensureReadyForPlatformSpecificTooling() async { + Future ensureReadyForPlatformSpecificTooling({DeprecationBehavior deprecationBehavior = DeprecationBehavior.none}) async { if (isModule && _shouldRegenerateFromTemplate()) { await _regenerateLibrary(); // Add ephemeral host app, if an editable host app does not already exist. @@ -536,6 +545,41 @@ class AndroidProject extends FlutterProjectPlatform { ); } + void checkForDeprecation({DeprecationBehavior deprecationBehavior = DeprecationBehavior.none}) { + if (getEmbeddingVersion() == AndroidEmbeddingVersion.v1) { + globals.printStatus( +''' +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Warning +────────────────────────────────────────────────────────────────────────────── +Your Flutter application is created using an older version of the Android +embedding. It is being deprecated in favor of Android embedding v2. Follow the +steps at + +https://flutter.dev/go/android-project-migration + +to migrate your project. You may also pass the --ignore-deprecation flag to +ignore this check and continue with the deprecated v1 embedding. However, +the v1 Android embedding will be removed in future versions of Flutter. +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +''' + ); + switch (deprecationBehavior) { + case DeprecationBehavior.none: + break; + case DeprecationBehavior.ignore: + BuildEvent('deprecated-v1-android-embedding-ignored', type: 'gradle', flutterUsage: globals.flutterUsage).send(); + break; + case DeprecationBehavior.exit: + BuildEvent('deprecated-v1-android-embedding-failed', type: 'gradle', flutterUsage: globals.flutterUsage).send(); + throwToolExit( + 'Build failed due to use of deprecated Android v1 embedding.', + exitCode: 1, + ); + } + } + } + AndroidEmbeddingVersion getEmbeddingVersion() { if (isModule) { // A module type's Android project is used in add-to-app scenarios and @@ -555,6 +599,12 @@ class AndroidProject extends FlutterProjectPlatform { throwToolExit('Error reading $appManifestFile even though it exists. ' 'Please ensure that you have read permission to this file and try again.'); } + for (final XmlElement application in document.findAllElements('application')) { + final String? applicationName = application.getAttribute('android:name'); + if (applicationName == 'io.flutter.app.FlutterApplication') { + return AndroidEmbeddingVersion.v1; + } + } for (final XmlElement metaData in document.findAllElements('meta-data')) { final String? name = metaData.getAttribute('android:name'); if (name == 'flutterEmbedding') { @@ -579,6 +629,16 @@ enum AndroidEmbeddingVersion { v2, } +// What the tool should do when encountering deprecated API in applications. +enum DeprecationBehavior { + // The command being run does not care about deprecation status. + none, + // The command should continue and ignore the deprecation warning. + ignore, + // The command should exit the tool. + exit, +} + /// Represents the web sub-project of a Flutter project. class WebProject extends FlutterProjectPlatform { WebProject._(this.parent); diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 3ea9aaa019..bd526eeb64 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -181,6 +181,8 @@ abstract class FlutterCommand extends Command { bool _usesFatalWarnings = false; + DeprecationBehavior get deprecationBehavior => DeprecationBehavior.none; + bool get shouldRunPub => _usesPubOption && boolArg('pub'); bool get shouldUpdateCache => true; @@ -834,6 +836,15 @@ abstract class FlutterCommand extends Command { ); } + void addIgnoreDeprecationOption({ bool hide = false }) { + argParser.addFlag('ignore-deprecation', + negatable: false, + help: 'Indicates that the app should ignore deprecation warnings and continue to build ' + 'using deprecated APIs. Use of this flag may cause your app to fail to build when ' + 'deprecated APIs are removed.', + ); + } + /// Adds build options common to all of the desktop build commands. void addCommonDesktopBuildOptions({ required bool verboseHelp }) { addBuildModeFlags(verboseHelp: verboseHelp); @@ -1263,8 +1274,10 @@ abstract class FlutterCommand extends Command { await validateCommand(); + final FlutterProject project = FlutterProject.current(); + project.checkForDeprecation(deprecationBehavior: deprecationBehavior); + if (shouldRunPub) { - final FlutterProject project = FlutterProject.current(); final Environment environment = Environment( artifacts: globals.artifacts!, logger: globals.logger, diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart index 6ca5fc6acc..0c0791a7f4 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart @@ -14,12 +14,15 @@ import 'dart:async'; import 'package:file/file.dart'; import 'package:file/memory.dart'; +import 'package:flutter_tools/src/android/android_device.dart'; +import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/user_messages.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; @@ -217,6 +220,88 @@ void main() { Cache: () => Cache.test(processManager: FakeProcessManager.any()), }); + testUsingContext('fails when v1 FlutterApplication is detected', () async { + fs.file('pubspec.yaml').createSync(); + fs.file('android/AndroidManifest.xml') + ..createSync(recursive: true) + ..writeAsStringSync(''' + + + + + ''', flush: true); + fs.file('.packages').writeAsStringSync('\n'); + fs.file('lib/main.dart').createSync(recursive: true); + final AndroidDevice device = AndroidDevice('1234', + modelID: 'TestModel', + logger: testLogger, + platform: FakePlatform(), + androidSdk: FakeAndroidSdk(), + fileSystem: fs, + processManager: FakeProcessManager.any(), + ); + + mockDeviceManager + ..devices = [device] + ..targetDevices = [device]; + + final RunCommand command = RunCommand(); + await expectLater(createTestCommandRunner(command).run([ + 'run', + '--pub', + ]), throwsToolExit(message: 'Build failed due to use of deprecated Android v1 embedding.')); + }, overrides: { + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + DeviceManager: () => mockDeviceManager, + Stdio: () => FakeStdio(), + Cache: () => Cache.test(processManager: FakeProcessManager.any()), + }); + + testUsingContext('fails when v1 metadata is detected', () async { + fs.file('pubspec.yaml').createSync(); + fs.file('android/AndroidManifest.xml') + ..createSync(recursive: true) + ..writeAsStringSync(''' + + + + + + ''', flush: true); + fs.file('.packages').writeAsStringSync('\n'); + fs.file('lib/main.dart').createSync(recursive: true); + final AndroidDevice device = AndroidDevice('1234', + modelID: 'TestModel', + logger: testLogger, + platform: FakePlatform(), + androidSdk: FakeAndroidSdk(), + fileSystem: fs, + processManager: FakeProcessManager.any(), + ); + + mockDeviceManager + ..devices = [device] + ..targetDevices = [device]; + + final RunCommand command = RunCommand(); + await expectLater(createTestCommandRunner(command).run([ + 'run', + '--pub', + ]), throwsToolExit(message: 'Build failed due to use of deprecated Android v1 embedding.')); + }, overrides: { + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + DeviceManager: () => mockDeviceManager, + Stdio: () => FakeStdio(), + Cache: () => Cache.test(processManager: FakeProcessManager.any()), + }); + testUsingContext('shows unsupported devices when no supported devices are found', () async { final RunCommand command = RunCommand(); final FakeDevice mockDevice = FakeDevice(targetPlatform: TargetPlatform.android_arm, isLocalEmulator: true, sdkNameAndVersion: 'api-14'); @@ -535,6 +620,10 @@ class FakeDeviceManager extends Fake implements DeviceManager { } } +class FakeAndroidSdk extends Fake implements AndroidSdk { + @override + String get adbPath => 'adb'; +} class TestRunCommand extends RunCommand { @override diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 3915d7d4cb..76a9c8b11a 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -182,8 +182,29 @@ void main() { // v1 embedding, as opposed to having . - await project.regeneratePlatformSpecificTooling(); - expect(testLogger.warningText, contains('https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects')); + project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.none); + expect(testLogger.statusText, contains('https://flutter.dev/go/android-project-migration')); + }); + _testInMemory('Android project not on v2 embedding exits', () async { + final FlutterProject project = await someProject(); + // The default someProject with an empty already indicates + // v1 embedding, as opposed to having . + + await expectToolExitLater( + Future.sync(() => project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.exit)), + contains('Build failed due to use of deprecated Android v1 embedding.') + ); + expect(testLogger.statusText, contains('https://flutter.dev/go/android-project-migration')); + }); + _testInMemory('Android project not on v2 embedding ignore continues', () async { + final FlutterProject project = await someProject(); + // The default someProject with an empty already indicates + // v1 embedding, as opposed to having . + + project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.ignore); + expect(testLogger.statusText, contains('https://flutter.dev/go/android-project-migration')); }); _testInMemory('Android plugin without example app does not show a warning', () async { final FlutterProject project = await aPluginProject(); diff --git a/packages/integration_test/example/android/app/src/main/AndroidManifest.xml b/packages/integration_test/example/android/app/src/main/AndroidManifest.xml index 32833b71c9..9e0765f4e9 100644 --- a/packages/integration_test/example/android/app/src/main/AndroidManifest.xml +++ b/packages/integration_test/example/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ found in the LICENSE file. --> additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. -->