[device_info] Support v2 android embedding. (#2163)
diff --git a/packages/device_info/CHANGELOG.md b/packages/device_info/CHANGELOG.md
index 62d4960..1fff8c7 100644
--- a/packages/device_info/CHANGELOG.md
+++ b/packages/device_info/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.4.1
+
+* Support the v2 Android embedding.
+* Update to AndroidX.
+* Migrate to using the new e2e test binding.
+* Add a e2e test.
+
+
## 0.4.0+4
* Define clang module for iOS.
diff --git a/packages/device_info/android/build.gradle b/packages/device_info/android/build.gradle
index df08280..7dfea2d 100644
--- a/packages/device_info/android/build.gradle
+++ b/packages/device_info/android/build.gradle
@@ -45,3 +45,29 @@
disable 'InvalidPackage'
}
}
+
+// TODO(cyanglaz): 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 = "1.1.1"
+ api "android.arch.lifecycle:runtime:$lifecycle_version"
+ api "android.arch.lifecycle:common:$lifecycle_version"
+ api "android.arch.lifecycle:common-java8:$lifecycle_version"
+ }
+ }
+ }
+}
diff --git a/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java b/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java
index a22009f..8ad0f5d 100644
--- a/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java
+++ b/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java
@@ -4,120 +4,43 @@
package io.flutter.plugins.deviceinfo;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.os.Build;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.provider.Settings;
-import io.flutter.plugin.common.MethodCall;
+import android.content.ContentResolver;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
-import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
/** DeviceInfoPlugin */
-public class DeviceInfoPlugin implements MethodCallHandler {
- private final Context context;
+public class DeviceInfoPlugin implements FlutterPlugin {
- /** Substitute for missing values. */
- private static final String[] EMPTY_STRING_LIST = new String[] {};
+ MethodChannel channel;
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
- final MethodChannel channel =
- new MethodChannel(registrar.messenger(), "plugins.flutter.io/device_info");
- channel.setMethodCallHandler(new DeviceInfoPlugin(registrar.context()));
- }
-
- /** Do not allow direct instantiation. */
- private DeviceInfoPlugin(Context context) {
- this.context = context;
+ DeviceInfoPlugin plugin = new DeviceInfoPlugin();
+ plugin.setupMethodChannel(registrar.messenger(), registrar.context().getContentResolver());
}
@Override
- public void onMethodCall(MethodCall call, Result result) {
- if (call.method.equals("getAndroidDeviceInfo")) {
- Map<String, Object> build = new HashMap<>();
- build.put("board", Build.BOARD);
- build.put("bootloader", Build.BOOTLOADER);
- build.put("brand", Build.BRAND);
- build.put("device", Build.DEVICE);
- build.put("display", Build.DISPLAY);
- build.put("fingerprint", Build.FINGERPRINT);
- build.put("hardware", Build.HARDWARE);
- build.put("host", Build.HOST);
- build.put("id", Build.ID);
- build.put("manufacturer", Build.MANUFACTURER);
- build.put("model", Build.MODEL);
- build.put("product", Build.PRODUCT);
- if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
- build.put("supported32BitAbis", Arrays.asList(Build.SUPPORTED_32_BIT_ABIS));
- build.put("supported64BitAbis", Arrays.asList(Build.SUPPORTED_64_BIT_ABIS));
- build.put("supportedAbis", Arrays.asList(Build.SUPPORTED_ABIS));
- } else {
- build.put("supported32BitAbis", Arrays.asList(EMPTY_STRING_LIST));
- build.put("supported64BitAbis", Arrays.asList(EMPTY_STRING_LIST));
- build.put("supportedAbis", Arrays.asList(EMPTY_STRING_LIST));
- }
- build.put("tags", Build.TAGS);
- build.put("type", Build.TYPE);
- build.put("isPhysicalDevice", !isEmulator());
- build.put("androidId", getAndroidId());
-
- Map<String, Object> version = new HashMap<>();
- if (VERSION.SDK_INT >= VERSION_CODES.M) {
- version.put("baseOS", VERSION.BASE_OS);
- version.put("previewSdkInt", VERSION.PREVIEW_SDK_INT);
- version.put("securityPatch", VERSION.SECURITY_PATCH);
- }
- version.put("codename", VERSION.CODENAME);
- version.put("incremental", VERSION.INCREMENTAL);
- version.put("release", VERSION.RELEASE);
- version.put("sdkInt", VERSION.SDK_INT);
- build.put("version", version);
-
- result.success(build);
- } else {
- result.notImplemented();
- }
+ public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
+ setupMethodChannel(
+ binding.getFlutterEngine().getDartExecutor(),
+ binding.getApplicationContext().getContentResolver());
}
- /**
- * Returns the Android hardware device ID that is unique between the device + user and app
- * signing. This key will change if the app is uninstalled or its data is cleared. Device factory
- * reset will also result in a value change.
- *
- * @return The android ID
- */
- @SuppressLint("HardwareIds")
- private String getAndroidId() {
- return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
+ @Override
+ public void onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding binding) {
+ tearDownChannel();
}
- /**
- * A simple emulator-detection based on the flutter tools detection logic and a couple of legacy
- * detection systems
- */
- private boolean isEmulator() {
- return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
- || Build.FINGERPRINT.startsWith("generic")
- || Build.FINGERPRINT.startsWith("unknown")
- || Build.HARDWARE.contains("goldfish")
- || Build.HARDWARE.contains("ranchu")
- || Build.MODEL.contains("google_sdk")
- || Build.MODEL.contains("Emulator")
- || Build.MODEL.contains("Android SDK built for x86")
- || Build.MANUFACTURER.contains("Genymotion")
- || Build.PRODUCT.contains("sdk_google")
- || Build.PRODUCT.contains("google_sdk")
- || Build.PRODUCT.contains("sdk")
- || Build.PRODUCT.contains("sdk_x86")
- || Build.PRODUCT.contains("vbox86p")
- || Build.PRODUCT.contains("emulator")
- || Build.PRODUCT.contains("simulator");
+ private void setupMethodChannel(BinaryMessenger messenger, ContentResolver contentResolver) {
+ channel = new MethodChannel(messenger, "plugins.flutter.io/device_info");
+ final MethodCallHandlerImpl handler = new MethodCallHandlerImpl(contentResolver);
+ channel.setMethodCallHandler(handler);
+ }
+
+ private void tearDownChannel() {
+ channel.setMethodCallHandler(null);
+ channel = null;
}
}
diff --git a/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java b/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java
new file mode 100644
index 0000000..22ea1f0
--- /dev/null
+++ b/packages/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java
@@ -0,0 +1,115 @@
+// 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.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.os.Build;
+import android.provider.Settings;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The implementation of {@link MethodChannel.MethodCallHandler} for the plugin. Responsible for
+ * receiving method calls from method channel.
+ */
+class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
+
+ private ContentResolver contentResolver;
+
+ /** Substitute for missing values. */
+ private static final String[] EMPTY_STRING_LIST = new String[] {};
+
+ /** Constructs DeviceInfo. The {@code contentResolver} must not be null. */
+ MethodCallHandlerImpl(ContentResolver contentResolver) {
+ this.contentResolver = contentResolver;
+ }
+
+ @Override
+ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
+ if (call.method.equals("getAndroidDeviceInfo")) {
+ Map<String, Object> build = new HashMap<>();
+ build.put("board", Build.BOARD);
+ build.put("bootloader", Build.BOOTLOADER);
+ build.put("brand", Build.BRAND);
+ build.put("device", Build.DEVICE);
+ build.put("display", Build.DISPLAY);
+ build.put("fingerprint", Build.FINGERPRINT);
+ build.put("hardware", Build.HARDWARE);
+ build.put("host", Build.HOST);
+ build.put("id", Build.ID);
+ build.put("manufacturer", Build.MANUFACTURER);
+ build.put("model", Build.MODEL);
+ build.put("product", Build.PRODUCT);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ build.put("supported32BitAbis", Arrays.asList(Build.SUPPORTED_32_BIT_ABIS));
+ build.put("supported64BitAbis", Arrays.asList(Build.SUPPORTED_64_BIT_ABIS));
+ build.put("supportedAbis", Arrays.asList(Build.SUPPORTED_ABIS));
+ } else {
+ build.put("supported32BitAbis", Arrays.asList(EMPTY_STRING_LIST));
+ build.put("supported64BitAbis", Arrays.asList(EMPTY_STRING_LIST));
+ build.put("supportedAbis", Arrays.asList(EMPTY_STRING_LIST));
+ }
+ build.put("tags", Build.TAGS);
+ build.put("type", Build.TYPE);
+ build.put("isPhysicalDevice", !isEmulator());
+ build.put("androidId", getAndroidId());
+
+ Map<String, Object> version = new HashMap<>();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ version.put("baseOS", Build.VERSION.BASE_OS);
+ version.put("previewSdkInt", Build.VERSION.PREVIEW_SDK_INT);
+ version.put("securityPatch", Build.VERSION.SECURITY_PATCH);
+ }
+ version.put("codename", Build.VERSION.CODENAME);
+ version.put("incremental", Build.VERSION.INCREMENTAL);
+ version.put("release", Build.VERSION.RELEASE);
+ version.put("sdkInt", Build.VERSION.SDK_INT);
+ build.put("version", version);
+
+ result.success(build);
+ } else {
+ result.notImplemented();
+ }
+ }
+
+ /**
+ * Returns the Android hardware device ID that is unique between the device + user and app
+ * signing. This key will change if the app is uninstalled or its data is cleared. Device factory
+ * reset will also result in a value change.
+ *
+ * @return The android ID
+ */
+ @SuppressLint("HardwareIds")
+ private String getAndroidId() {
+ return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID);
+ }
+
+ /**
+ * A simple emulator-detection based on the flutter tools detection logic and a couple of legacy
+ * detection systems
+ */
+ private boolean isEmulator() {
+ return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
+ || Build.FINGERPRINT.startsWith("generic")
+ || Build.FINGERPRINT.startsWith("unknown")
+ || Build.HARDWARE.contains("goldfish")
+ || Build.HARDWARE.contains("ranchu")
+ || Build.MODEL.contains("google_sdk")
+ || Build.MODEL.contains("Emulator")
+ || Build.MODEL.contains("Android SDK built for x86")
+ || Build.MANUFACTURER.contains("Genymotion")
+ || Build.PRODUCT.contains("sdk_google")
+ || Build.PRODUCT.contains("google_sdk")
+ || Build.PRODUCT.contains("sdk")
+ || Build.PRODUCT.contains("sdk_x86")
+ || Build.PRODUCT.contains("vbox86p")
+ || Build.PRODUCT.contains("emulator")
+ || Build.PRODUCT.contains("simulator");
+ }
+}
diff --git a/packages/device_info/example/android/app/src/main/AndroidManifest.xml b/packages/device_info/example/android/app/src/main/AndroidManifest.xml
index b46ebe8..45242ab 100644
--- a/packages/device_info/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/device_info/example/android/app/src/main/AndroidManifest.xml
@@ -4,12 +4,19 @@
<uses-permission android:name="android.permission.INTERNET"/>
<application android:name="io.flutter.app.FlutterApplication" android:label="device_info_example" android:icon="@mipmap/ic_launcher">
- <activity android:name="io.flutter.plugins.deviceinfoexample.MainActivity"
+ <activity android:name=".EmbeddingV1Activity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
+ android:exported="true"
android:windowSoftInputMode="adjustResize">
+ </activity>
+ <activity android:name=".MainActivity"
+ android:theme="@android:style/Theme.Black.NoTitleBar"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1Activity.java b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1Activity.java
new file mode 100644
index 0000000..0bfaee8
--- /dev/null
+++ b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1Activity.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.deviceinfoexample;
+
+import android.os.Bundle;
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class EmbeddingV1Activity extends FlutterActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ GeneratedPluginRegistrant.registerWith(this);
+ }
+}
diff --git a/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1ActivityTest.java b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1ActivityTest.java
new file mode 100644
index 0000000..2bec9fb
--- /dev/null
+++ b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/EmbeddingV1ActivityTest.java
@@ -0,0 +1,13 @@
+package io.flutter.plugins.deviceinfoexample;
+
+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 EmbeddingV1ActivityTest {
+ @Rule
+ public ActivityTestRule<EmbeddingV1Activity> rule =
+ new ActivityTestRule<>(EmbeddingV1Activity.class);
+}
diff --git a/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivity.java b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivity.java
index 06a3172..b820542 100644
--- a/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivity.java
+++ b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivity.java
@@ -1,17 +1,20 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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.deviceinfoexample;
-import android.os.Bundle;
-import io.flutter.app.FlutterActivity;
-import io.flutter.plugins.GeneratedPluginRegistrant;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
+import io.flutter.plugins.deviceinfo.DeviceInfoPlugin;
public class MainActivity extends FlutterActivity {
+
+ // TODO(cyanglaz): 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) {
+ super.configureFlutterEngine(flutterEngine);
+ flutterEngine.getPlugins().add(new DeviceInfoPlugin());
}
}
diff --git a/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivityTest.java b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/MainActivityTest.java
new file mode 100644
index 0000000..36967eb
--- /dev/null
+++ b/packages/device_info/example/android/app/src/main/java/io/flutter/plugins/deviceinfoexample/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.deviceinfoexample;
+
+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<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
+}
diff --git a/packages/device_info/example/android/gradle.properties b/packages/device_info/example/android/gradle.properties
index 8bd86f6..38c8d45 100644
--- a/packages/device_info/example/android/gradle.properties
+++ b/packages/device_info/example/android/gradle.properties
@@ -1 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/device_info/example/pubspec.yaml b/packages/device_info/example/pubspec.yaml
index 65b7bf8..148b7b9 100644
--- a/packages/device_info/example/pubspec.yaml
+++ b/packages/device_info/example/pubspec.yaml
@@ -7,5 +7,15 @@
device_info:
path: ../
+dev_dependencies:
+ flutter_driver:
+ sdk: flutter
+ e2e: ^0.2.0
+
flutter:
uses-material-design: true
+
+environment:
+ sdk: ">=2.0.0-dev.28.0 <3.0.0"
+ flutter: ">=1.9.1+hotfix.2 <2.0.0"
+
diff --git a/packages/device_info/example/test_driver/device_info_e2e.dart b/packages/device_info/example/test_driver/device_info_e2e.dart
new file mode 100644
index 0000000..db8a73f
--- /dev/null
+++ b/packages/device_info/example/test_driver/device_info_e2e.dart
@@ -0,0 +1,32 @@
+// 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:device_info/device_info.dart';
+import 'package:e2e/e2e.dart';
+
+void main() {
+ E2EWidgetsFlutterBinding.ensureInitialized();
+
+ IosDeviceInfo iosInfo;
+ AndroidDeviceInfo androidInfo;
+
+ setUpAll(() async {
+ final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
+ if (Platform.isIOS) {
+ iosInfo = await deviceInfoPlugin.iosInfo;
+ } else if (Platform.isAndroid) {
+ androidInfo = await deviceInfoPlugin.androidInfo;
+ }
+ });
+
+ testWidgets('Can get non-null device model', (WidgetTester tester) async {
+ if (Platform.isIOS) {
+ expect(iosInfo.model, isNotNull);
+ } else if (Platform.isAndroid) {
+ expect(androidInfo.model, isNotNull);
+ }
+ });
+}
diff --git a/packages/device_info/example/test_driver/device_info_e2e_test.dart b/packages/device_info/example/test_driver/device_info_e2e_test.dart
new file mode 100644
index 0000000..ff6e9ce
--- /dev/null
+++ b/packages/device_info/example/test_driver/device_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:async';
+import 'dart:io';
+import 'package:flutter_driver/flutter_driver.dart';
+
+Future<void> 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/device_info/pubspec.yaml b/packages/device_info/pubspec.yaml
index bc192a8..580df14 100644
--- a/packages/device_info/pubspec.yaml
+++ b/packages/device_info/pubspec.yaml
@@ -3,7 +3,7 @@
(make, model, etc.), and Android or iOS version the app is running on.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/device_info
-version: 0.4.0+4
+version: 0.4.1
flutter:
plugin:
@@ -15,6 +15,12 @@
flutter:
sdk: flutter
+dev_dependencies:
+ test: ^1.3.0
+ flutter_test:
+ sdk: flutter
+ e2e: ^0.2.0
+
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"