Skip to content

Commit

Permalink
Merge pull request #3 from pedia/schema
Browse files Browse the repository at this point in the history
ios and android more implement
  • Loading branch information
pedia authored Dec 16, 2017
2 parents 571e3aa + 55bb7c3 commit 72badf1
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,13 +26,13 @@
*/
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);
}

Expand All @@ -38,27 +49,163 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
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);
}
}
}
9 changes: 6 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
}
});
Expand Down Expand Up @@ -149,7 +150,9 @@ class _MyHomePageState extends State<MyHomePage> {
),
new RaisedButton(
onPressed: () {
_history.clear();
setState(() {
_history.clear();
});
flutterWebviewPlugin.close();
},
child: new Text("Close"),
Expand Down
81 changes: 32 additions & 49 deletions ios/Classes/FlutterWebviewPlugin.m
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,11 +18,6 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
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 {
Expand Down Expand Up @@ -62,34 +55,36 @@ - (void)initWebView:(FlutterMethodCall*)call {
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];
}
Expand All @@ -113,16 +108,25 @@ - (void)closeWebView {
[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;
Expand All @@ -134,40 +138,19 @@ - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)
}

-(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
Loading

0 comments on commit 72badf1

Please sign in to comment.