diff --git a/.github/workflows/cronet.yml b/.github/workflows/cronet.yml index 9cf9be4560..63e7b7d253 100644 --- a/.github/workflows/cronet.yml +++ b/.github/workflows/cronet.yml @@ -26,7 +26,10 @@ jobs: runs-on: macos-latest strategy: matrix: - package: ['cronet_http', 'cronet_http_embedded'] + cronetHttpNoPlay: ['false', 'true'] + defaults: + run: + working-directory: pkgs/cronet_http steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 @@ -36,23 +39,14 @@ jobs: - uses: subosito/flutter-action@2783a3f08e1baf891508463f8c6653c258246225 with: channel: 'stable' - - name: Make cronet_http_embedded copy - if: ${{ matrix.package == 'cronet_http_embedded' }} - run: | - mv pkgs/cronet_http pkgs/cronet_http_embedded - cd pkgs/cronet_http_embedded - flutter pub get && dart tool/prepare_for_embedded.dart - id: install name: Install dependencies - working-directory: 'pkgs/${{ matrix.package }}' run: flutter pub get - name: Check formatting if: always() && steps.install.outcome == 'success' - working-directory: 'pkgs/${{ matrix.package }}' run: dart format --output=none --set-exit-if-changed . - name: Analyze code if: always() && steps.install.outcome == 'success' - working-directory: 'pkgs/${{ matrix.package }}' run: flutter analyze --fatal-infos - name: Run tests uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 @@ -64,6 +58,6 @@ jobs: # - pkgs/cronet_http/example/android/app/build.gradle api-level: 21 arch: x86_64 - target: ${{ matrix.package == 'cronet_http_embedded' && 'default' || 'google_apis' }} + target: ${{ matrix.cronetHttpNoPlay == 'true' && 'default' || 'google_apis' }} profile: pixel - script: cd pkgs/${{ matrix.package }}/example && flutter test --timeout=1200s integration_test/ + script: cd pkgs/cronet_http/example && flutter test --dart-define=cronetHttpNoPlay=${{ matrix.cronetHttpNoPlay }} --timeout=1200s integration_test/ diff --git a/pkgs/cronet_http/CHANGELOG.md b/pkgs/cronet_http/CHANGELOG.md index f8b134047d..30979ca249 100644 --- a/pkgs/cronet_http/CHANGELOG.md +++ b/pkgs/cronet_http/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0-wip + +* Support the Cronet embedding dependency with `--dart-define=cronetHttpNoPlay=true`. + ## 1.1.1 * Make it possible to construct `CronetClient` with custom a `CronetEngine` diff --git a/pkgs/cronet_http/README.md b/pkgs/cronet_http/README.md index e53e54d19e..e1ff6d2f15 100644 --- a/pkgs/cronet_http/README.md +++ b/pkgs/cronet_http/README.md @@ -2,22 +2,20 @@ [![package publisher](https://img.shields.io/pub/publisher/cronet_http.svg)](https://pub.dev/packages/cronet_http/publisher) An Android Flutter plugin that provides access to the -[Cronet][] -HTTP client. +[Cronet][] HTTP client. -Cronet is available as part of -[Google Play Services][]. +Cronet is available as part of [Google Play Services][] +and as [a standalone embedded library][]. -This package depends on [Google Play Services][] for its [Cronet][] -implementation. -[`package:cronet_http_embedded`](https://pub.dev/packages/cronet_http_embedded) -is functionally identical to this package but embeds [Cronet][] directly -instead of relying on [Google Play Services][]. +This package depends on [Google Play Services][] +for its [Cronet][] implementation. +To use the embedded version of [Cronet][] without [Google Play Services][], +see [Use embedded Cronet](#use-embedded-cronet). ## Motivation -Using [Cronet][], rather than the socket-based [dart:io HttpClient][] -implemententation, has several advantages: +Using [Cronet][], rather than the socket-based +[dart:io HttpClient][] implementation, has several advantages: 1. It automatically supports Android platform features such as HTTP proxies. 2. It supports configurable caching. @@ -40,23 +38,46 @@ void main() async { final Client httpClient; if (Platform.isAndroid) { final engine = CronetEngine.build( - cacheMode: CacheMode.memory, - cacheMaxSize: 2 * 1024 * 1024, - userAgent: 'Book Agent'); + cacheMode: CacheMode.memory, + cacheMaxSize: 2 * 1024 * 1024, + userAgent: 'Book Agent', + ); httpClient = CronetClient.fromCronetEngine(engine, isOwned: true); } else { httpClient = IOClient(HttpClient()..userAgent = 'Book Agent'); } - final response = await client.get(Uri.https( + final response = await client.get( + Uri.https( 'www.googleapis.com', '/books/v1/volumes', - {'q': 'HTTP', 'maxResults': '40', 'printType': 'books'})); + {'q': 'HTTP', 'maxResults': '40', 'printType': 'books'}, + ), + ); httpClient.close(); } ``` +### Use embedded Cronet + +If you want your application to work without [Google Play Services][], +you can instead depend on the `org.chromium.net:cronet-embedded` package +by using `dart-define` to set `cronetHttpNoPlay` is set to `true`. + +For example: + +``` +flutter run --dart-define=cronetHttpNoPlay=true +``` + +To use the embedded version in `flutter test`: + +``` +flutter test --dart-define=cronetHttpNoPlay=true +``` + [Cronet]: https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/package-summary -[dart:io HttpClient]: https://api.dart.dev/stable/dart-io/HttpClient-class.html [Google Play Services]: https://developers.google.com/android/guides/overview +[a standalone embedded library]: https://mvnrepository.com/artifact/org.chromium.net/cronet-embedded +[dart:io HttpClient]: https://api.dart.dev/stable/dart-io/HttpClient-class.html [package:http Client]: https://pub.dev/documentation/http/latest/http/Client-class.html diff --git a/pkgs/cronet_http/README_EMBEDDED.md b/pkgs/cronet_http/README_EMBEDDED.md deleted file mode 100644 index ebc84ded77..0000000000 --- a/pkgs/cronet_http/README_EMBEDDED.md +++ /dev/null @@ -1,12 +0,0 @@ -An Android Flutter plugin that provides access to the -[Cronet](https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/package-summary) -HTTP client. - -This package is identical to [`package:cronet_http`](https://pub.dev/packages/cronet_http) -except that it embeds -[Cronet](https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/package-summary) -rather than using the version included with -[Google Play Services](https://developers.google.com/android/guides/overview). -This increases the uncompressed size of the application by approximately 8MB. - -See more details about cronet_http at: https://pub.dev/packages/cronet_http. diff --git a/pkgs/cronet_http/android/build.gradle b/pkgs/cronet_http/android/build.gradle index af945d9d8b..b11c74b8a8 100644 --- a/pkgs/cronet_http/android/build.gradle +++ b/pkgs/cronet_http/android/build.gradle @@ -21,6 +21,17 @@ rootProject.allprojects { } } +def dartDefines = [ + cronetHttpNoPlay: 'false' +] +if (project.hasProperty('dart-defines')) { + def defines = project.property('dart-defines').split(',').collectEntries { entry -> + def pair = new String(entry.decodeBase64(), 'UTF-8').split('=') + [(pair.first()): pair.last()] + } + dartDefines = dartDefines + defines +} + apply plugin: 'com.android.library' apply plugin: 'kotlin-android' @@ -65,5 +76,9 @@ android { } dependencies { - implementation "com.google.android.gms:play-services-cronet:18.0.1" + if (dartDefines.cronetHttpNoPlay == 'true') { + implementation 'org.chromium.net:cronet-embedded:113.5672.61' + } else { + implementation "com.google.android.gms:play-services-cronet:18.0.1" + } } diff --git a/pkgs/cronet_http/pubspec.yaml b/pkgs/cronet_http/pubspec.yaml index f3cb350ebe..d398f32560 100644 --- a/pkgs/cronet_http/pubspec.yaml +++ b/pkgs/cronet_http/pubspec.yaml @@ -1,5 +1,5 @@ name: cronet_http -version: 1.1.1 +version: 1.2.0-wip description: >- An Android Flutter plugin that provides access to the Cronet HTTP client. repository: https://github.com/dart-lang/http/tree/master/pkgs/cronet_http diff --git a/pkgs/cronet_http/tool/prepare_for_embedded.dart b/pkgs/cronet_http/tool/prepare_for_embedded.dart deleted file mode 100644 index 16a2c0baef..0000000000 --- a/pkgs/cronet_http/tool/prepare_for_embedded.dart +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/// The cronet_http directory is used to produce two packages: -/// - `cronet_http`, which uses the Google Play Services version of Cronet. -/// - `cronet_http_embedded`, which embeds Cronet. -/// -/// The default configuration of this code is to use the -/// Google Play Services version of Cronet. -/// -/// The script transforms the configuration into one that embeds Cronet by: -/// 1. Modifying the Gradle build file to reference the embedded Cronet. -/// 2. Modifying the *name* and *description* in `pubspec.yaml`. -/// 3. Replacing `README.md` with `README_EMBEDDED.md`. -/// 4. Change the name of `cronet_http.dart` to `cronet_http_embedded.dart`. -/// 5. Update all the imports from `package:cronet_http/cronet_http.dart` to -/// `package:cronet_http_embedded/cronet_http_embedded.dart` -/// -/// After running this script, `flutter pub publish` -/// can be run to update package:cronet_http_embedded. -/// -/// NOTE: This script modifies the above files in place. -library; - -import 'dart:io'; - -import 'package:http/http.dart' as http; -import 'package:xml/xml.dart'; -import 'package:yaml_edit/yaml_edit.dart'; - -late final String _scriptName; -late final Directory _packageDirectory; - -const _gmsDependencyName = 'com.google.android.gms:play-services-cronet'; -const _embeddedDependencyName = 'org.chromium.net:cronet-embedded'; -const _packageName = 'cronet_http_embedded'; -const _packageDescription = 'An Android Flutter plugin that ' - 'provides access to the Cronet HTTP client. ' - 'Identical to package:cronet_http except that it embeds Cronet ' - 'rather than relying on Google Play Services.'; -final _cronetVersionUri = Uri.https( - 'dl.google.com', - 'android/maven2/org/chromium/net/group-index.xml', -); -// Finds the Google Play Services Cronet dependency line. For example: -// ' implementation "com.google.android.gms:play-services-cronet:18.0.1"' -final implementationRegExp = RegExp( - '^\\s*implementation [\'"]' - '$_gmsDependencyName' - ':\\d+.\\d+.\\d+[\'"]', - multiLine: true, -); - -void main(List args) async { - final script = Platform.script.toFilePath(); - _scriptName = script.split(Platform.pathSeparator).last; - _packageDirectory = Directory( - Uri.directory( - '${script.replaceAll(_scriptName, '')}' - '..${Platform.pathSeparator}', - ).toFilePath(), - ); - final latestVersion = await _getLatestCronetVersion(); - updateBuildGradle(latestVersion); - updateExampleBuildGradle(); - updatePubSpec(); - updateReadme(); - updateLibraryName(); - updateImports(); -} - -Future _getLatestCronetVersion() async { - final response = await http.get(_cronetVersionUri); - final parsedXml = XmlDocument.parse(response.body); - final embeddedNode = parsedXml.children - .singleWhere((e) => e is XmlElement) - .children - .singleWhere((e) => e is XmlElement && e.name.local == 'cronet-embedded'); - final stableVersionReg = RegExp(r'^\d+.\d+.\d+$'); - final versions = embeddedNode.attributes - .singleWhere((e) => e.name.local == 'versions') - .value - .split(',') - .where((e) => stableVersionReg.stringMatch(e) == e); - return versions.last; -} - -/// Update android/build.gradle. -void updateBuildGradle(String latestVersion) { - final buildGradle = File('${_packageDirectory.path}/android/build.gradle'); - final gradleContent = buildGradle.readAsStringSync(); - final newImplementation = '$_embeddedDependencyName:$latestVersion'; - print('Updating ${buildGradle.path}: adding $newImplementation'); - final newGradleContent = gradleContent.replaceAll( - implementationRegExp, - ' implementation "$newImplementation"', - ); - buildGradle.writeAsStringSync(newGradleContent); -} - -/// Remove the cronet reference from ./example/android/app/build.gradle. -void updateExampleBuildGradle() { - final buildGradle = - File('${_packageDirectory.path}/example/android/app/build.gradle'); - final gradleContent = buildGradle.readAsStringSync(); - - print('Updating ${buildGradle.path}: removing cronet reference'); - final newGradleContent = gradleContent.replaceAll( - implementationRegExp, - ' // NOTE: removed in package:cronet_http_embedded', - ); - buildGradle.writeAsStringSync(newGradleContent); -} - -/// Update pubspec.yaml and example/pubspec.yaml. -void updatePubSpec() { - print('Updating pubspec.yaml'); - final fPubspec = File('${_packageDirectory.path}/pubspec.yaml'); - final yamlEditor = YamlEditor(fPubspec.readAsStringSync()) - ..update(['name'], _packageName) - ..update(['description'], _packageDescription); - fPubspec.writeAsStringSync(yamlEditor.toString()); - print('Updating example/pubspec.yaml'); - final examplePubspec = File('${_packageDirectory.path}/example/pubspec.yaml'); - final replaced = examplePubspec - .readAsStringSync() - .replaceAll('cronet_http:', 'cronet_http_embedded:'); - examplePubspec.writeAsStringSync(replaced); -} - -/// Move README_EMBEDDED.md to replace README.md. -void updateReadme() { - print('Updating README.md from README_EMBEDDED.md'); - File('${_packageDirectory.path}/README.md').deleteSync(); - File('${_packageDirectory.path}/README_EMBEDDED.md') - .renameSync('${_packageDirectory.path}/README.md'); -} - -void updateImports() { - print('Updating imports in Dart files'); - for (final file in _packageDirectory.listSync(recursive: true)) { - if (file is File && - file.path.endsWith('.dart') && - !file.path.contains(_scriptName)) { - final updatedSource = file.readAsStringSync().replaceAll( - 'package:cronet_http/cronet_http.dart', - 'package:cronet_http_embedded/cronet_http_embedded.dart', - ); - file.writeAsStringSync(updatedSource); - } - } -} - -void updateLibraryName() { - print('Renaming cronet_http.dart to cronet_http_embedded.dart'); - File( - '${_packageDirectory.path}/lib/cronet_http.dart', - ).renameSync( - '${_packageDirectory.path}/lib/cronet_http_embedded.dart', - ); -}