[webview_flutter] Fix input bug on route changes (#1853)

Fixes bug where the InputConnection proxy view was persisting across
multiple WebView creation calls.
diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md
index de0fba5..bb09f98 100644
--- a/packages/webview_flutter/CHANGELOG.md
+++ b/packages/webview_flutter/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.10+2
+
+* Fix InputConnection being lost when combined with route transitions.
+
 ## 0.3.10+1
 
 * Add support for simultaenous Flutter `TextInput` and WebView text fields.
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
index 327434d..8389877 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
@@ -261,5 +261,6 @@
   @Override
   public void dispose() {
     methodChannel.setMethodCallHandler(null);
+    webView.dispose();
   }
 }
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
index 1748be1..da4d81e 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
@@ -53,10 +53,15 @@
     imm.restartInput(containerView);
   }
 
+  /** Restore the original InputConnection, if needed. */
+  void dispose() {
+    resetInputConnection();
+  }
+
   /**
    * Creates an InputConnection from the IME thread when needed.
    *
-   * <p>We only need to create a {@link ThreadedInputConnectionProxy} and create an
+   * <p>We only need to create a {@link ThreadedInputConnectionProxyAdapterView} and create an
    * InputConnectionProxy on the IME thread when WebView is doing the same thing. So we rely on the
    * system calling this method for WebView's proxy view in order to know when we need to create our
    * own.
@@ -84,32 +89,7 @@
             /*containerView=*/ containerView,
             /*targetView=*/ view,
             /*imeHandler=*/ view.getHandler());
-    final View container = this;
-    proxyAdapterView.requestFocus();
-    // This is the crucial trick that gets the InputConnection creation to happen on the correct
-    // thread.
-    // https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java?l=169&rcl=f0698ee3e4483fad5b0c34159276f71cfaf81f3a
-    post(
-        new Runnable() {
-          @Override
-          public void run() {
-            InputMethodManager imm =
-                (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
-            // This is a hack to make InputMethodManager believe that the proxy view now has focus.
-            // As a result, InputMethodManager will think that proxyAdapterView is focused, and will
-            // call getHandler() of the view when creating input connection.
-
-            // Step 1: Set proxyAdapterView as InputMethodManager#mNextServedView. This does not
-            // affect the real window focus.
-            proxyAdapterView.onWindowFocusChanged(true);
-
-            // Step 2: Have InputMethodManager focus in on proxyAdapterView. As a result, IMM will
-            // call onCreateInputConnection() on proxyAdapterView on the same thread as
-            // proxyAdapterView.getHandler(). It will also call subsequent InputConnection methods
-            // on this IME thread.
-            imm.isActive(containerView);
-          }
-        });
+    setInputConnectionTarget(/*targetView=*/ proxyAdapterView);
     return super.checkInputConnectionProxy(view);
   }
 
@@ -124,18 +104,52 @@
   @Override
   public void clearFocus() {
     super.clearFocus();
+    resetInputConnection();
+  }
+
+  /**
+   * Ensure that input creation happens back on {@link #containerView}.
+   *
+   * <p>The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's
+   * thread for all connections. We undo it here so users will be able to go back to typing in
+   * Flutter UIs as expected.
+   */
+  private void resetInputConnection() {
     if (proxyAdapterView == null) {
       // No need to reset the InputConnection to the default thread if we've never changed it.
       return;
     }
-    containerView.requestFocus();
+    setInputConnectionTarget(/*targetView=*/ containerView);
+  }
+
+  /**
+   * This is the crucial trick that gets the InputConnection creation to happen on the correct
+   * thread pre Android N.
+   * https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java?l=169&rcl=f0698ee3e4483fad5b0c34159276f71cfaf81f3a
+   *
+   * <p>{@code targetView} should have a {@link View#getHandler} method with the thread that future
+   * InputConnections should be created on.
+   */
+  private void setInputConnectionTarget(final View targetView) {
+    targetView.requestFocus();
     containerView.post(
         new Runnable() {
           @Override
           public void run() {
-            containerView.onWindowFocusChanged(true);
             InputMethodManager imm =
                 (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
+            // This is a hack to make InputMethodManager believe that the target view now has focus.
+            // As a result, InputMethodManager will think that targetView is focused, and will call
+            // getHandler() of the view when creating input connection.
+
+            // Step 1: Set targetView as InputMethodManager#mNextServedView. This does not affect
+            // the real window focus.
+            targetView.onWindowFocusChanged(true);
+
+            // Step 2: Have InputMethodManager focus in on targetView. As a result, IMM will call
+            // onCreateInputConnection() on targetView on the same thread as
+            // targetView.getHandler(). It will also call subsequent InputConnection methods on this
+            // thread. This is the IME thread in cases where targetView is our proxyAdapterView.
             imm.isActive(containerView);
           }
         });
diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml
index 236a42e..b1aa092 100644
--- a/packages/webview_flutter/pubspec.yaml
+++ b/packages/webview_flutter/pubspec.yaml
@@ -1,6 +1,6 @@
 name: webview_flutter
 description: A Flutter plugin that provides a WebView widget on Android and iOS.
-version: 0.3.10+1
+version: 0.3.10+2
 author: Flutter Team <flutter-dev@googlegroups.com>
 homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter