[image_picker] support android V2 embedding (#2430)

diff --git a/packages/image_picker/CHANGELOG.md b/packages/image_picker/CHANGELOG.md
index 6592c9e..bd0c289 100644
--- a/packages/image_picker/CHANGELOG.md
+++ b/packages/image_picker/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.6.3
+
+* Support Android V2 embedding.
+* Migrate to using the new e2e test binding.
+
 ## 0.6.2+3
 * Remove the deprecated `author:` field from pubspec.yaml
 * Migrate the plugin to the pubspec platforms manifest.
diff --git a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
index e34a3b5..f2a0b02 100644
--- a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
+++ b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
@@ -199,6 +199,7 @@
     this.cache = cache;
   }
 
+  // Save the state of the image picker so it can be retrieved with `retrieveLostImage`.
   void saveStateBeforeResult() {
     if (methodCall == null) {
       return;
diff --git a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
index b495a8e..950304c 100644
--- a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
+++ b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
@@ -10,13 +10,86 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.embedding.engine.plugins.activity.ActivityAware;
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
+import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter;
+import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.PluginRegistry;
 import java.io.File;
 
-public class ImagePickerPlugin implements MethodChannel.MethodCallHandler {
+@SuppressWarnings("deprecation")
+public class ImagePickerPlugin
+    implements MethodChannel.MethodCallHandler, FlutterPlugin, ActivityAware {
+
+  private class LifeCycleObserver
+      implements Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver {
+    private final Activity thisActivity;
+
+    LifeCycleObserver(Activity activity) {
+      this.thisActivity = activity;
+    }
+
+    @Override
+    public void onCreate(@NonNull LifecycleOwner owner) {}
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {}
+
+    @Override
+    public void onResume(@NonNull LifecycleOwner owner) {}
+
+    @Override
+    public void onPause(@NonNull LifecycleOwner owner) {}
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+      onActivityStopped(thisActivity);
+    }
+
+    @Override
+    public void onDestroy(@NonNull LifecycleOwner owner) {
+      onActivityDestroyed(thisActivity);
+    }
+
+    @Override
+    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+
+    @Override
+    public void onActivityStarted(Activity activity) {}
+
+    @Override
+    public void onActivityResumed(Activity activity) {}
+
+    @Override
+    public void onActivityPaused(Activity activity) {}
+
+    @Override
+    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+      if (thisActivity == activity && activity.getApplicationContext() != null) {
+        ((Application) activity.getApplicationContext())
+            .unregisterActivityLifecycleCallbacks(
+                this); // Use getApplicationContext() to avoid casting failures
+      }
+    }
+
+    @Override
+    public void onActivityStopped(Activity activity) {
+      if (thisActivity == activity) {
+        delegate.saveStateBeforeResult();
+      }
+    }
+  }
 
   static final String METHOD_CALL_IMAGE = "pickImage";
   static final String METHOD_CALL_VIDEO = "pickVideo";
@@ -27,9 +100,15 @@
   private static final int SOURCE_CAMERA = 0;
   private static final int SOURCE_GALLERY = 1;
 
-  private final PluginRegistry.Registrar registrar;
+  private MethodChannel channel;
   private ImagePickerDelegate delegate;
-  private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
+  private FlutterPluginBinding pluginBinding;
+  private ActivityPluginBinding activityBinding;
+  private Application application;
+  private Activity activity;
+  // This is null when not using v2 embedding;
+  private Lifecycle lifecycle;
+  private LifeCycleObserver observer;
 
   public static void registerWith(PluginRegistry.Registrar registrar) {
     if (registrar.activity() == null) {
@@ -37,72 +116,114 @@
       // we stop the registering process immediately because the ImagePicker requires an activity.
       return;
     }
-    final ImagePickerCache cache = new ImagePickerCache(registrar.activity());
-
-    final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL);
-
-    final File externalFilesDirectory =
-        registrar.activity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
-    final ExifDataCopier exifDataCopier = new ExifDataCopier();
-    final ImageResizer imageResizer = new ImageResizer(externalFilesDirectory, exifDataCopier);
-    final ImagePickerDelegate delegate =
-        new ImagePickerDelegate(registrar.activity(), externalFilesDirectory, imageResizer, cache);
-
-    registrar.addActivityResultListener(delegate);
-    registrar.addRequestPermissionsResultListener(delegate);
-    final ImagePickerPlugin instance = new ImagePickerPlugin(registrar, delegate);
-    channel.setMethodCallHandler(instance);
+    Activity activity = registrar.activity();
+    Application application = null;
+    if (registrar.context() != null) {
+      application = (Application) (registrar.context().getApplicationContext());
+    }
+    ImagePickerPlugin plugin = new ImagePickerPlugin();
+    plugin.setup(registrar.messenger(), application, activity, registrar, null);
   }
 
+  /**
+   * Default constructor for the plugin.
+   *
+   * <p>Use this constructor for production code.
+   */
+  // See also: * {@link #ImagePickerPlugin(ImagePickerDelegate, Activity)} for testing.
+  public ImagePickerPlugin() {}
+
   @VisibleForTesting
-  ImagePickerPlugin(final PluginRegistry.Registrar registrar, final ImagePickerDelegate delegate) {
-    this.registrar = registrar;
+  ImagePickerPlugin(final ImagePickerDelegate delegate, final Activity activity) {
     this.delegate = delegate;
-    this.activityLifecycleCallbacks =
-        new Application.ActivityLifecycleCallbacks() {
-          @Override
-          public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+    this.activity = activity;
+  }
 
-          @Override
-          public void onActivityStarted(Activity activity) {}
+  @Override
+  public void onAttachedToEngine(FlutterPluginBinding binding) {
+    pluginBinding = binding;
+  }
 
-          @Override
-          public void onActivityResumed(Activity activity) {}
+  @Override
+  public void onDetachedFromEngine(FlutterPluginBinding binding) {
+    pluginBinding = null;
+  }
 
-          @Override
-          public void onActivityPaused(Activity activity) {}
+  @Override
+  public void onAttachedToActivity(ActivityPluginBinding binding) {
+    activityBinding = binding;
+    setup(
+        pluginBinding.getBinaryMessenger(),
+        (Application) pluginBinding.getApplicationContext(),
+        activityBinding.getActivity(),
+        null,
+        activityBinding);
+  }
 
-          @Override
-          public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
-            if (activity == registrar.activity()) {
-              delegate.saveStateBeforeResult();
-            }
-          }
+  @Override
+  public void onDetachedFromActivity() {
+    tearDown();
+  }
 
-          @Override
-          public void onActivityDestroyed(Activity activity) {
-            if (activity == registrar.activity()
-                && registrar.activity().getApplicationContext() != null) {
-              ((Application) registrar.activity().getApplicationContext())
-                  .unregisterActivityLifecycleCallbacks(
-                      this); // Use getApplicationContext() to avoid casting failures
-            }
-          }
+  @Override
+  public void onDetachedFromActivityForConfigChanges() {
+    onDetachedFromActivity();
+  }
 
-          @Override
-          public void onActivityStopped(Activity activity) {}
-        };
+  @Override
+  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
+    onAttachedToActivity(binding);
+  }
 
