Pārlūkot izejas kodu

ios: remove event channel
android: add rect, fullScreen, userAgent, eval

pedia 8 gadi atpakaļ
vecāks
revīzija
a3ccf62e54

+ 158 - 11
android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

@@ -1,10 +1,21 @@
 package com.flutter_webview_plugin;
 
-import android.content.Intent;
+
 import android.app.Activity;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.view.ViewGroup;
+import android.view.View;
+import android.webkit.CookieManager;
+import android.webkit.ValueCallback;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.FrameLayout;
+
+import java.util.HashMap;
+import java.util.Map;
 
-import io.flutter.app.FlutterActivity;
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
@@ -15,13 +26,13 @@ import io.flutter.plugin.common.PluginRegistry;
  */
 public class FlutterWebviewPlugin implements MethodCallHandler {
   private Activity activity;
+  private WebView webView;
   public static MethodChannel channel;
-  private final int WEBVIEW_ACTIVITY_CODE = 1;
   private static final String CHANNEL_NAME = "flutter_webview_plugin";
 
   public static void registerWith(PluginRegistry.Registrar registrar) {
     channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
-    FlutterWebviewPlugin instance = new FlutterWebviewPlugin((Activity) registrar.activity());
+    FlutterWebviewPlugin instance = new FlutterWebviewPlugin((Activity)registrar.activity());
     channel.setMethodCallHandler(instance);
   }
 
@@ -38,27 +49,163 @@ public class FlutterWebviewPlugin implements MethodCallHandler {
       case "close":
         close(call, result);
         break;
+      case "eval":
+        eval(call, result);
+        break;
       default:
         result.notImplemented();
         break;
     }
   }
 
+  private void clearCookies() {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+      CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
+        @Override
+        public void onReceiveValue(Boolean aBoolean) {
+
+        }
+      });
+    } else {
+      CookieManager.getInstance().removeAllCookie();
+    }
+  }
+
+  private void clearCache() {
+    webView.clearCache(true);
+    webView.clearFormData();
+  }
+
+  private WebViewClient setWebViewClient() {
+    WebViewClient webViewClient = new BrowserClient();
+    webView.setWebViewClient(webViewClient);
+    return webViewClient;
+  }
+
+  private void eval(String code, final MethodChannel.Result result) {
+    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+      webView.evaluateJavascript(code, new ValueCallback<String>() {
+        @Override
+        public void onReceiveValue(String value) {
+          result.success(value);
+        }
+      });
+    } else {
+      webView.loadUrl(code);
+    }
+  }
+
+  // @Override
+  protected void onDestroy() {
+    FlutterWebviewPlugin.channel.invokeMethod("onDestroy", null);
+  }
+
+  // @Override
+  public void onBackPressed() {
+    if(webView.canGoBack()){
+      webView.goBack();
+      return;
+    }
+    FlutterWebviewPlugin.channel.invokeMethod("onBackPressed", null);
+  }
+
+  private static int dp2px(Context context, float dp) {
+    final float scale = context.getResources().getDisplayMetrics().density;
+    return (int) (dp * scale +0.5f);
+  }
+
   private void openUrl(MethodCall call, MethodChannel.Result result) {
-    Intent intent = new Intent(activity, WebviewActivity.class);
+    if (webView == null) {
+      webView = new WebView(activity);
 
-    intent.putExtra(WebviewActivity.URL_KEY, (String) call.argument("url"));
-    intent.putExtra(WebviewActivity.WITH_JAVASCRIPT_KEY, (boolean) call.argument("withJavascript"));
-    intent.putExtra(WebviewActivity.CLEAR_CACHE_KEY, (boolean) call.argument("clearCache"));
-    intent.putExtra(WebviewActivity.CLEAR_COOKIES_KEY, (boolean) call.argument("clearCookies"));
+      Map<String, Number> rc = call.argument("rect");
+      if (rc != null) {
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                dp2px(activity, rc.get("width").intValue()), dp2px(activity, rc.get("height").intValue()));
+        params.setMargins(dp2px(activity, rc.get("left").intValue()), dp2px(activity, rc.get("top").intValue()),
+                0, 0);
+        activity.addContentView(webView, params);
+      }
+      else if (!(boolean) call.argument("hidden")) {
+        activity.setContentView(webView);
+      }
+
+      setWebViewClient();
+    }
 
-    activity.startActivityForResult(intent, WEBVIEW_ACTIVITY_CODE);
+    webView.getSettings().setJavaScriptEnabled((boolean) call.argument("withJavascript"));
 
