Commit b4e3e73b authored by pichillilorenzo's avatar pichillilorenzo

updated onLoadResource method, InAppBrowser.open url parameter has the default value: 'about:blank'

parent d696ed1e
This diff is collapsed.
package com.pichillilorenzo.flutter_inappbrowser;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.HttpAuthHandler;
import android.webkit.MimeTypeMap;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
......@@ -278,28 +269,55 @@ public class InAppBrowserWebViewClient extends WebViewClient {
Request mRequest = new Request.Builder().url(url).build();
try {
long loadingTime = System.currentTimeMillis();
Response response = activity.httpClient.newCall(mRequest).execute();
loadingTime = System.currentTimeMillis() - loadingTime;
String reasonPhrase = response.message();
if (reasonPhrase.equals("")) {
reasonPhrase = statusCodeMapping.get(response.code());
Log.d(LOG_TAG, reasonPhrase);
}
reasonPhrase = (reasonPhrase.equals("") || reasonPhrase == null) ? "OK" : reasonPhrase;
Map<String, String> headers = new HashMap<String, String>();
Map<String, String> headersResponse = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
String value = "";
for (String val: entry.getValue()) {
value += (value == "") ? val : "; " + val;
}
headers.put(entry.getKey().toLowerCase(), value);
headersResponse.put(entry.getKey().toLowerCase(), value);
}
Map<String, String> headersRequest = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : mRequest.headers().toMultimap().entrySet()) {
String value = "";
for (String val: entry.getValue()) {
value += (value == "") ? val : "; " + val;
}
headersRequest.put(entry.getKey().toLowerCase(), value);
}
Map<String, Object> obj = new HashMap<>();
Map<String, Object> res = new HashMap<>();
Map<String, Object> req = new HashMap<>();
obj.put("uuid", activity.uuid);
obj.put("url", url);
obj.put("statusCode", response.code());
obj.put("headers", headers);
byte[] dataBytes = response.body().bytes();
InputStream dataStream = new ByteArrayInputStream(dataBytes);
res.put("url", url);
res.put("statusCode", response.code());
res.put("headers", headersResponse);
res.put("loadingTime", loadingTime);
res.put("data", dataBytes);
req.put("url", url);
req.put("headers", headersRequest);
req.put("method", mRequest.method());
obj.put("response", res);
obj.put("request", req);
InAppBrowserFlutterPlugin.channel.invokeMethod("onLoadResource", obj);
......@@ -308,8 +326,8 @@ public class InAppBrowserWebViewClient extends WebViewClient {
response.header("content-encoding"),
response.code(),
reasonPhrase,
headers,
response.body().byteStream()
headersResponse,
dataStream
);
} catch (IOException e) {
e.printStackTrace();
......
......@@ -72,10 +72,10 @@ class MyInAppBrowser extends InAppBrowser {
}
@override
void onLoadResource(String url, int statusCode, Map<String, String> headers) {
print("\n\n resource: $url\n\n");
print(statusCode);
print(headers);
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
print(response.loadingTime.toString() + "ms " + response.url);
if (response.headers["content-length"] != null)
print(response.headers["content-length"] + " length");
}
@override
......@@ -138,7 +138,7 @@ class _MyAppState extends State<MyApp> {
body: new Center(
child: new RaisedButton(onPressed: () {
//chromeSafariBrowser.open("https://flutter.io/");
inAppBrowserFallback.open("https://flutter.io/", options: {
inAppBrowserFallback.open(url: "https://flutter.io/", options: {
//"hidden": true,
//"toolbarTopFixedTitle": "Fixed title",
//"useShouldOverrideUrlLoading": true
......
......@@ -132,12 +132,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
override func viewDidLoad() {
super.viewDidLoad()
URLProtocol.wk_registerScheme("http")
URLProtocol.wk_registerScheme("https")
MyURLProtocol.URLProtocolDelegate = self
URLProtocol.registerClass(MyURLProtocol.self)
MyURLProtocol.wkWebViewDelegateMap[uuid] = self
webView.uiDelegate = self
webView.navigationDelegate = self
......@@ -303,8 +298,21 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
if ((browserOptions?.userAgent)! != "") {
if #available(iOS 9.0, *) {
self.webView.customUserAgent = (browserOptions?.userAgent)!
} else {
// Fallback on earlier versions
}
}
// set uuid in the User-Agent in order to know which webview is making internal requests and
// to send the onLoadResource event to the correct webview
if #available(iOS 9.0, *) {
if (self.webView.customUserAgent != nil) {
self.webView.customUserAgent = self.webView.customUserAgent! + " WKWebView/" + self.uuid
}
else {
self.webView.evaluateJavaScript("navigator.userAgent") { [weak webView] (result, error) in
if let webView = self.webView, let userAgent = result as? String {
webView.customUserAgent = userAgent + " WKWebView/" + self.uuid
}
}
}
}
......@@ -491,7 +499,6 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
//dump((navigationResponse.response as! HTTPURLResponse))
//print(navigationResponse.response.mimeType)
//print(navigationResponse.response.url)
......@@ -569,8 +576,8 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio
navigationDelegate?.onLoadError(uuid: self.uuid, webView: webView, error: error)
}
func didReceiveResponse(_ response: URLResponse, from request: URLRequest?) {
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response)
func didReceiveResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int) {
navigationDelegate?.onLoadResource(uuid: self.uuid, webView: webView, response: response, fromRequest: request, withData: data, loadingTime: time)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
......
......@@ -6,29 +6,47 @@
//
import Foundation
import WebKit
func currentTimeInMilliSeconds() -> Int {
let currentDate = Date()
let since1970 = currentDate.timeIntervalSince1970
return Int(since1970 * 1000)
}
class MyURLProtocol: URLProtocol {
struct Constants {
static let RequestHandledKey = "URLProtocolRequestHandled"
}
// struct Constants {
// static let RequestHandledKey = "URLProtocolRequestHandled"
// }
var wkWebViewUuid: String?
var session: URLSession?
var sessionTask: URLSessionDataTask?
static var URLProtocolDelegate: MyURLProtocolDelegate?
var response: URLResponse?
var data: Data?
static var wkWebViewDelegateMap: [String: MyURLProtocolDelegate] = [:]
var loadingTime: Int = 0
override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
super.init(request: request, cachedResponse: cachedResponse, client: client)
if session == nil {
self.wkWebViewUuid = MyURLProtocol.getUuid(request)
if session == nil && self.wkWebViewUuid != nil {
session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
}
}
override class func canInit(with request: URLRequest) -> Bool {
if MyURLProtocol.property(forKey: Constants.RequestHandledKey, in: request) != nil {
if getUuid(request) == nil {
return false
}
// if MyURLProtocol.property(forKey: Constants.RequestHandledKey, in: request) != nil {
// return false
// }
return true
}
......@@ -38,25 +56,54 @@ class MyURLProtocol: URLProtocol {
override func startLoading() {
let newRequest = ((request as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!
MyURLProtocol.setProperty(true, forKey: Constants.RequestHandledKey, in: newRequest)
loadingTime = currentTimeInMilliSeconds()
//MyURLProtocol.setProperty(true, forKey: Constants.RequestHandledKey, in: newRequest)
sessionTask = session?.dataTask(with: newRequest as URLRequest)
sessionTask?.resume()
}
override func stopLoading() {
if let uuid = self.wkWebViewUuid {
if MyURLProtocol.wkWebViewDelegateMap[uuid] != nil && self.response != nil {
loadingTime = currentTimeInMilliSeconds() - loadingTime
if self.data == nil {
self.data = Data()
}
MyURLProtocol.wkWebViewDelegateMap[uuid]!.didReceiveResponse(self.response!, fromRequest: request, withData: self.data!, loadingTime: loadingTime)
}
}
sessionTask?.cancel()
}
class func getUuid(_ request: URLRequest?) -> String? {
let userAgent: String? = request?.allHTTPHeaderFields?["User-Agent"]
var uuid: String? = nil
if userAgent != nil {
if userAgent!.contains("WKWebView/") {
let userAgentSplitted = userAgent!.split(separator: " ")
uuid = String(userAgentSplitted[userAgentSplitted.count-1]).replacingOccurrences(of: "WKWebView/", with: "")
}
}
return uuid
}
}
extension MyURLProtocol: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if self.data == nil {
self.data = data
}
else {
self.data!.append(data)
}
client?.urlProtocol(self, didLoad: data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
let policy = URLCache.StoragePolicy(rawValue: request.cachePolicy.rawValue) ?? .notAllowed
self.response = response
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: policy)
MyURLProtocol.URLProtocolDelegate?.didReceiveResponse(response, from: dataTask.currentRequest)
completionHandler(.allow)
}
......@@ -98,5 +145,5 @@ extension MyURLProtocol: URLSessionDataDelegate {
}
protocol MyURLProtocolDelegate {
func didReceiveResponse(_ response: URLResponse, from request: URLRequest?)
func didReceiveResponse(_ response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int)
}
......@@ -46,6 +46,9 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
public static func register(with registrar: FlutterPluginRegistrar) {
URLProtocol.wk_registerScheme("http")
URLProtocol.wk_registerScheme("https")
URLProtocol.registerClass(MyURLProtocol.self)
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
let instance = SwiftFlutterPlugin(with: registrar)
registrar.addMethodCallDelegate(instance, channel: channel)
......@@ -489,16 +492,28 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
}
func onLoadResource(uuid: String, webView: WKWebView, response: URLResponse) {
func onLoadResource(uuid: String, webView: WKWebView, response: URLResponse, fromRequest request: URLRequest?, withData data: Data, loadingTime time: Int) {
if self.webViewControllers[uuid] != nil {
var headers = (response as! HTTPURLResponse).allHeaderFields as! [String: String]
headers.lowercaseKeys()
var headersResponse = (response as! HTTPURLResponse).allHeaderFields as! [String: String]
headersResponse.lowercaseKeys()
var headersRequest = request!.allHTTPHeaderFields! as [String: String]
headersRequest.lowercaseKeys()
let arguments: [String : Any] = [
"uuid": uuid,
"url": response.url?.absoluteString ?? "",
"response": [
"url": response.url!.absoluteString,
"statusCode": (response as! HTTPURLResponse).statusCode,
"headers": headers
"headers": headersResponse,
"loadingTime": time,
"data": data
],
"request": [
"url": request!.url!.absoluteString,
"headers": headersRequest,
"method": request!.httpMethod!
]
]
channel.invokeMethod("onLoadResource", arguments: arguments)
}
......
......@@ -21,6 +21,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:uuid/uuid.dart';
......@@ -34,6 +35,28 @@ enum ConsoleMessageLevel {
DEBUG, ERROR, LOG, TIP, WARNING
}
class WebResourceRequest {
String url;
Map<String, String> headers;
String method;
WebResourceRequest(this.url, this.headers, this.method);
}
class WebResourceResponse {
String url;
Map<String, String> headers;
int statusCode;
int loadingTime;
Uint8List data;
WebResourceResponse(this.url, this.headers, this.statusCode, this.loadingTime, this.data);
}
///Public class representing a JavaScript console message from WebCore.
///This could be a issued by a call to one of the console logging functions (e.g. console.log('...')) or a JavaScript error on the page.
///
......@@ -107,10 +130,27 @@ class InAppBrowser {
shouldOverrideUrlLoading(url);
break;
case "onLoadResource":
String url = call.arguments["url"];
int statusCode = call.arguments["statusCode"];
Map<dynamic, dynamic> headers = call.arguments["headers"];
onLoadResource(url, statusCode, headers.cast<String, String>());
Map<dynamic, dynamic> rawResponse = call.arguments["response"];
rawResponse = rawResponse.cast<String, dynamic>();
Map<dynamic, dynamic> rawRequest = call.arguments["request"];
rawRequest = rawRequest.cast<String, dynamic>();
String urlResponse = rawResponse["url"];
Map<dynamic, dynamic> headersResponse = rawResponse["headers"];
headersResponse = headersResponse.cast<String, String>();
int statusCode = rawResponse["statusCode"];
int loadingTime = rawResponse["loadingTime"];
Uint8List data = rawResponse["data"];
String urlRequest = rawRequest["url"];
Map<dynamic, dynamic> headersRequest = rawRequest["headers"];
headersRequest = headersResponse.cast<String, String>();
String method = rawRequest["method"];
var response = new WebResourceResponse(urlResponse, headersResponse, statusCode, loadingTime, data);
var request = new WebResourceRequest(urlRequest, headersRequest, method);
onLoadResource(response, request);
break;
case "onConsoleMessage":
String sourceURL = call.arguments["sourceURL"];
......@@ -187,7 +227,7 @@ class InAppBrowser {
/// - __allowsInlineMediaPlayback__: Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. For this to work, add the `webkit-playsinline` attribute to any `<video>` elements. The default value is `false`.
/// - __allowsPictureInPictureMediaPlayback__: Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
/// - __spinner__: Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
Future<void> open(String url, {Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}}) async {
Future<void> open({String url = "about:blank", Map<String, String> headers = const {}, String target = "_self", Map<String, dynamic> options = const {}}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url);
......@@ -328,8 +368,8 @@ class InAppBrowser {
}
///Event fires when the [InAppBrowser] webview will load the resource specified by the given [url].
void onLoadResource(String url, int statusCode, Map<String, String> headers) {
///Event fires when the [InAppBrowser] webview will load the resource specified by the given [WebResourceRequest].
void onLoadResource(WebResourceResponse response, WebResourceRequest request) {
}
......
name: flutter_inappbrowser
description: A Flutter plugin that allows you to open an in-app browser window. (inspired by the popular cordova-plugin-inappbrowser).
version: 0.2.1
version: 0.3.0
author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappbrowser
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment