[Share] Support v2 android embedder. (#2156)

diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md
index 087c66b..fa8b261 100644
--- a/packages/share/CHANGELOG.md
+++ b/packages/share/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.6.3
+
+* Support the v2 Android embedder.
+* Update to AndroidX.
+* Migrate to using the new e2e test binding.
+* Add a e2e test.
+
 ## 0.6.2+4
 
 * Define clang module for iOS.
diff --git a/packages/share/android/build.gradle b/packages/share/android/build.gradle
index 3b265e6..1461873 100644
--- a/packages/share/android/build.gradle
+++ b/packages/share/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/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java b/packages/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java
new file mode 100644
index 0000000..f7e4d57
--- /dev/null
+++ b/packages/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java
@@ -0,0 +1,33 @@
+// 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.
+
+package io.flutter.plugins.share;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import java.util.Map;
+
+/** Handles the method calls for the plugin. */
+class MethodCallHandler implements MethodChannel.MethodCallHandler {
+
+  private Share share;
+
+  MethodCallHandler(Share share) {
+    this.share = share;
+  }
+
+  @Override
+  public void onMethodCall(MethodCall call, MethodChannel.Result result) {
+    if (call.method.equals("share")) {
+      if (!(call.arguments instanceof Map)) {
+        throw new IllegalArgumentException("Map argument expected");
+      }
+      // Android does not support showing the share sheet at a particular point on screen.
+      share.share((String) call.argument("text"), (String) call.argument("subject"));
+      result.success(null);
+    } else {
+      result.notImplemented();
+    }
+  }
+}
diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java b/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java
new file mode 100644
index 0000000..8c9e833
--- /dev/null
+++ b/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java
@@ -0,0 +1,50 @@
+// 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.
+
+package io.flutter.plugins.share;
+
+import android.app.Activity;
+import android.content.Intent;
+
+/** Handles share intent. */
+class Share {
+
+  private Activity activity;
+
+  /**
+   * Constructs a Share object. The {@code activity} is used to start the share intent. It might be
+   * null when constructing the {@link Share} object and set to non-null when an activity is
+   * available using {@link #setActivity(Activity)}.
+   */
+  Share(Activity activity) {
+    this.activity = activity;
+  }
+
+  /**
+   * Sets the activity when an activity is available. When the activity becomes unavailable, use
+   * this method to set it to null.
+   */
+  void setActivity(Activity activity) {
+    this.activity = activity;
+  }
+
+  void share(String text, String subject) {
+    if (text == null || text.isEmpty()) {
+      throw new IllegalArgumentException("Non-empty text expected");
+    }
+
+    Intent shareIntent = new Intent();
+    shareIntent.setAction(Intent.ACTION_SEND);
+    shareIntent.putExtra(Intent.EXTRA_TEXT, text);
+    shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+    shareIntent.setType("text/plain");
+    Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */);
+    if (activity != null) {
+      activity.startActivity(chooserIntent);
+    } else {
+      chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+      activity.startActivity(chooserIntent);
+    }
+  }
+}
diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java b/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java
index 60b83e4..443f28e 100644
--- a/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java
+++ b/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java
@@ -4,59 +4,68 @@
 
 package io.flutter.plugins.share;
 
-import android.content.Intent;
-import io.flutter.plugin.common.MethodCall;
+import android.app.Activity;
+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.plugin.common.BinaryMessenger;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.PluginRegistry.Registrar;
-import java.util.Map;
 
 /** Plugin method host for presenting a share sheet via Intent */
-public class SharePlugin implements MethodChannel.MethodCallHandler {
+public class SharePlugin implements FlutterPlugin, ActivityAware {
 
   private static final String CHANNEL = "plugins.flutter.io/share";
+  private MethodCallHandler handler;
+  private Share share;
+  private MethodChannel methodChannel;
 
   public static void registerWith(Registrar registrar) {
-    MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL);
-    SharePlugin instance = new SharePlugin(registrar);
-    channel.setMethodCallHandler(instance);
-  }
-
-  private final Registrar mRegistrar;
-
-  private SharePlugin(Registrar registrar) {
-    this.mRegistrar = registrar;
+    SharePlugin plugin = new SharePlugin();
+    plugin.setUpChannel(registrar.activity(), registrar.messenger());
   }
 
   @Override
-  public void onMethodCall(MethodCall call, MethodChannel.Result result) {
-    if (call.method.equals("share")) {
-      if (!(call.arguments instanceof Map)) {
-        throw new IllegalArgumentException("Map argument expected");
-      }
-      // Android does not support showing the share sheet at a particular point on screen.
-      share((String) call.argument("text"), (String) call.argument("subject"));
-      result.success(null);
-    } else {
-      result.notImplemented();
-    }
+  public void onAttachedToEngine(FlutterPluginBinding binding) {
+    setUpChannel(null, binding.getFlutterEngine().getDartExecutor());
   }
 
-  private void share(String text, String subject) {
-    if (text == null || text.isEmpty()) {
-      throw new IllegalArgumentException("Non-empty text expected");
-    }
+  @Override
+  public void onDetachedFromEngine(FlutterPluginBinding binding) {
+    methodChannel.setMethodCallHandler(null);
+    methodChannel = null;
+    share = null;
+  }
 
-    Intent shareIntent = new Intent();
-    shareIntent.setAction(Intent.ACTION_SEND);
-    shareIntent.putExtra(Intent.EXTRA_TEXT, text);
-    shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
-    shareIntent.setType("text/plain");
-    Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */);
-    if (mRegistrar.activity() != null) {
-      mRegistrar.activity().startActivity(chooserIntent);
-    } else {
-      chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-      mRegistrar.context().startActivity(chooserIntent);
-    }
+  @Override
+  public void onAttachedToActivity(ActivityPluginBinding binding) {
+    share.setActivity(binding.getActivity());
+  }
+
+  @Override
+  public void onDetachedFromActivity() {
+    tearDownChannel();
+  }
+
+  @Override
+  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
+    onAttachedToActivity(binding);
+  }
+
+  @Override
+  public void onDetachedFromActivityForConfigChanges() {
+    onDetachedFromActivity();
+  }
+
+  private void setUpChannel(Activity activity, BinaryMessenger messenger) {
+    methodChannel = new MethodChannel(messenger, CHANNEL);
+    share = new Share(activity);
+    handler = new MethodCallHandler(share);
+    methodChannel.setMethodCallHandler(handler);
+  }
+
+  private void tearDownChannel() {
+    share.setActivity(null);
+    methodChannel.setMethodCallHandler(null);
   }
 }
diff --git a/packages/share/example/android/app/src/main/AndroidManifest.xml b/packages/share/example/android/app/src/main/AndroidManifest.xml
index a86d072..d5e5ec8 100644
--- a/packages/share/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/share/example/android/app/src/main/AndroidManifest.xml
@@ -4,12 +4,18 @@
     <uses-permission android:name="android.permission.INTERNET"/>
 
     <application android:name="io.flutter.app.FlutterApplication" android:label="share_example" android:icon="@mipmap/ic_launcher">
-        <activity android:name=".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: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/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1Activity.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1Activity.java
new file mode 100644
index 0000000..736ac54
--- /dev/null
+++ b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1Activity.java
@@ -0,0 +1,18 @@
+// 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.shareexample;
+
+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/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1ActivityTest.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1ActivityTest.java
new file mode 100644
index 0000000..9585411
--- /dev/null
+++ b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/EmbeddingV1ActivityTest.java
@@ -0,0 +1,13 @@
+package io.flutter.plugins.shareexample;
+
+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/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivity.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivity.java
index 89d8bb2..3717feb 100644
--- a/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivity.java
+++ b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivity.java
@@ -1,18 +1,19 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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.
 
 package io.flutter.plugins.shareexample;
 
-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.share.SharePlugin;
 
 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 SharePlugin());
   }
 }
diff --git a/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivityTest.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/MainActivityTest.java
new file mode 100644
index 0000000..fcd936a
--- /dev/null
+++ b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/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.shareexample;
+
+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/share/example/android/gradle.properties b/packages/share/example/android/gradle.properties
index 8bd86f6..38c8d45 100644
--- a/packages/share/example/android/gradle.properties
+++ b/packages/share/example/android/gradle.properties
@@ -1 +1,4 @@
 org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/share/example/pubspec.yaml b/packages/share/example/pubspec.yaml
index f28c6dc..4e4fc49 100644
--- a/packages/share/example/pubspec.yaml
+++ b/packages/share/example/pubspec.yaml
@@ -7,5 +7,15 @@
   share:
     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/share/example/test_driver/test/share_e2e_test.dart b/packages/share/example/test_driver/test/share_e2e_test.dart
new file mode 100644
index 0000000..ff6e9ce
--- /dev/null
+++ b/packages/share/example/test_driver/test/share_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/share/pubspec.yaml b/packages/share/pubspec.yaml
index 800f1b4..d569cbf 100644
--- a/packages/share/pubspec.yaml
+++ b/packages/share/pubspec.yaml
@@ -3,7 +3,7 @@
   the ACTION_SEND intent on Android and UIActivityViewController on iOS.
 author: Flutter Team <flutter-dev@googlegroups.com>
 homepage: https://github.com/flutter/plugins/tree/master/packages/share
-version: 0.6.2+4
+version: 0.6.3
 
 flutter:
   plugin:
@@ -21,7 +21,8 @@
   mockito: ^3.0.0
   flutter_test:
     sdk: flutter
+  e2e: ^0.2.0
 
 environment:
   sdk: ">=2.0.0-dev.28.0 <3.0.0"
-  flutter: ">=1.6.0 <2.0.0"
+  flutter: ">=1.6.7 <2.0.0"
diff --git a/packages/share/test/share_e2e.dart b/packages/share/test/share_e2e.dart
new file mode 100644
index 0000000..eb99022
--- /dev/null
+++ b/packages/share/test/share_e2e.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 'package:flutter_test/flutter_test.dart';
+import 'package:share/share.dart';
+import 'package:e2e/e2e.dart';
+
+void main() {
+  E2EWidgetsFlutterBinding.ensureInitialized();
+
+  testWidgets('Can launch share', (WidgetTester tester) async {
+    expect(Share.share('message', subject: 'title'), completes);
+  });
+}