Browse Source

add javascriptChannelMessage for android

rin.lv 6 years ago
parent
commit
8357409f08

+ 1 - 1
android/src/main/AndroidManifest.xml

@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="com.flutter_webview_plugin">
-    <uses-sdk android:minSdkVersion="16" />
+
     <application>
         <provider
             android:name="androidx.core.content.FileProvider"

+ 10 - 2
android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

@@ -11,6 +11,8 @@ import android.webkit.CookieManager;
 import android.webkit.ValueCallback;
 import android.os.Build;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import io.flutter.plugin.common.MethodCall;
@@ -27,10 +29,11 @@ public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.A
     private Context context;
     static MethodChannel channel;
     private static final String CHANNEL_NAME = "flutter_webview_plugin";
+    private static final String JS_CHANNEL_NAMES_FIELD = "javascriptChannelNames";
 
     public static void registerWith(PluginRegistry.Registrar registrar) {
         channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
-        final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity(),registrar.activeContext());
+        final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity(), registrar.activeContext());
         registrar.addActivityResultListener(instance);
         channel.setMethodCallHandler(instance);
     }
@@ -107,7 +110,12 @@ public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.A
         boolean debuggingEnabled = call.argument("debuggingEnabled");
 
         if (webViewManager == null || webViewManager.closed == true) {
-            webViewManager = new WebviewManager(activity, context);
+            Map<String, Object> arguments = (Map<String, Object>) call.arguments;
+            List<String> channelNames = new ArrayList();
+            if (arguments.containsKey(JS_CHANNEL_NAMES_FIELD)) {
+                channelNames = (List<String>) arguments.get(JS_CHANNEL_NAMES_FIELD);
+            }
+            webViewManager = new WebviewManager(activity, context, channelNames);
         }
 
         FrameLayout.LayoutParams params = buildLayoutParams(call);

+ 60 - 0
android/src/main/java/com/flutter_webview_plugin/JavaScriptChannel.java

@@ -0,0 +1,60 @@
+// 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 com.flutter_webview_plugin;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.webkit.JavascriptInterface;
+
+import java.util.HashMap;
+
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * Added as a JavaScript interface to the WebView for any JavaScript channel that the Dart code sets
+ * up.
+ *
+ * <p>Exposes a single method named `postMessage` to JavaScript, which sends a message over a method
+ * channel to the Dart code.
+ */
+class JavaScriptChannel {
+  private final MethodChannel methodChannel;
+  private final String javaScriptChannelName;
+  private final Handler platformThreadHandler;
+
+  /**
+   * @param methodChannel the Flutter WebView method channel to which JS messages are sent
+   * @param javaScriptChannelName the name of the JavaScript channel, this is sent over the method
+   *     channel with each message to let the Dart code know which JavaScript channel the message
+   *     was sent through
+   */
+  JavaScriptChannel(
+      MethodChannel methodChannel, String javaScriptChannelName, Handler platformThreadHandler) {
+    this.methodChannel = methodChannel;
+    this.javaScriptChannelName = javaScriptChannelName;
+    this.platformThreadHandler = platformThreadHandler;
+  }
+
+  // Suppressing unused warning as this is invoked from JavaScript.
+  @SuppressWarnings("unused")
+  @JavascriptInterface
+  public void postMessage(final String message) {
+    Runnable postMessageRunnable =
+        new Runnable() {
+          @Override
+          public void run() {
+            HashMap<String, String> arguments = new HashMap<>();
+            arguments.put("channel", javaScriptChannelName);
+            arguments.put("message", message);
+            methodChannel.invokeMethod("javascriptChannelMessage", arguments);
+          }
+        };
+    if (platformThreadHandler.getLooper() == Looper.myLooper()) {
+      postMessageRunnable.run();
+    } else {
+      platformThreadHandler.post(postMessageRunnable);
+    }
+  }
+}

+ 58 - 39
android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

@@ -6,6 +6,7 @@ import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.os.Build;
+import android.os.Handler;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -17,7 +18,9 @@ import android.webkit.WebSettings;
 import android.webkit.WebView;
 import android.widget.FrameLayout;
 import android.provider.MediaStore;
+
 import androidx.core.content.FileProvider;
+
 import android.database.Cursor;
 import android.provider.OpenableColumns;
 