+    if ((boolean) call.argument("clearCache")) {
+      clearCache();
+    }
+
+    if ((boolean) call.argument("hidden")) {
+      webView.setVisibility(View.INVISIBLE);
+    }
+
+    if ((boolean) call.argument("clearCookies")) {
+      clearCookies();
+    }
+
+    String userAgent = call.argument("userAgent");
+    if (userAgent != null) {
+      webView.getSettings().setUserAgentString(userAgent);
+    }
+
+    String url = (String) call.argument("url");
+    webView.loadUrl(url);
     result.success(null);
   }
 
   private void close(MethodCall call, MethodChannel.Result result) {
-    activity.finishActivity(WEBVIEW_ACTIVITY_CODE);
+    ViewGroup vg = (ViewGroup)(webView.getParent());
+    vg.removeView(webView);
+    webView = null;
     result.success(null);
+
+    FlutterWebviewPlugin.channel.invokeMethod("onDestroy", null);
+  }
+
+  private void eval(MethodCall call, final MethodChannel.Result result) {
+    String code = call.argument("code");
+
+    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+      webView.evaluateJavascript(code, new ValueCallback<String>() {
+        @Override
+        public void onReceiveValue(String value) {
+          result.success(value);
+        }
+      });
+    } else {
+      // TODO:
+      webView.loadUrl(code);
+    }
+  }
+
+
+  private class BrowserClient extends WebViewClient {
+    private BrowserClient() {
+      super();
+    }
+
+    @Override
+    public void onPageStarted(WebView view, String url, Bitmap favicon) {
+      super.onPageStarted(view, url, favicon);
+      Map<String, Object> data = new HashMap<>();
+      data.put("url", url);
+      FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);
+
+      data.put("type", "startLoad");
+      FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+    }
+
+    @Override
+    public void onPageFinished(WebView view, String url) {
+      super.onPageFinished(view, url);
+      Map<String, Object> data = new HashMap<>();
+      data.put("url", url);
+      data.put("type", "finishLoad");
+      FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+    }
   }
 }

+ 6 - 3
example/lib/main.dart