-    if (this.registrar != null
-        && this.registrar.context() != null
-        && this.registrar.context().getApplicationContext() != null) {
-      ((Application) this.registrar.context().getApplicationContext())
-          .registerActivityLifecycleCallbacks(
-              this
-                  .activityLifecycleCallbacks); // Use getApplicationContext() to avoid casting failures.
+  private void setup(
+      final BinaryMessenger messenger,
+      final Application application,
+      final Activity activity,
+      final PluginRegistry.Registrar registrar,
+      final ActivityPluginBinding activityBinding) {
+    this.activity = activity;
+    this.application = application;
+    this.delegate = constructDelegate(activity);
+    channel = new MethodChannel(messenger, CHANNEL);
+    channel.setMethodCallHandler(this);
+    observer = new LifeCycleObserver(activity);
+    if (registrar != null) {
+      // V1 embedding setup for activity listeners.
+      application.registerActivityLifecycleCallbacks(observer);
+      registrar.addActivityResultListener(delegate);
+      registrar.addRequestPermissionsResultListener(delegate);
+    } else {
+      // V2 embedding setup for activity listeners.
+      activityBinding.addActivityResultListener(delegate);
+      activityBinding.addRequestPermissionsResultListener(delegate);
+      lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding);
+      lifecycle.addObserver(observer);
     }
   }
 
