diff --git a/.ci/Dockerfile b/.ci/Dockerfile index a69f9cb67526..bae957da08eb 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,5 +1,20 @@ FROM cirrusci/flutter:stable +RUN sudo apt-get update -y + +RUN sudo apt-get install -y --no-install-recommends gnupg + +# Add repo for gcloud sdk and install it +RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \ + sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list + +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \ + sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - + +RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \ + gcloud config set core/disable_usage_reporting true && \ + gcloud config set component_manager/disable_update_check true + RUN yes | sdkmanager \ "platforms;android-27" \ "build-tools;27.0.3" \ diff --git a/.cirrus.yml b/.cirrus.yml index 0caa1a226807..9ee572939474 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -24,12 +24,13 @@ task: script: ./script/incremental_build.sh analyze - name: build_all_plugins_apk script: ./script/build_all_plugins_app.sh apk - - name: build-apks+java-test+drive-examples + - name: build-apks+java-test+firebase-test-lab env: matrix: PLUGIN_SHARDING: "--shardIndex 0 --shardCount 2" PLUGIN_SHARDING: "--shardIndex 1 --shardCount 2" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[07586610af1fdfc894e5969f70ef2458341b9b7e9c3b7c4225a663b4a48732b7208a4d91c3b7d45305a6b55fa2a37fc4] script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. @@ -43,6 +44,12 @@ task: - export CIRRUS_COMMIT_MESSAGE="" - ./script/incremental_build.sh build-examples --apk - ./script/incremental_build.sh java-test # must come after apk build + - if [[ $GCLOUD_FIREBASE_TESTLAB_KEY == ENCRYPTED* ]]; then + - echo "This user does not have permission to run Firebase Test Lab tests." + - else + - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json + - ./script/incremental_build.sh firebase-test-lab + - fi - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt` - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt` diff --git a/packages/package_info/CHANGELOG.md b/packages/package_info/CHANGELOG.md index fbb076fd181f..dc9c3cbd84d4 100644 --- a/packages/package_info/CHANGELOG.md +++ b/packages/package_info/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.4.0+8 + +* Support the v2 Android embedder. +* Update to AndroidX. +* Add a unit test. +* Migrate to using the new e2e test binding. + ## 0.4.0+7 * Update and migrate iOS example project. diff --git a/packages/package_info/android/build.gradle b/packages/package_info/android/build.gradle index 2feb6adb735d..eb75e2334d4a 100644 --- a/packages/package_info/android/build.gradle +++ b/packages/package_info/android/build.gradle @@ -45,3 +45,28 @@ android { disable 'InvalidPackage' } } + +// TODO(amirh): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348 +afterEvaluate { + def containsEmbeddingDependencies = false + for (def configuration : configurations.all) { + for (def dependency : configuration.dependencies) { + if (dependency.group == 'io.flutter' && + dependency.name.startsWith('flutter_embedding') && + dependency.isTransitive()) + { + containsEmbeddingDependencies = true + break + } + } + } + if (!containsEmbeddingDependencies) { + android { + dependencies { + def lifecycle_version = "2.1.0" + api "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + api "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + } + } + } +} diff --git a/packages/package_info/android/gradle.properties b/packages/package_info/android/gradle.properties index 8bd86f680510..7884ce914984 100644 --- a/packages/package_info/android/gradle.properties +++ b/packages/package_info/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableJetifier=true diff --git a/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java b/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java index 81fae62a1f4f..45593de41782 100644 --- a/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java +++ b/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java @@ -8,6 +8,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; @@ -17,31 +19,45 @@ import java.util.Map; /** PackageInfoPlugin */ -public class PackageInfoPlugin implements MethodCallHandler { - private final Registrar mRegistrar; +public class PackageInfoPlugin implements MethodCallHandler, FlutterPlugin { + private Context applicationContext; + private MethodChannel methodChannel; /** Plugin registration. */ public static void registerWith(Registrar registrar) { - final MethodChannel channel = - new MethodChannel(registrar.messenger(), "plugins.flutter.io/package_info"); - channel.setMethodCallHandler(new PackageInfoPlugin(registrar)); + final PackageInfoPlugin instance = new PackageInfoPlugin(); + instance.onAttachedToEngine(registrar.context(), registrar.messenger()); } - private PackageInfoPlugin(Registrar registrar) { - this.mRegistrar = registrar; + @Override + public void onAttachedToEngine(FlutterPluginBinding binding) { + onAttachedToEngine( + binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor()); + } + + private void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) { + this.applicationContext = applicationContext; + methodChannel = new MethodChannel(messenger, "plugins.flutter.io/package_info"); + methodChannel.setMethodCallHandler(this); + } + + @Override + public void onDetachedFromEngine(FlutterPluginBinding binding) { + applicationContext = null; + methodChannel.setMethodCallHandler(null); + methodChannel = null; } @Override public void onMethodCall(MethodCall call, Result result) { try { - Context context = mRegistrar.context(); if (call.method.equals("getAll")) { - PackageManager pm = context.getPackageManager(); - PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0); + PackageManager pm = applicationContext.getPackageManager(); + PackageInfo info = pm.getPackageInfo(applicationContext.getPackageName(), 0); Map map = new HashMap<>(); map.put("appName", info.applicationInfo.loadLabel(pm).toString()); - map.put("packageName", context.getPackageName()); + map.put("packageName", applicationContext.getPackageName()); map.put("version", info.versionName); map.put("buildNumber", String.valueOf(getLongVersionCode(info))); diff --git a/packages/package_info/example/android/app/gradle.properties b/packages/package_info/example/android/app/gradle.properties index 5465fec0ecad..845d3bcf6963 100644 --- a/packages/package_info/example/android/app/gradle.properties +++ b/packages/package_info/example/android/app/gradle.properties @@ -1,2 +1,3 @@ android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true +android.enableR8=true diff --git a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/EmbedderV1ActivityTest.java b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/EmbedderV1ActivityTest.java new file mode 100644 index 000000000000..47362ba64a9d --- /dev/null +++ b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/EmbedderV1ActivityTest.java @@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium 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 io.flutter.plugins.packageinfoexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class EmbedderV1ActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(EmbedderV1Activity.class); +} diff --git a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java new file mode 100644 index 000000000000..4241bf194002 --- /dev/null +++ b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java @@ -0,0 +1,15 @@ +// Copyright 2019 The Chromium 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 io.flutter.plugins.packageinfoexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} diff --git a/packages/package_info/example/android/app/src/main/AndroidManifest.xml b/packages/package_info/example/android/app/src/main/AndroidManifest.xml index 3e46c5d3bf06..1b95d5d9cde4 100644 --- a/packages/package_info/example/android/app/src/main/AndroidManifest.xml +++ b/packages/package_info/example/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ + + diff --git a/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/EmbedderV1Activity.java b/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/EmbedderV1Activity.java new file mode 100644 index 000000000000..170d0f8cb767 --- /dev/null +++ b/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/EmbedderV1Activity.java @@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium 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 io.flutter.plugins.packageinfoexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class EmbedderV1Activity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/MainActivity.java b/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/MainActivity.java index 6b4ea9c0289f..cb417e5b0067 100644 --- a/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/MainActivity.java +++ b/packages/package_info/example/android/app/src/main/java/io/flutter/plugins/packageinfoexample/MainActivity.java @@ -4,14 +4,17 @@ package io.flutter.plugins.packageinfoexample; -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; +import dev.flutter.plugins.e2e.E2EPlugin; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugins.packageinfo.PackageInfoPlugin; public class MainActivity extends FlutterActivity { + // TODO(jackson): Remove this once v2 of GeneratedPluginRegistrant rolls to stable. + // https://github.com/flutter/flutter/issues/42694 @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); + public void configureFlutterEngine(FlutterEngine flutterEngine) { + flutterEngine.getPlugins().add(new PackageInfoPlugin()); + flutterEngine.getPlugins().add(new E2EPlugin()); } } diff --git a/packages/package_info/example/android/gradle.properties b/packages/package_info/example/android/gradle.properties index 8bd86f680510..05413bc45d00 100644 --- a/packages/package_info/example/android/gradle.properties +++ b/packages/package_info/example/android/gradle.properties @@ -1 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.enableJetifier=true +android.enableR8=true +android.useAndroidX=true diff --git a/packages/package_info/example/pubspec.yaml b/packages/package_info/example/pubspec.yaml index e4844cd95648..5d94abaf2042 100644 --- a/packages/package_info/example/pubspec.yaml +++ b/packages/package_info/example/pubspec.yaml @@ -6,6 +6,7 @@ dependencies: sdk: flutter package_info: path: ../ + e2e: "^0.2.1" dev_dependencies: flutter_driver: diff --git a/packages/package_info/example/test_driver/package_info.dart b/packages/package_info/example/test_driver/package_info.dart deleted file mode 100644 index 97c2db6363c7..000000000000 --- a/packages/package_info/example/test_driver/package_info.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:flutter_driver/driver_extension.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:package_info/package_info.dart'; - -void main() { - final Completer completer = Completer(); - enableFlutterDriverExtension(handler: (_) => completer.future); - tearDownAll(() => completer.complete(null)); - - group('package_info test driver', () { - test('test package info result', () async { - final PackageInfo info = await PackageInfo.fromPlatform(); - // These tests are based on the example app. The tests should be updated if any related info changes. - if (Platform.isAndroid) { - expect(info.appName, 'package_info_example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'io.flutter.plugins.packageinfoexample'); - expect(info.version, '1.0'); - } else if (Platform.isIOS) { - expect(info.appName, 'Package Info Example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'io.flutter.plugins.packageInfoExample'); - expect(info.version, '1.0'); - } else { - throw (UnsupportedError('platform not supported')); - } - }); - }); -} diff --git a/packages/package_info/example/test_driver/package_info_e2e.dart b/packages/package_info/example/test_driver/package_info_e2e.dart new file mode 100644 index 000000000000..243ddf6563f7 --- /dev/null +++ b/packages/package_info/example/test_driver/package_info_e2e.dart @@ -0,0 +1,51 @@ +// Copyright 2019, the Chromium 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. + +import 'dart:io'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:e2e/e2e.dart'; +import 'package:package_info/package_info.dart'; +import 'package:package_info_example/main.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('fromPlatform', (WidgetTester tester) async { + final PackageInfo info = await PackageInfo.fromPlatform(); + // These tests are based on the example app. The tests should be updated if any related info changes. + if (Platform.isAndroid) { + expect(info.appName, 'package_info_example'); + expect(info.buildNumber, '1'); + expect(info.packageName, 'io.flutter.plugins.packageinfoexample'); + expect(info.version, '1.0'); + } else if (Platform.isIOS) { + expect(info.appName, 'Package Info Example'); + expect(info.buildNumber, '1'); + expect(info.packageName, 'io.flutter.plugins.packageInfoExample'); + expect(info.version, '1.0'); + } else { + throw (UnsupportedError('platform not supported')); + } + }); + + testWidgets('example', (WidgetTester tester) async { + await tester.pumpWidget(MyApp()); + await tester.pumpAndSettle(); + if (Platform.isAndroid) { + expect(find.text('package_info_example'), findsOneWidget); + expect(find.text('1'), findsOneWidget); + expect( + find.text('io.flutter.plugins.packageinfoexample'), findsOneWidget); + expect(find.text('1.0'), findsOneWidget); + } else if (Platform.isIOS) { + expect(find.text('Package Info Example'), findsOneWidget); + expect(find.text('1'), findsOneWidget); + expect( + find.text('io.flutter.plugins.packageInfoExample'), findsOneWidget); + expect(find.text('1.0'), findsOneWidget); + } else { + throw (UnsupportedError('platform not supported')); + } + }); +} diff --git a/packages/package_info/example/test_driver/package_info_e2e_test.dart b/packages/package_info/example/test_driver/package_info_e2e_test.dart new file mode 100644 index 000000000000..ac4ea11482e2 --- /dev/null +++ b/packages/package_info/example/test_driver/package_info_e2e_test.dart @@ -0,0 +1,15 @@ +// Copyright 2019, the Chromium 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. + +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +Future main() async { + final FlutterDriver driver = await FlutterDriver.connect(); + final String result = + await driver.requestData(null, timeout: const Duration(minutes: 1)); + driver.close(); + exit(result == 'pass' ? 0 : 1); +} diff --git a/packages/package_info/example/test_driver/package_info_test.dart b/packages/package_info/example/test_driver/package_info_test.dart deleted file mode 100644 index c1d690c17ee3..000000000000 --- a/packages/package_info/example/test_driver/package_info_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:test/test.dart'; - -void main() { - test('package_info', () async { - final FlutterDriver driver = await FlutterDriver.connect(); - await driver.requestData(null, timeout: const Duration(minutes: 1)); - driver.close(); - }); -} diff --git a/packages/package_info/pubspec.yaml b/packages/package_info/pubspec.yaml index f24b5d711adf..d94a1eb4f8ba 100644 --- a/packages/package_info/pubspec.yaml +++ b/packages/package_info/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/package_info -version: 0.4.0+7 +version: 0.4.0+8 flutter: plugin: @@ -21,7 +21,8 @@ dev_dependencies: flutter_driver: sdk: flutter test: any + e2e: "^0.2.1" environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" - flutter: ">=1.5.0 <2.0.0" + flutter: ">=1.6.7 <2.0.0" diff --git a/packages/package_info/test/package_info_test.dart b/packages/package_info/test/package_info_test.dart new file mode 100644 index 000000000000..47d48fde2d2d --- /dev/null +++ b/packages/package_info/test/package_info_test.dart @@ -0,0 +1,49 @@ +// Copyright 2019 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 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:package_info/package_info.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const MethodChannel channel = + MethodChannel('plugins.flutter.io/package_info'); + List log; + + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'getAll': + return { + 'appName': 'package_info_example', + 'buildNumber': '1', + 'packageName': 'io.flutter.plugins.packageinfoexample', + 'version': '1.0', + }; + default: + assert(false); + return null; + } + }); + + setUp(() { + log = []; + }); + + test('fromPlatform', () async { + final PackageInfo info = await PackageInfo.fromPlatform(); + expect(info.appName, 'package_info_example'); + expect(info.buildNumber, '1'); + expect(info.packageName, 'io.flutter.plugins.packageinfoexample'); + expect(info.version, '1.0'); + expect( + log, + [ + isMethodCall('getAll', arguments: null), + ], + ); + }); +}