@@ -59,10 +59,11 @@ class _MyHomePageState extends State<MyHomePage> {
   initState() {
     super.initState();
 
-    _onStateChanged = flutterWebviewPlugin.stateChanged.listen((dynamic state) {
+    _onStateChanged =
+        flutterWebviewPlugin.onStateChanged.listen((dynamic state) {
       if (mounted) {
         setState(() {
-          _history.add("stateChanged: $state");
+          _history.add("state: $state");
         });
       }
     });
@@ -149,7 +150,9 @@ class _MyHomePageState extends State<MyHomePage> {
           ),
           new RaisedButton(
             onPressed: () {
-              _history.clear();
+              setState(() {
+                _history.clear();
+              });
               flutterWebviewPlugin.close();
             },
             child: new Text("Close"),

+ 32 - 49
ios/Classes/FlutterWebviewPlugin.m

@@ -1,11 +1,9 @@
 #import "FlutterWebviewPlugin.h"
 
 static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
-static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
 
 // UIWebViewDelegate
-@interface FlutterWebviewPlugin() <UIWebViewDelegate, FlutterStreamHandler> {
-    FlutterEventSink _eventSink;
+@interface FlutterWebviewPlugin() <UIWebViewDelegate> {
     BOOL _enableAppScheme;
 }
 @end
@@ -20,11 +18,6 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     FlutterWebviewPlugin* instance = [[FlutterWebviewPlugin alloc] initWithViewController:viewController];
     
     [registrar addMethodCallDelegate:instance channel:channel];
-    
-    FlutterEventChannel* event =
-    [FlutterEventChannel eventChannelWithName:EVENT_CHANNEL_NAME
-                              binaryMessenger:[registrar messenger]];
-    [event setStreamHandler:instance];
 }
 
 - (instancetype)initWithViewController:(UIViewController *)viewController {
@@ -62,34 +55,36 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     NSString *userAgent = call.arguments[@"userAgent"];
     
     //
-    if ([clearCache boolValue]) {
+    if (clearCache != (id)[NSNull null] && [clearCache boolValue]) {
         [[NSURLCache sharedURLCache] removeAllCachedResponses];
     }
     
-    if ([clearCookies boolValue]) {
+    if (clearCookies != (id)[NSNull null] && [clearCookies boolValue]) {
         [[NSURLSession sharedSession] resetWithCompletionHandler:^{
         }];
     }
     
+    if (userAgent != (id)[NSNull null]) {
+        [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent": userAgent}];
+    }
+    
     CGRect rc;
-    if (rect) {
+    if (rect != nil) {
         rc = CGRectMake([[rect valueForKey:@"left"] doubleValue],
-                               [[rect valueForKey:@"top"] doubleValue],
-                               [[rect valueForKey:@"width"] doubleValue],
-                               [[rect valueForKey:@"height"] doubleValue]);
+                        [[rect valueForKey:@"top"] doubleValue],
+                        [[rect valueForKey:@"width"] doubleValue],
+                        [[rect valueForKey:@"height"] doubleValue]);
     } else {
+        // TODO: create top NavigatorController and push
         rc = self.viewController.view.bounds;
     }
     
-    if (userAgent) {
-        [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent": userAgent}];
-    }
-    
     self.webview = [[UIWebView alloc] initWithFrame:rc];
     self.webview.delegate = self;
     
-    if (!hidden || ![hidden boolValue])
-        [self.viewController.view addSubview:self.webview];
+    if (hidden != (id)[NSNull null] && [hidden boolValue])
+        self.webview.hidden = YES;
+    [self.viewController.view addSubview:self.webview];
     
     [self launch:call];
 }
@@ -113,16 +108,25 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     [self.webview removeFromSuperview];
     self.webview.delegate = nil;
     self.webview = nil;
-    [self sendEvent:@"destroy"];
+    
+    // manually trigger onDestroy
+    [channel invokeMethod:@"onDestroy" arguments:nil];
 }
 
 
 #pragma mark -- WebView Delegate
 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
-    NSArray *data = [NSArray arrayWithObjects:@"shouldStart",
-                     request.URL.absoluteString, [NSNumber numberWithInt:navigationType],
-                     nil];
-    [self sendEvent:data];
+    id data = @{@"url": request.URL.absoluteString,
+                @"type": @"shouldStart",
+                @"navigationType": [NSNumber numberWithInt:navigationType]};
+    [channel invokeMethod:@"onState" arguments:data];
+    
+    if (navigationType == UIWebViewNavigationTypeBackForward)
+        [channel invokeMethod:@"onBackPressed" arguments:nil];
+    else {
+        id data = @{@"url": request.URL.absoluteString};
+        [channel invokeMethod:@"onUrlChanged" arguments:data];
+    }
     
     if (_enableAppScheme)
         return YES;
@@ -134,40 +138,19 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
 }
 
 -(void)webViewDidStartLoad:(UIWebView *)webView {
-    [self sendEvent:@"startLoad"];
+    [channel invokeMethod:@"onState" arguments:@{@"type": @"startLoad"}];
 }
 
 - (void)webViewDidFinishLoad:(UIWebView *)webView {
-    [self sendEvent:@"finishLoad"];
+    [channel invokeMethod:@"onState" arguments:@{@"type": @"finishLoad"}];
 }
 
 - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
     id data = [FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", error.code]
                                   message:error.localizedDescription
                                   details:error.localizedFailureReason];
-    [self sendEvent:data];
+    [channel invokeMethod:@"onError" arguments:data];
 }
 
 #pragma mark -- WkWebView Delegate
-
-#pragma mark -- FlutterStreamHandler impl
-
-- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
-    _eventSink = eventSink;
-    return nil;
-}
-
-- (FlutterError*)onCancelWithArguments:(id)arguments {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    _eventSink = nil;
-    return nil;
-}
-
-- (void)sendEvent:(id)data {
-    // data should be @"" or [FlutterError]
-    if (!_eventSink)
-        return;
-    
-    _eventSink(data);
-}
 @end

+ 34 - 60
lib/flutter_webview_plugin.dart

@@ -4,57 +4,15 @@ import 'package:flutter/services.dart';
 import 'package:flutter/material.dart';
 
 const _kChannel = 'flutter_webview_plugin';
-const _kEvent = 'flutter_webview_plugin_event';
 
 // TODO: more genral state for iOS/android
-enum WebViewState { startLoad, finishLoad }
-
-// copy from UIWebView.h
-enum _WebViewNavigateType {
-  TypeLinkClicked,
-  TypeFormSubmitted,
-  TypeBackForward,
-  TypeReload,
-  TypeFormResubmitted,
-  TypeOther
-}
+enum WebViewState { shouldStart, startLoad, finishLoad }
 
 /// Singleton Class that communicate with a fullscreen Webview Instance
 /// Have to be instanciate after `runApp` called.
 class FlutterWebViewPlugin {
   final MethodChannel _channel;
 
-  /// iOS WebView: Implemented
-  /// Android WebView: not implemented
-  final EventChannel _event;
-  Stream<String> _stateChanged;
-
-  Stream<String> get stateChanged {
-    assert(_WebViewNavigateType.TypeLinkClicked.index == 0);
-    assert(_WebViewNavigateType.TypeOther.index == 5);
-    if (_stateChanged == null) {
-      _stateChanged = _event.receiveBroadcastStream();
-      _stateChanged.listen((var result) {
-        // the list like: [state, url, navtype]
-        if (result is List && result.length == 3) {
-          if (_WebViewNavigateType.TypeBackForward.index == result[2]) {
-            _onBackPressed.add(Null);
-          } else if (_WebViewNavigateType.TypeOther.index == result[2] ||
-              _WebViewNavigateType.TypeLinkClicked.index == result[2] ||
-              _WebViewNavigateType.TypeFormSubmitted.index == result[2]) {
-            // TODO: find out better way
-            _onUrlChanged.add(result[1]);
-          }
-        } else if (result is String) {
-          if (result == "destroy") {
-            _onDestroy.add(Null);
-          }
-        }
-      });
-    }
-    return _stateChanged;
-  }
-
   final StreamController<Null> _onDestroy = new StreamController.broadcast();
   final StreamController<Null> _onBackPressed =
       new StreamController.broadcast();
@@ -62,9 +20,12 @@ class FlutterWebViewPlugin {
   final StreamController<String> _onUrlChanged =
       new StreamController.broadcast();
 
-  FlutterWebViewPlugin()
-      : _channel = const MethodChannel(_kChannel),
-        _event = const EventChannel(_kEvent) {
+  final StreamController<Null> _onStateChanged =
+      new StreamController.broadcast();
+
+  final StreamController<Null> _onError = new StreamController.broadcast();
+
+  FlutterWebViewPlugin() : _channel = const MethodChannel(_kChannel) {
     _channel.setMethodCallHandler(_handleMessages);
   }
 
@@ -79,19 +40,37 @@ class FlutterWebViewPlugin {
       case "onUrlChanged":
         _onUrlChanged.add(call.arguments["url"]);
         break;
+      case "onState":
+        _onStateChanged.add(call.arguments);
+        break;
+      case "onError":
+        _onError.add(call.arguments);
+        break;
     }
   }
 
-  //////////////////////
-
   /// Listening the OnDestroy LifeCycle Event for Android
-  ///
+  /// content is Map for url
   Stream<Null> get onDestroy => _onDestroy.stream;
 
+  /// Listening url changed
+  /// iOS WebView: worked
+  /// android: worked
+  Stream<Null> get onUrlChanged => _onUrlChanged.stream;
+
   /// Listening the onBackPressed Event for Android
-  ///
+  /// content null
+  /// iOS WebView: worked
+  /// android: worked
   Stream<Null> get onBackPressed => _onBackPressed.stream;
 
+  /// Listening the onState Event for iOS WebView and Android
+  /// content is Map for type: {shouldStart|startLoad|finishLoad}
+  /// more detail than other events
+  /// iOS WebView: worked
+  /// android: Not for now.
+  Stream<Null> get onStateChanged => _onStateChanged.stream;
+
   /// Start the Webview with [url]
   /// - [withJavascript] enable Javascript or not for the Webview
   ///     iOS WebView: Not implemented yet
@@ -106,19 +85,19 @@ class FlutterWebViewPlugin {
   ///     android: Implemented
   /// - [hidden] not show
   ///     iOS WebView: not shown(addSubView) in ViewController
-  ///     android: Not implemented yet.
+  ///     android: Implemented
   ///   [fullScreen]: show in full screen mode, default true
   ///     iOS WebView: without rect, show in full screen mode
   ///     android: Implemented
   ///   [rect]: show in rect(not full screen)
   ///     iOS WebView: worked
-  ///     android: Not implemented yet
+  ///     android: Implemented
   ///   [enableAppScheme]: false will enable all schemes, true only for httt/https/about
   ///     iOS WebView: worked
   ///     android: Not implemented yet
   ///   [userAgent]: set the User-Agent of WebView
   ///     iOS WebView: worked
-  ///     android: Not implemented yet
+  ///     android: Implemented
   Future<Null> launch(String url,
       {bool withJavascript: true,
       bool clearCache: false,
@@ -142,7 +121,7 @@ class FlutterWebViewPlugin {
     if (rect != null) {
       args["rect"] = {
         "left": rect.left,
-        "right": rect.right,
+        "top": rect.top,
         "width": rect.width,
         "height": rect.height
       };
@@ -151,7 +130,7 @@ class FlutterWebViewPlugin {
   }
 
   /// iOS WebView: worked
-  /// android: Not implemented yet
+  /// android: implemented
   Future<String> evalJavascript(String code) {
     return _channel.invokeMethod('eval', {"code": code});
   }
@@ -159,9 +138,4 @@ class FlutterWebViewPlugin {
   /// Close the Webview
   /// Will trigger the [onDestroy] event
   Future<Null> close() => _channel.invokeMethod("close");
-
-  /// Listening url changed
-  /// iOS WebView: worked
-  /// android: worked
-  Stream<String> get onUrlChanged => _onUrlChanged.stream;
 }