+  private void tearDown() {
+    activityBinding.removeActivityResultListener(delegate);
+    activityBinding.removeRequestPermissionsResultListener(delegate);
+    activityBinding = null;
+    lifecycle.removeObserver(observer);
+    lifecycle = null;
+    delegate = null;
+    channel.setMethodCallHandler(null);
+    channel = null;
+    application.unregisterActivityLifecycleCallbacks(observer);
+    application = null;
+  }
+
+  private final ImagePickerDelegate constructDelegate(final Activity setupActivity) {
+    final ImagePickerCache cache = new ImagePickerCache(setupActivity);
+
+    final File externalFilesDirectory =
+        setupActivity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+    final ExifDataCopier exifDataCopier = new ExifDataCopier();
+    final ImageResizer imageResizer = new ImageResizer(externalFilesDirectory, exifDataCopier);
+    return new ImagePickerDelegate(setupActivity, externalFilesDirectory, imageResizer, cache);
+  }
+
   // MethodChannel.Result wrapper that responds on the platform thread.
   private static class MethodResultWrapper implements MethodChannel.Result {
     private MethodChannel.Result methodResult;
@@ -150,7 +271,7 @@
 
   @Override
   public void onMethodCall(MethodCall call, MethodChannel.Result rawResult) {
-    if (registrar.activity() == null) {
+    if (activity == null) {
       rawResult.error("no_activity", "image_picker plugin requires a foreground activity.", null);
       return;
     }
diff --git a/packages/image_picker/example/android/app/src/main/AndroidManifest.xml b/packages/image_picker/example/android/app/src/main/AndroidManifest.xml
index fa2b500..5de9f04 100755
--- a/packages/image_picker/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/image_picker/example/android/app/src/main/AndroidManifest.xml
@@ -4,8 +4,7 @@
     <uses-permission android:name="android.permission.INTERNET"/>
 
     <application android:name="io.flutter.app.FlutterApplication" android:label="Image Picker Example" android:icon="@mipmap/ic_launcher">
-        <activity android:name=".MainActivity"
-                  android:launchMode="singleTop"
+        <activity android:name="io.flutter.embedding.android.FlutterActivity"
                   android:theme="@android:style/Theme.Black.NoTitleBar"
                   android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
                   android:hardwareAccelerated="true"
@@ -15,5 +14,12 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+            <activity
+            android:name=".EmbeddingV1Activity"
+                  android:theme="@android:style/Theme.Black.NoTitleBar"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+                  android:hardwareAccelerated="true"
+                  android:windowSoftInputMode="adjustResize">
+        </activity>
     </application>
 </manifest>
diff --git a/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/MainActivity.java b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1Activity.java
similarity index 78%
rename from packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/MainActivity.java
rename to packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1Activity.java
index 4690ebc..79c1ca6 100644
--- a/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/MainActivity.java
+++ b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1Activity.java
@@ -1,4 +1,4 @@
-// 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.
 
@@ -8,8 +8,7 @@
 import io.flutter.app.FlutterActivity;
 import io.flutter.plugins.GeneratedPluginRegistrant;
 
-public class MainActivity extends FlutterActivity {
-
+public class EmbeddingV1Activity extends FlutterActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
diff --git a/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1ActivityTest.java b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1ActivityTest.java
new file mode 100644
index 0000000..924cf2f
--- /dev/null
+++ b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/EmbeddingV1ActivityTest.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.imagepickerexample;
+
+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/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java
new file mode 100644
index 0000000..df9794c
--- /dev/null
+++ b/packages/image_picker/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java
@@ -0,0 +1,13 @@
+package io.flutter.plugins.imagepickerexample;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.e2e.FlutterRunner;
+import io.flutter.embedding.android.FlutterActivity;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(FlutterRunner.class)
+public class FlutterActivityTest {
+  @Rule
+  public ActivityTestRule<FlutterActivity> rule = new ActivityTestRule<>(FlutterActivity.class);
+}
diff --git a/packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
index 94a81d3..acfd064 100644
--- a/packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
+++ b/packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
@@ -40,16 +40,15 @@
     MockitoAnnotations.initMocks(this);
     when(mockRegistrar.context()).thenReturn(mockApplication);
 
-    plugin = new ImagePickerPlugin(mockRegistrar, mockImagePickerDelegate);
+    plugin = new ImagePickerPlugin(mockImagePickerDelegate, mockActivity);
   }
 
   @Test
   public void onMethodCall_WhenActivityIsNull_FinishesWithForegroundActivityRequiredError() {
-    when(mockRegistrar.activity()).thenReturn(null);
     MethodCall call = buildMethodCall(SOURCE_GALLERY);
-
-    plugin.onMethodCall(call, mockResult);
-
+    ImagePickerPlugin imagePickerPluginWithNullActivity =
+        new ImagePickerPlugin(mockImagePickerDelegate, null);
+    imagePickerPluginWithNullActivity.onMethodCall(call, mockResult);
     verify(mockResult)
         .error("no_activity", "image_picker plugin requires a foreground activity.", null);
     verifyZeroInteractions(mockImagePickerDelegate);
@@ -57,46 +56,34 @@
 
   @Test
   public void onMethodCall_WhenCalledWithUnknownMethod_ThrowsException() {
-    when(mockRegistrar.activity()).thenReturn(mockActivity);
     exception.expect(IllegalArgumentException.class);
     exception.expectMessage("Unknown method test");
-
     plugin.onMethodCall(new MethodCall("test", null), mockResult);
-
     verifyZeroInteractions(mockImagePickerDelegate);
     verifyZeroInteractions(mockResult);
   }
 
   @Test
   public void onMethodCall_WhenCalledWithUnknownImageSource_ThrowsException() {
-    when(mockRegistrar.activity()).thenReturn(mockActivity);
     exception.expect(IllegalArgumentException.class);
     exception.expectMessage("Invalid image source: -1");
-
     plugin.onMethodCall(buildMethodCall(-1), mockResult);
-
     verifyZeroInteractions(mockImagePickerDelegate);
     verifyZeroInteractions(mockResult);
   }
 
   @Test
   public void onMethodCall_WhenSourceIsGallery_InvokesChooseImageFromGallery() {
-    when(mockRegistrar.activity()).thenReturn(mockActivity);
     MethodCall call = buildMethodCall(SOURCE_GALLERY);
-
     plugin.onMethodCall(call, mockResult);
-
     verify(mockImagePickerDelegate).chooseImageFromGallery(eq(call), any());
     verifyZeroInteractions(mockResult);
   }
 
   @Test
   public void onMethodCall_WhenSourceIsCamera_InvokesTakeImageWithCamera() {
-    when(mockRegistrar.activity()).thenReturn(mockActivity);
     MethodCall call = buildMethodCall(SOURCE_CAMERA);
-
     plugin.onMethodCall(call, mockResult);
-
     verify(mockImagePickerDelegate).takeImageWithCamera(eq(call), any());
     verifyZeroInteractions(mockResult);
   }
@@ -111,8 +98,7 @@
 
   @Test
   public void onConstructor_WhenContextTypeIsActivity_ShouldNotCrash() {
-    when(mockRegistrar.context()).thenReturn(mockActivity);
-    new ImagePickerPlugin(mockRegistrar, mockImagePickerDelegate);
+    new ImagePickerPlugin(mockImagePickerDelegate, mockActivity);
     assertTrue(
         "No exception thrown when ImagePickerPlugin() ran with context instanceof Activity", true);
   }
diff --git a/packages/image_picker/example/lib/main.dart b/packages/image_picker/example/lib/main.dart
index 3ece64b..919c838 100755
--- a/packages/image_picker/example/lib/main.dart
+++ b/packages/image_picker/example/lib/main.dart
@@ -305,13 +305,13 @@
               FlatButton(
                   child: const Text('PICK'),
                   onPressed: () {
-                    double width = maxWidthController.text.length > 0
+                    double width = maxWidthController.text.isNotEmpty
                         ? double.parse(maxWidthController.text)
                         : null;
-                    double height = maxHeightController.text.length > 0
+                    double height = maxHeightController.text.isNotEmpty
                         ? double.parse(maxHeightController.text)
                         : null;
-                    int quality = qualityController.text.length > 0
+                    int quality = qualityController.text.isNotEmpty
                         ? int.parse(qualityController.text)
                         : null;
                     onPick(width, height, quality);
diff --git a/packages/image_picker/example/pubspec.yaml b/packages/image_picker/example/pubspec.yaml
index 1f793d7..b84ee9f 100755
--- a/packages/image_picker/example/pubspec.yaml
+++ b/packages/image_picker/example/pubspec.yaml
@@ -3,12 +3,22 @@
 author: Flutter Team <flutter-dev@googlegroups.com>
 
 dependencies:
-  video_player: 0.10.1+5
+  video_player: ^0.10.3
   flutter:
     sdk: flutter
   image_picker:
     path: ../
+  flutter_plugin_android_lifecycle: ^1.0.2
+
+dev_dependencies:
+  flutter_driver:
+    sdk: flutter
+  e2e:  ^0.2.1
 
 flutter:
   uses-material-design: true
 
+environment:
+  sdk: ">=2.0.0-dev.28.0 <3.0.0"
+  flutter: ">=1.10.0 <2.0.0"
+  
diff --git a/packages/image_picker/example/test_driver/test/image_picker_e2e_test.dart b/packages/image_picker/example/test_driver/test/image_picker_e2e_test.dart
new file mode 100644
index 0000000..f3aa9e2
--- /dev/null
+++ b/packages/image_picker/example/test_driver/test/image_picker_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));
+  await driver.close();
+  exit(result == 'pass' ? 0 : 1);
+}
diff --git a/packages/image_picker/pubspec.yaml b/packages/image_picker/pubspec.yaml
index 217764c..5b88c71 100755
--- a/packages/image_picker/pubspec.yaml
+++ b/packages/image_picker/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Flutter plugin for selecting images from the Android and iOS image
   library, and taking new pictures with the camera.
 homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker
-version: 0.6.2+3
+version: 0.6.3
 
 flutter:
   plugin:
@@ -16,11 +16,13 @@
 dependencies:
   flutter:
     sdk: flutter
+  flutter_plugin_android_lifecycle: ^1.0.2
 
 dev_dependencies:
-  video_player: 0.10.1+5
+  video_player: ^0.10.3
   flutter_test:
     sdk: flutter
+  e2e: ^0.2.1
 
 environment:
   sdk: ">=2.0.0-dev.28.0 <3.0.0"
diff --git a/packages/image_picker/test/image_picker_e2e.dart b/packages/image_picker/test/image_picker_e2e.dart
new file mode 100644
index 0000000..b19e37d
--- /dev/null
+++ b/packages/image_picker/test/image_picker_e2e.dart
@@ -0,0 +1,5 @@
+import 'package:e2e/e2e.dart';
+
+void main() {
+  E2EWidgetsFlutterBinding.ensureInitialized();
+}