@@ -43,7 +46,7 @@ class WebviewManager {
 
     private ValueCallback<Uri> mUploadMessage;
     private ValueCallback<Uri[]> mUploadMessageArray;
-    private final static int FILECHOOSER_RESULTCODE=1;
+    private final static int FILECHOOSER_RESULTCODE = 1;
     private Uri fileUri;
     private Uri videoUri;
 
@@ -56,29 +59,29 @@ class WebviewManager {
 
     @TargetApi(7)
     class ResultHandler {
-        public boolean handleResult(int requestCode, int resultCode, Intent intent){
+        public boolean handleResult(int requestCode, int resultCode, Intent intent) {
             boolean handled = false;
-            if(Build.VERSION.SDK_INT >= 21){
-                if(requestCode == FILECHOOSER_RESULTCODE){
+            if (Build.VERSION.SDK_INT >= 21) {
+                if (requestCode == FILECHOOSER_RESULTCODE) {
                     Uri[] results = null;
                     if (resultCode == Activity.RESULT_OK) {
                         if (fileUri != null && getFileSize(fileUri) > 0) {
-                            results = new Uri[] { fileUri };
+                            results = new Uri[]{fileUri};
                         } else if (videoUri != null && getFileSize(videoUri) > 0) {
-                            results = new Uri[] { videoUri };
+                            results = new Uri[]{videoUri};
                         } else if (intent != null) {
                             results = getSelectedFiles(intent);
                         }
                     }
-                    if(mUploadMessageArray != null){
+                    if (mUploadMessageArray != null) {
                         mUploadMessageArray.onReceiveValue(results);
                         mUploadMessageArray = null;
                     }
                     handled = true;
                 }
-            }else {
+            } else {
                 if (requestCode == FILECHOOSER_RESULTCODE) {
-                	Uri result = null;
+                    Uri result = null;
                     if (resultCode == RESULT_OK && intent != null) {
                         result = intent.getData();
                     }
@@ -97,8 +100,8 @@ class WebviewManager {
         // we have one files selected
         if (data.getData() != null) {
             String dataString = data.getDataString();
-            if(dataString != null){
-                return new Uri[]{ Uri.parse(dataString) };
+            if (dataString != null) {
+                return new Uri[]{Uri.parse(dataString)};
             }
         }
         // we have multiple files selected
@@ -113,6 +116,7 @@ class WebviewManager {
         return null;
     }
 
+    private final Handler platformThreadHandler;
     boolean closed = false;
     WebView webView;
     Activity activity;
@@ -120,11 +124,12 @@ class WebviewManager {
     ResultHandler resultHandler;
     Context context;
 
-    WebviewManager(final Activity activity, final Context context) {
+    WebviewManager(final Activity activity, final Context context, final List<String> channelNames) {
         this.webView = new ObservableWebView(activity);
         this.activity = activity;
         this.context = context;
         this.resultHandler = new ResultHandler();
+        this.platformThreadHandler = new Handler(context.getMainLooper());
         webViewClient = new BrowserClient();
         webView.setOnKeyListener(new View.OnKeyListener() {
             @Override
@@ -145,20 +150,19 @@ class WebviewManager {
             }
         });
 
-        ((ObservableWebView) webView).setOnScrollChangedCallback(new ObservableWebView.OnScrollChangedCallback(){
-            public void onScroll(int x, int y, int oldx, int oldy){
+        ((ObservableWebView) webView).setOnScrollChangedCallback(new ObservableWebView.OnScrollChangedCallback() {
+            public void onScroll(int x, int y, int oldx, int oldy) {
                 Map<String, Object> yDirection = new HashMap<>();
-                yDirection.put("yDirection", (double)y);
+                yDirection.put("yDirection", (double) y);
                 FlutterWebviewPlugin.channel.invokeMethod("onScrollYChanged", yDirection);
                 Map<String, Object> xDirection = new HashMap<>();
-                xDirection.put("xDirection", (double)x);
+                xDirection.put("xDirection", (double) x);
                 FlutterWebviewPlugin.channel.invokeMethod("onScrollXChanged", xDirection);
             }
         });
 
         webView.setWebViewClient(webViewClient);
-        webView.setWebChromeClient(new WebChromeClient()
-        {
+        webView.setWebChromeClient(new WebChromeClient() {
             //The undocumented magic method override
             //Eclipse will swear at you if you try to put @Override here
             // For Android 3.0+
@@ -168,36 +172,36 @@ class WebviewManager {
                 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                 i.addCategory(Intent.CATEGORY_OPENABLE);
                 i.setType("image/*");
-                activity.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
+                activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
 
             }
 
             // For Android 3.0+
-            public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
+            public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
                 mUploadMessage = uploadMsg;
                 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                 i.addCategory(Intent.CATEGORY_OPENABLE);
                 i.setType("*/*");
-               activity.startActivityForResult(
+                activity.startActivityForResult(
                         Intent.createChooser(i, "File Browser"),
                         FILECHOOSER_RESULTCODE);
             }
 
             //For Android 4.1
-            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
+            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                 mUploadMessage = uploadMsg;
                 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                 i.addCategory(Intent.CATEGORY_OPENABLE);
                 i.setType("image/*");
-                activity.startActivityForResult( Intent.createChooser( i, "File Chooser" ), FILECHOOSER_RESULTCODE );
+                activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
 
             }
 
             //For Android 5.0+
             public boolean onShowFileChooser(
                     WebView webView, ValueCallback<Uri[]> filePathCallback,
-                    FileChooserParams fileChooserParams){
-                if(mUploadMessageArray != null){
+                    FileChooserParams fileChooserParams) {
+                if (mUploadMessageArray != null) {
                     mUploadMessageArray.onReceiveValue(null);
                 }
                 mUploadMessageArray = filePathCallback;
@@ -248,6 +252,7 @@ class WebviewManager {
                 callback.invoke(origin, true, false);
             }
         });
+        registerJavaScriptChannelNames(channelNames);
     }
 
     private Uri getOutputFilename(String intentType) {
@@ -334,6 +339,13 @@ class WebviewManager {
         webView.clearFormData();
     }
 
+    private void registerJavaScriptChannelNames(List<String> channelNames) {
+        for (String channelName : channelNames) {
+            webView.addJavascriptInterface(
+                    new JavaScriptChannel(FlutterWebviewPlugin.channel, channelName, platformThreadHandler), channelName);
+        }
+    }
+
     void openUrl(
             boolean withJavascript,
             boolean clearCache,
@@ -371,7 +383,7 @@ class WebviewManager {
         webView.getSettings().setAllowUniversalAccessFromFileURLs(allowFileURLs);
 
         webView.getSettings().setUseWideViewPort(useWideViewPort);
-        
+
         // Handle debugging
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
             webView.setWebContentsDebuggingEnabled(debuggingEnabled);
@@ -403,7 +415,7 @@ class WebviewManager {
             webView.getSettings().setUserAgentString(userAgent);
         }
 
-        if(!scrollBar){
+        if (!scrollBar) {
             webView.setVerticalScrollBarEnabled(false);
         }
 
@@ -451,25 +463,28 @@ class WebviewManager {
             }
         });
     }
+
     /**
-    * Reloads the Webview.
-    */
+     * Reloads the Webview.
+     */
     void reload(MethodCall call, MethodChannel.Result result) {
         if (webView != null) {
             webView.reload();
         }
     }
+
     /**
-    * Navigates back on the Webview.
-    */
+     * Navigates back on the Webview.
+     */
     void back(MethodCall call, MethodChannel.Result result) {
         if (webView != null && webView.canGoBack()) {
             webView.goBack();
         }
     }
+
     /**
-    * Navigates forward on the Webview.
-    */
+     * Navigates forward on the Webview.
+     */
     void forward(MethodCall call, MethodChannel.Result result) {
         if (webView != null && webView.canGoForward()) {
             webView.goForward();
@@ -479,31 +494,35 @@ class WebviewManager {
     void resize(FrameLayout.LayoutParams params) {
         webView.setLayoutParams(params);
     }
+
     /**
-    * Checks if going back on the Webview is possible.
-    */
+     * Checks if going back on the Webview is possible.
+     */
     boolean canGoBack() {
         return webView.canGoBack();
     }
+
     /**
-    * Checks if going forward on the Webview is possible.
-    */
+     * Checks if going forward on the Webview is possible.
+     */
     boolean canGoForward() {
         return webView.canGoForward();
     }
+
     void hide(MethodCall call, MethodChannel.Result result) {
         if (webView != null) {
             webView.setVisibility(View.GONE);
         }
     }
+
     void show(MethodCall call, MethodChannel.Result result) {
         if (webView != null) {
             webView.setVisibility(View.VISIBLE);
         }
     }
 
-    void stopLoading(MethodCall call, MethodChannel.Result result){
-        if (webView != null){
+    void stopLoading(MethodCall call, MethodChannel.Result result) {
+        if (webView != null) {
             webView.stopLoading();
         }
     }

+ 3 - 2
example/android/build.gradle

@@ -32,6 +32,7 @@ task clean(type: Delete) {
     delete rootProject.buildDir
 }
 
-task wrapper(type: Wrapper) {
-    gradleVersion = '4.10.2'
+wrapper {
+    gradleVersion = '4.4'
+    distributionUrl = distributionUrl.replace("bin", "all")
 }

+ 22 - 6
lib/src/base.dart

@@ -31,6 +31,7 @@ class FlutterWebviewPlugin {
   final _onScrollYChanged = StreamController<double>.broadcast();
   final _onProgressChanged = new StreamController<double>.broadcast();
   final _onHttpError = StreamController<WebViewHttpError>.broadcast();
+  final _onPostMessage = StreamController<Map<String, String>>.broadcast();
 
   Future<Null> _handleMessages(MethodCall call) async {
     switch (call.method) {
@@ -49,8 +50,8 @@ class FlutterWebviewPlugin {
       case 'onScrollYChanged':
         _onScrollYChanged.add(call.arguments['yDirection']);
         break;
-      case "onProgressChanged":
-        _onProgressChanged.add(call.arguments["progress"]);
+      case 'onProgressChanged':
+        _onProgressChanged.add(call.arguments['progress']);
         break;
       case 'onState':
         _onStateChanged.add(
@@ -60,7 +61,11 @@ class FlutterWebviewPlugin {
         );
         break;
       case 'onHttpError':
-        _onHttpError.add(WebViewHttpError(call.arguments['code'], call.arguments['url']));
+        _onHttpError.add(
+            WebViewHttpError(call.arguments['code'], call.arguments['url']));
+        break;
+      case 'javascriptChannelMessage':
+        _onPostMessage.add(call.arguments);
         break;
     }
   }
@@ -90,6 +95,8 @@ class FlutterWebviewPlugin {
 
   Stream<WebViewHttpError> get onHttpError => _onHttpError.stream;
 
+  Stream<Map<String, String>> get onPostMessage => _onPostMessage.stream;
+
   /// Start the Webview with [url]
   /// - [headers] specify additional HTTP headers
   /// - [withJavascript] enable Javascript or not for the Webview
@@ -115,8 +122,10 @@ class FlutterWebviewPlugin {
   /// - [displayZoomControls]: display zoom controls on webview
   /// - [withOverviewMode]: enable overview mode for Android webview ( setLoadWithOverviewMode )
   /// - [useWideViewPort]: use wide viewport for Android webview ( setUseWideViewPort )
-  Future<Null> launch(String url, {
+  Future<Null> launch(
+    String url, {
     Map<String, String> headers,
+    List<String> javascriptChannelNames,
     bool withJavascript,
     bool clearCache,
     bool clearCookies,
@@ -165,6 +174,10 @@ class FlutterWebviewPlugin {
       args['headers'] = headers;
     }
 
+    if (javascriptChannelNames != null) {
+      args['javascriptChannelNames'] = javascriptChannelNames;
+    }
+
     if (rect != null) {
       args['rect'] = {
         'left': rect.left,
@@ -211,10 +224,12 @@ class FlutterWebviewPlugin {
   }
 
   // Clean cookies on WebView
-  Future<Null> cleanCookies() async => await _channel.invokeMethod('cleanCookies');
+  Future<Null> cleanCookies() async =>
+      await _channel.invokeMethod('cleanCookies');
 
   // Stops current loading process
-  Future<Null> stopLoading() async => await _channel.invokeMethod('stopLoading');
+  Future<Null> stopLoading() async =>
+      await _channel.invokeMethod('stopLoading');
 
   /// Close all Streams
   void dispose() {
@@ -225,6 +240,7 @@ class FlutterWebviewPlugin {
     _onScrollXChanged.close();
     _onScrollYChanged.close();
     _onHttpError.close();
+    _onPostMessage.close();
     _instance = null;
   }