Support host android apps with kts
gradle files for add to app (#156502)
Allows applying of `include_flutter.groovy` via the `apply from:` syntax, which allows using a host app that is using the Gradle Kotlin DSL (the default these days when creating an Android app in AS). Explanation: The `include_flutter.groovy` script is currently not able to be called by Kotlin gradle files, because it is [intended to be invoked with the following lines](https://docs.flutter.dev/add-to-app/android/project-setup#depend-on-the-modules-source-code): ``` setBinding(new Binding([gradle: this])) // new evaluate(new File( // new settingsDir.parentFile, // new 'flutter_module/.android/include_flutter.groovy' // new )) ``` `setBinding` isn't part of the Kotlin gradle DSL, and there isn't (that I can find) an easy Kotlin equivalent. If this binding isn't set, the reference to `gradle` in `include_flutter.groovy` is wrong, which breaks the script. This PR modifies `include_flutter.groovy` to also support being invoked through the standard way of invoking a script via the Gradle Groovy/Kotlin DSLs, which is `apply from:` (or it's slightly different Kotlin syntax). The start of the script identifies which of the two approaches is being used by checking if the binding is set, and then initializes some variables differently depending on the case. If we land this, I believe we should update the example Gradle files for both the `kts` and `groovy` cases to prefer the `apply from` syntax as I think this is the syntax most developers would be more familiar with already seeing in their Gradle files.
This commit is contained in:
parent
54bf532305
commit
d1d9954c45
74
.ci.yaml
74
.ci.yaml
@ -1001,7 +1001,8 @@ targets:
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Linux module_test
|
||||
- name: Linux build_android_host_app_with_module_aar
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
@ -1013,7 +1014,27 @@ targets:
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "linux"]
|
||||
task_name: module_test
|
||||
task_name: build_android_host_app_with_module_aar
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Linux build_android_host_app_with_module_source
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
dependencies: >-
|
||||
[
|
||||
{"dependency": "android_sdk", "version": "version:34v3"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:125.0.6422.141"},
|
||||
{"dependency": "open_jdk", "version": "version:17"}
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "linux"]
|
||||
task_name: build_android_host_app_with_module_source
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
@ -4067,7 +4088,8 @@ targets:
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Mac module_test
|
||||
- name: Mac build_android_host_app_with_module_aar
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
@ -4078,7 +4100,26 @@ targets:
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "mac"]
|
||||
task_name: module_test
|
||||
task_name: build_android_host_app_with_module_aar
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Mac build_android_host_app_with_module_source
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
dependencies: >-
|
||||
[
|
||||
{"dependency": "android_sdk", "version": "version:34v3"},
|
||||
{"dependency": "open_jdk", "version": "version:17"}
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "mac"]
|
||||
task_name: build_android_host_app_with_module_source
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
@ -5703,7 +5744,8 @@ targets:
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Windows module_test
|
||||
- name: Windows build_android_host_app_with_module_aar
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
@ -5715,7 +5757,27 @@ targets:
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "windows"]
|
||||
task_name: module_test
|
||||
task_name: build_android_host_app_with_module_aar
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
- bin/**
|
||||
- .ci.yaml
|
||||
|
||||
- name: Windows build_android_host_app_with_module_source
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
properties:
|
||||
dependencies: >-
|
||||
[
|
||||
{"dependency": "android_sdk", "version": "version:34v3"},
|
||||
{"dependency": "chrome_and_driver", "version": "version:125.0.6422.141"},
|
||||
{"dependency": "open_jdk", "version": "version:17"}
|
||||
]
|
||||
tags: >
|
||||
["devicelab", "hostonly", "windows"]
|
||||
task_name: build_android_host_app_with_module_source
|
||||
runIf:
|
||||
- dev/**
|
||||
- packages/flutter_tools/**
|
||||
|
@ -265,7 +265,8 @@
|
||||
/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart @andrewkolos @flutter/tool
|
||||
/dev/devicelab/bin/tasks/module_custom_host_app_name_test.dart @andrewkolos @flutter/tool
|
||||
/dev/devicelab/bin/tasks/module_host_with_custom_build_test.dart @andrewkolos @flutter/tool
|
||||
/dev/devicelab/bin/tasks/module_test.dart @andrewkolos @flutter/tool
|
||||
/dev/devicelab/bin/tasks/build_android_host_app_with_module_aar.dart @andrewkolos @flutter/tool
|
||||
/dev/devicelab/bin/tasks/build_android_host_app_with_module_source.dart @gmackall @flutter/android
|
||||
/dev/devicelab/bin/tasks/module_test_ios.dart @jmagman @flutter/tool
|
||||
/dev/devicelab/bin/tasks/native_assets_ios_simulator.dart @dcharkes @flutter/ios
|
||||
/dev/devicelab/bin/tasks/native_ui_tests_macos.dart @cbracken @flutter/desktop
|
||||
|
@ -35,16 +35,15 @@ TaskFunction combine(List<TaskFunction> tasks) {
|
||||
/// Tests that the Flutter module project template works and supports
|
||||
/// adding Flutter to an existing Android app.
|
||||
class ModuleTest {
|
||||
ModuleTest(
|
||||
this.buildTarget, {
|
||||
ModuleTest({
|
||||
this.gradleVersion = '7.6.3',
|
||||
});
|
||||
|
||||
final String buildTarget;
|
||||
static const String buildTarget = 'module-gradle';
|
||||
final String gradleVersion;
|
||||
|
||||
Future<TaskResult> call() async {
|
||||
section('Running: $buildTarget');
|
||||
section('Running: $buildTarget-$gradleVersion');
|
||||
section('Find Java');
|
||||
|
||||
final String? javaHome = await findJavaHome();
|
||||
@ -228,6 +227,7 @@ class ModuleTest {
|
||||
flutterDirectory.path,
|
||||
'dev',
|
||||
'integration_tests',
|
||||
'pure_android_host_apps',
|
||||
'android_host_app_v2_embedding',
|
||||
),
|
||||
),
|
||||
@ -449,7 +449,7 @@ class ModuleTest {
|
||||
Future<void> main() async {
|
||||
await task(combine(<TaskFunction>[
|
||||
// ignore: avoid_redundant_argument_values
|
||||
ModuleTest('module-gradle-7.6', gradleVersion: '8.4').call,
|
||||
ModuleTest('module-gradle-7.6', gradleVersion: '8.4-rc-3').call,
|
||||
ModuleTest(gradleVersion: '8.4').call,
|
||||
ModuleTest(gradleVersion: '8.4-rc-3').call,
|
||||
]));
|
||||
}
|
@ -0,0 +1,430 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:flutter_devicelab/framework/apk_utils.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
|
||||
final String gradlewExecutable =
|
||||
Platform.isWindows ? '.\\$gradlew' : './$gradlew';
|
||||
final String fileReadWriteMode = Platform.isWindows ? 'rw-rw-rw-' : 'rw-r--r--';
|
||||
final String platformLineSep = Platform.isWindows ? '\r\n' : '\n';
|
||||
|
||||
/// Combines several TaskFunctions with trivial success value into one.
|
||||
TaskFunction combine(List<TaskFunction> tasks) {
|
||||
return () async {
|
||||
for (final TaskFunction task in tasks) {
|
||||
final TaskResult result = await task();
|
||||
if (result.failed) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return TaskResult.success(null);
|
||||
};
|
||||
}
|
||||
|
||||
/// Tests that the Flutter module project template works and supports
|
||||
/// adding Flutter to an existing Android app.
|
||||
class ModuleTest {
|
||||
ModuleTest({
|
||||
this.gradleVersion = '7.6.3',
|
||||
});
|
||||
|
||||
static const String buildTarget = 'module-gradle';
|
||||
final String gradleVersion;
|
||||
|
||||
Future<TaskResult> call() async {
|
||||
section('Running: $buildTarget-$gradleVersion');
|
||||
section('Find Java');
|
||||
|
||||
final String? javaHome = await findJavaHome();
|
||||
if (javaHome == null) {
|
||||
return TaskResult.failure('Could not find Java');
|
||||
}
|
||||
print('\nUsing JAVA_HOME=$javaHome');
|
||||
|
||||
section('Create Flutter module project');
|
||||
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
|
||||
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
|
||||
try {
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>['--org', 'io.flutter.devicelab', '--template=module', 'hello'],
|
||||
);
|
||||
});
|
||||
|
||||
section('Create package with native assets');
|
||||
|
||||
await flutter(
|
||||
'config',
|
||||
options: <String>['--enable-native-assets'],
|
||||
);
|
||||
|
||||
const String ffiPackageName = 'ffi_package';
|
||||
await createFfiPackage(ffiPackageName, tempDir);
|
||||
|
||||
section('Add FFI package');
|
||||
|
||||
final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
|
||||
String content = await pubspec.readAsString();
|
||||
content = content.replaceFirst(
|
||||
'dependencies:$platformLineSep',
|
||||
'dependencies:$platformLineSep $ffiPackageName:$platformLineSep path: ..${Platform.pathSeparator}$ffiPackageName$platformLineSep',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'packages',
|
||||
options: <String>['get'],
|
||||
);
|
||||
});
|
||||
|
||||
section('Add read-only asset');
|
||||
|
||||
final File readonlyTxtAssetFile = await File(path.join(
|
||||
projectDir.path,
|
||||
'assets',
|
||||
'read-only.txt'
|
||||
))
|
||||
.create(recursive: true);
|
||||
|
||||
if (!exists(readonlyTxtAssetFile)) {
|
||||
return TaskResult.failure('Failed to create read-only asset');
|
||||
}
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
await exec('chmod', <String>[
|
||||
'444',
|
||||
readonlyTxtAssetFile.path,
|
||||
]);
|
||||
}
|
||||
|
||||
content = content.replaceFirst(
|
||||
'$platformLineSep # assets:$platformLineSep',
|
||||
'$platformLineSep assets:$platformLineSep - assets/read-only.txt$platformLineSep',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
|
||||
section('Add plugins');
|
||||
|
||||
content = content.replaceFirst(
|
||||
'${platformLineSep}dependencies:$platformLineSep',
|
||||
'${platformLineSep}dependencies:$platformLineSep',
|
||||
);
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'packages',
|
||||
options: <String>['get'],
|
||||
);
|
||||
});
|
||||
|
||||
section('Build ephemeral host app');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['apk'],
|
||||
);
|
||||
});
|
||||
|
||||
final bool ephemeralHostApkBuilt = exists(File(path.join(
|
||||
projectDir.path,
|
||||
'build',
|
||||
'host',
|
||||
'outputs',
|
||||
'apk',
|
||||
'release',
|
||||
'app-release.apk',
|
||||
)));
|
||||
|
||||
if (!ephemeralHostApkBuilt) {
|
||||
return TaskResult.failure('Failed to build ephemeral host .apk');
|
||||
}
|
||||
|
||||
section('Clean build');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter('clean');
|
||||
});
|
||||
|
||||
section('Make Android host app editable');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'make-host-app-editable',
|
||||
options: <String>['android'],
|
||||
);
|
||||
});
|
||||
|
||||
section('Build editable host app');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['apk'],
|
||||
);
|
||||
});
|
||||
|
||||
final bool editableHostApkBuilt = exists(File(path.join(
|
||||
projectDir.path,
|
||||
'build',
|
||||
'host',
|
||||
'outputs',
|
||||
'apk',
|
||||
'release',
|
||||
'app-release.apk',
|
||||
)));
|
||||
|
||||
if (!editableHostApkBuilt) {
|
||||
return TaskResult.failure('Failed to build editable host .apk');
|
||||
}
|
||||
|
||||
section('Add to existing Android app');
|
||||
|
||||
final Directory hostApp = Directory(path.join(tempDir.path, 'hello_host_app'));
|
||||
mkdir(hostApp);
|
||||
recursiveCopy(
|
||||
Directory(
|
||||
path.join(
|
||||
flutterDirectory.path,
|
||||
'dev',
|
||||
'integration_tests',
|
||||
'pure_android_host_apps',
|
||||
'host_app_kotlin_gradle_dsl',
|
||||
),
|
||||
),
|
||||
hostApp,
|
||||
);
|
||||
copy(
|
||||
File(path.join(projectDir.path, '.android', gradlew)),
|
||||
hostApp,
|
||||
);
|
||||
copy(
|
||||
File(path.join(projectDir.path, '.android', 'gradle', 'wrapper',
|
||||
'gradle-wrapper.jar')),
|
||||
Directory(path.join(hostApp.path, 'gradle', 'wrapper')),
|
||||
);
|
||||
|
||||
// Modify gradle version to passed in version.
|
||||
// This is somehow the wrong file.
|
||||
final File gradleWrapperProperties = File(path.join(
|
||||
hostApp.path, 'gradle', 'wrapper', 'gradle-wrapper.properties'));
|
||||
String propertyContent = await gradleWrapperProperties.readAsString();
|
||||
propertyContent = propertyContent.replaceFirst(
|
||||
'REPLACEME',
|
||||
gradleVersion,
|
||||
);
|
||||
section(propertyContent);
|
||||
await gradleWrapperProperties.writeAsString(propertyContent, flush: true);
|
||||
|
||||
final File analyticsOutputFile =
|
||||
File(path.join(tempDir.path, 'analytics.log'));
|
||||
|
||||
section('Build debug host APK');
|
||||
|
||||
await inDirectory(hostApp, () async {
|
||||
if (!Platform.isWindows) {
|
||||
await exec('chmod', <String>['+x', 'gradlew']);
|
||||
}
|
||||
await exec(gradlewExecutable,
|
||||
<String>['app:assembleDebug'],
|
||||
environment: <String, String>{
|
||||
'JAVA_HOME': javaHome,
|
||||
'FLUTTER_ANALYTICS_LOG_FILE': analyticsOutputFile.path,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
section('Check debug APK exists');
|
||||
|
||||
final String debugHostApk = path.join(
|
||||
hostApp.path,
|
||||
'app',
|
||||
'build',
|
||||
'outputs',
|
||||
'apk',
|
||||
'debug',
|
||||
'app-debug.apk',
|
||||
);
|
||||
if (!exists(File(debugHostApk))) {
|
||||
return TaskResult.failure('Failed to build debug host APK');
|
||||
}
|
||||
|
||||
section('Check files in debug APK');
|
||||
|
||||
checkCollectionContains<String>(<String>[
|
||||
...flutterAssets,
|
||||
...debugAssets,
|
||||
...baseApkFiles,
|
||||
'lib/arm64-v8a/lib$ffiPackageName.so',
|
||||
'lib/armeabi-v7a/lib$ffiPackageName.so',
|
||||
], await getFilesInApk(debugHostApk));
|
||||
|
||||
section('Check debug AndroidManifest.xml');
|
||||
|
||||
final String androidManifestDebug = await getAndroidManifest(debugHostApk);
|
||||
if (!androidManifestDebug.contains('''
|
||||
<meta-data
|
||||
android:name="flutterProjectType"
|
||||
android:value="module" />''')
|
||||
) {
|
||||
return TaskResult.failure("Debug host APK doesn't contain metadata: flutterProjectType = module ");
|
||||
}
|
||||
|
||||
final String analyticsOutput = analyticsOutputFile.readAsStringSync();
|
||||
if (!analyticsOutput.contains('cd24: android')
|
||||
|| !analyticsOutput.contains('cd25: true')
|
||||
|| !analyticsOutput.contains('viewName: assemble')) {
|
||||
return TaskResult.failure(
|
||||
'Building outer app produced the following analytics: "$analyticsOutput" '
|
||||
'but not the expected strings: "cd24: android", "cd25: true" and '
|
||||
'"viewName: assemble"'
|
||||
);
|
||||
}
|
||||
|
||||
section('Check file access modes for read-only asset from Flutter module');
|
||||
|
||||
final String readonlyDebugAssetFilePath = path.joinAll(<String>[
|
||||
hostApp.path,
|
||||
'app',
|
||||
'build',
|
||||
'intermediates',
|
||||
'assets',
|
||||
'debug',
|
||||
'mergeDebugAssets',
|
||||
'flutter_assets',
|
||||
'assets',
|
||||
'read-only.txt',
|
||||
]);
|
||||
final File readonlyDebugAssetFile = File(readonlyDebugAssetFilePath);
|
||||
if (!exists(readonlyDebugAssetFile)) {
|
||||
return TaskResult.failure('Failed to copy read-only asset file');
|
||||
}
|
||||
|
||||
String modes = readonlyDebugAssetFile.statSync().modeString();
|
||||
print('\nread-only.txt file access modes = $modes');
|
||||
if (modes.compareTo(fileReadWriteMode) != 0) {
|
||||
return TaskResult.failure('Failed to make assets user-readable and writable');
|
||||
}
|
||||
|
||||
section('Build release host APK');
|
||||
|
||||
await inDirectory(hostApp, () async {
|
||||
await exec(gradlewExecutable,
|
||||
<String>['app:assembleRelease'],
|
||||
environment: <String, String>{
|
||||
'JAVA_HOME': javaHome,
|
||||
'FLUTTER_ANALYTICS_LOG_FILE': analyticsOutputFile.path,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
final String releaseHostApk = path.join(
|
||||
hostApp.path,
|
||||
'app',
|
||||
'build',
|
||||
'outputs',
|
||||
'apk',
|
||||
'release',
|
||||
'app-release-unsigned.apk',
|
||||
);
|
||||
if (!exists(File(releaseHostApk))) {
|
||||
return TaskResult.failure('Failed to build release host APK');
|
||||
}
|
||||
|
||||
section('Check files in release APK');
|
||||
|
||||
checkCollectionContains<String>(<String>[
|
||||
...flutterAssets,
|
||||
...baseApkFiles,
|
||||
'lib/arm64-v8a/lib$ffiPackageName.so',
|
||||
'lib/arm64-v8a/libapp.so',
|
||||
'lib/arm64-v8a/libflutter.so',
|
||||
'lib/armeabi-v7a/lib$ffiPackageName.so',
|
||||
'lib/armeabi-v7a/libapp.so',
|
||||
'lib/armeabi-v7a/libflutter.so',
|
||||
], await getFilesInApk(releaseHostApk));
|
||||
|
||||
section('Check the NOTICE file is correct');
|
||||
|
||||
await inDirectory(hostApp, () async {
|
||||
final File apkFile = File(releaseHostApk);
|
||||
final Archive apk = ZipDecoder().decodeBytes(apkFile.readAsBytesSync());
|
||||
// Shouldn't be missing since we already checked it exists above.
|
||||
final ArchiveFile? noticesFile = apk.findFile('assets/flutter_assets/NOTICES.Z');
|
||||
|
||||
final Uint8List? licenseData = noticesFile?.content as Uint8List?;
|
||||
if (licenseData == null) {
|
||||
return TaskResult.failure('Invalid license file.');
|
||||
}
|
||||
final String licenseString = utf8.decode(gzip.decode(licenseData));
|
||||
if (!licenseString.contains('skia') || !licenseString.contains('Flutter Authors')) {
|
||||
return TaskResult.failure('License content missing.');
|
||||
}
|
||||
});
|
||||
|
||||
section('Check release AndroidManifest.xml');
|
||||
|
||||
final String androidManifestRelease = await getAndroidManifest(debugHostApk);
|
||||
if (!androidManifestRelease.contains('''
|
||||
<meta-data
|
||||
android:name="flutterProjectType"
|
||||
android:value="module" />''')
|
||||
) {
|
||||
return TaskResult.failure("Release host APK doesn't contain metadata: flutterProjectType = module ");
|
||||
}
|
||||
|
||||
section('Check file access modes for read-only asset from Flutter module');
|
||||
|
||||
final String readonlyReleaseAssetFilePath = path.joinAll(<String>[
|
||||
hostApp.path,
|
||||
'app',
|
||||
'build',
|
||||
'intermediates',
|
||||
'assets',
|
||||
'release',
|
||||
'mergeReleaseAssets',
|
||||
'flutter_assets',
|
||||
'assets',
|
||||
'read-only.txt',
|
||||
]);
|
||||
final File readonlyReleaseAssetFile = File(readonlyReleaseAssetFilePath);
|
||||
if (!exists(readonlyReleaseAssetFile)) {
|
||||
return TaskResult.failure('Failed to copy read-only asset file');
|
||||
}
|
||||
|
||||
modes = readonlyReleaseAssetFile.statSync().modeString();
|
||||
print('\nread-only.txt file access modes = $modes');
|
||||
if (modes.compareTo(fileReadWriteMode) != 0) {
|
||||
return TaskResult.failure('Failed to make assets user-readable and writable');
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
} on TaskResult catch (taskResult) {
|
||||
return taskResult;
|
||||
} catch (e) {
|
||||
return TaskResult.failure(e.toString());
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
await task(combine(<TaskFunction>[
|
||||
// ignore: avoid_redundant_argument_values
|
||||
ModuleTest(gradleVersion: '8.7').call,
|
||||
]));
|
||||
}
|
@ -178,6 +178,7 @@ Future<void> main() async {
|
||||
flutterDirectory.path,
|
||||
'dev',
|
||||
'integration_tests',
|
||||
'pure_android_host_apps',
|
||||
'android_custom_host_app',
|
||||
),
|
||||
),
|
||||
|
2
dev/integration_tests/pure_android_host_apps/README.md
Normal file
2
dev/integration_tests/pure_android_host_apps/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This directory contains minimal Android apps used by integration tests for testing add-to-app
|
||||
use cases.
|
@ -0,0 +1,2 @@
|
||||
[*.{kt,kts}]
|
||||
ktlint = disabled
|
15
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/.gitignore
vendored
Normal file
15
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
@ -0,0 +1,2 @@
|
||||
This directory contains a minimal Android app that uses the Kotlin DSL for its Gradle files.
|
||||
It is used in add-to-app integration testing.
|
1
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/app/.gitignore
vendored
Normal file
1
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,70 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.myapplication"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.myapplication"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(project(":flutter"))
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
}
|
21
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/app/proguard-rules.pro
vendored
Normal file
21
dev/integration_tests/pure_android_host_apps/host_app_kotlin_gradle_dsl/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,24 @@
|
||||
package com.example.myapplication
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.example.myapplication", appContext.packageName)
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.MyApplication">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="io.flutter.embedding.android.FlutterActivity"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,55 @@
|
||||
package com.example.myapplication
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
MyApplicationTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Greeting(
|
||||
name = "Android",
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startActivity(
|
||||
FlutterActivity.createDefaultIntent(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(
|
||||
name: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
MyApplicationTheme {
|
||||
Greeting("Android")
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
@ -0,0 +1,58 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun MyApplicationTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography =
|
||||
Typography(
|
||||
bodyLarge =
|
||||
TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
@ -0,0 +1,7 @@
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">My Application</string>
|
||||
</resources>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="Theme.MyApplication" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2014 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. -->
|
||||
|
||||
<!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
@ -0,0 +1,17 @@
|
||||
package com.example.myapplication
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
@ -0,0 +1,30 @@
|
||||
[versions]
|
||||
agp = "8.5.2"
|
||||
kotlin = "1.9.0"
|
||||
coreKtx = "1.10.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
lifecycleRuntimeKtx = "2.6.1"
|
||||
activityCompose = "1.8.0"
|
||||
composeBom = "2024.04.01"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
@ -0,0 +1,6 @@
|
||||
#Mon Oct 14 12:37:17 PDT 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-REPLACEME-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
val flutterStorageUrl = System.getenv("FLUTTER_STORAGE_BASE_URL") ?: "https://storage.googleapis.com"
|
||||
maven("$flutterStorageUrl/download.flutter.io")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "My Application"
|
||||
include(":app")
|
||||
apply(from = File(settingsDir.parentFile.toString() + "/hello/.android/include_flutter.groovy"))
|
@ -1,5 +1,17 @@
|
||||
def scriptFile = getClass().protectionDomain.codeSource.location.toURI()
|
||||
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
|
||||
def gradle = null
|
||||
def flutterProjectRoot = null
|
||||
|
||||
// The second block handles the original syntax for including Flutter modules, which used a Groovy
|
||||
// method that isn't a part of the Kotlin Gradle DSL (setBinding). The first block handles the
|
||||
// preferred way of including Flutter modules, which is to use the apply from: Gradle syntax.
|
||||
if (!getBinding().getVariables().containsKey("gradle")) {
|
||||
gradle = this
|
||||
flutterProjectRoot = gradle.buildscript.getSourceFile().getParentFile().getParentFile().absolutePath
|
||||
} else {
|
||||
gradle = getBinding().getVariables().get("gradle")
|
||||
def scriptFile = getClass().protectionDomain.codeSource.location.toURI()
|
||||
flutterProjectRoot = new File(scriptFile).parentFile.parentFile.absolutePath
|
||||
}
|
||||
|
||||
gradle.include ":flutter"
|
||||
gradle.project(":flutter").projectDir = new File(flutterProjectRoot, ".android/Flutter")
|
||||
|
Loading…
x
Reference in New Issue
Block a user