Commit ade4480c authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

Fixed error 'java.lang.ClassCastException: cannot be cast to...

Fixed error 'java.lang.ClassCastException:  cannot be cast to android.view.WindowManagerImpl' on Android when using native alert dialogs, updated README.md with Xcode version required
parent a56b2128
This diff is collapsed.
## 2.0.1+1
- Fixed error "java.lang.ClassCastException: $Proxy1 cannot be cast to android.view.WindowManagerImpl" on Android when using native alert dialogs
## 2.0.1 ## 2.0.1
- Added `onPermissionRequest` event. This event is fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android). - Added `onPermissionRequest` event. This event is fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).
......
...@@ -14,7 +14,7 @@ A Flutter plugin that allows you to add an inline webview or open an in-app brow ...@@ -14,7 +14,7 @@ A Flutter plugin that allows you to add an inline webview or open an in-app brow
- Dart sdk: ">=2.0.0-dev.68.0 <3.0.0" - Dart sdk: ">=2.0.0-dev.68.0 <3.0.0"
- Flutter: ">=1.9.1+hotfix.5 <2.0.0" - Flutter: ">=1.9.1+hotfix.5 <2.0.0"
- Android: `minSdkVersion 17` - Android: `minSdkVersion 17`
- iOS: `--ios-language swift` - iOS: `--ios-language swift`, Xcode version `>= 11`
### Note for Android ### Note for Android
...@@ -440,7 +440,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: ...@@ -440,7 +440,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `onSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android). * `onSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
* `onReceivedHttpAuthRequest`: Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. * `onReceivedHttpAuthRequest`: Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
* `onReceivedServerTrustAuthRequest`: Event fired when the WebView need to perform server trust authentication (certificate validation). * `onReceivedServerTrustAuthRequest`: Event fired when the WebView need to perform server trust authentication (certificate validation).
* `onReceivedClientCertRequest`: Notify the host application to handle a SSL client certificate request. * `onReceivedClientCertRequest`: Notify the host application to handle an SSL client certificate request.
* `onFindResultReceived`: Event fired as find-on-page operations progress. * `onFindResultReceived`: Event fired as find-on-page operations progress.
* `shouldInterceptAjaxRequest`: Event fired when an `XMLHttpRequest` is sent to a server. * `shouldInterceptAjaxRequest`: Event fired when an `XMLHttpRequest` is sent to a server.
* `onAjaxReadyStateChange`: Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. * `onAjaxReadyStateChange`: Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
......
...@@ -164,7 +164,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR ...@@ -164,7 +164,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, clickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, clickListener);
...@@ -255,7 +255,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR ...@@ -255,7 +255,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener);
...@@ -372,7 +372,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR ...@@ -372,7 +372,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener);
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
# This is a generated file; do not edit or check into version control. # This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter" export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example" export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/test_driver/app.dart" export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios" export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios" export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"
...@@ -268,6 +268,8 @@ ...@@ -268,6 +268,8 @@
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
"${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
...@@ -275,6 +277,8 @@ ...@@ -275,6 +277,8 @@
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
......
...@@ -22,6 +22,7 @@ dependencies: ...@@ -22,6 +22,7 @@ dependencies:
flutter_downloader: ^1.3.2 flutter_downloader: ^1.3.2
path_provider: ^1.4.0 path_provider: ^1.4.0
permission_handler: ^3.3.0 permission_handler: ^3.3.0
connectivity: ^0.4.5+6
flutter_inappwebview: flutter_inappwebview:
path: ../ path: ../
......
...@@ -27,7 +27,7 @@ class InAppWebViewOnLoadErrorTestState extends WidgetTestState { ...@@ -27,7 +27,7 @@ class InAppWebViewOnLoadErrorTestState extends WidgetTestState {
Expanded( Expanded(
child: Container( child: Container(
child: InAppWebView( child: InAppWebView(
initialUrl: "http://not-existing-domain.org/", initialUrl: "https://not-existing-domain.org/",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewWidgetOptions(
inAppWebViewOptions: InAppWebViewOptions( inAppWebViewOptions: InAppWebViewOptions(
......
...@@ -6,7 +6,8 @@ import 'package:flutter/services.dart'; ...@@ -6,7 +6,8 @@ import 'package:flutter/services.dart';
import 'types.dart' show ListenerCallback; import 'types.dart' show ListenerCallback;
class ChannelManager { class ChannelManager {
static const MethodChannel channel = const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); static const MethodChannel channel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
static bool initialized = false; static bool initialized = false;
static final listeners = HashMap<String, ListenerCallback>(); static final listeners = HashMap<String, ListenerCallback>();
...@@ -16,13 +17,12 @@ class ChannelManager { ...@@ -16,13 +17,12 @@ class ChannelManager {
} }
static void addListener(String key, ListenerCallback callback) { static void addListener(String key, ListenerCallback callback) {
if (!initialized) if (!initialized) init();
init();
listeners.putIfAbsent(key, () => callback); listeners.putIfAbsent(key, () => callback);
} }
static void init () { static void init() {
channel.setMethodCallHandler(_handleMethod); channel.setMethodCallHandler(_handleMethod);
initialized = true; initialized = true;
} }
} }
\ No newline at end of file
...@@ -20,7 +20,7 @@ class ChromeSafariBrowser { ...@@ -20,7 +20,7 @@ class ChromeSafariBrowser {
bool _isOpened = false; bool _isOpened = false;
///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`. ///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`.
ChromeSafariBrowser ({bFallback}) { ChromeSafariBrowser({bFallback}) {
uuid = uuidGenerator.v4(); uuid = uuidGenerator.v4();
browserFallback = bFallback; browserFallback = bFallback;
ChannelManager.addListener(uuid, handleMethod); ChannelManager.addListener(uuid, handleMethod);
...@@ -28,7 +28,7 @@ class ChromeSafariBrowser { ...@@ -28,7 +28,7 @@ class ChromeSafariBrowser {
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
switch(call.method) { switch (call.method) {
case "onChromeSafariBrowserOpened": case "onChromeSafariBrowserOpened":
onOpened(); onOpened();
break; break;
...@@ -53,7 +53,11 @@ class ChromeSafariBrowser { ...@@ -53,7 +53,11 @@ class ChromeSafariBrowser {
///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value. ///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value.
/// ///
///[optionsFallback]: Options used by the [InAppBrowser] instance fallback. ///[optionsFallback]: Options used by the [InAppBrowser] instance fallback.
Future<void> open({@required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback}) async { Future<void> open(
{@required String url,
ChromeSafariBrowserClassOptions options,
Map<String, String> headersFallback = const {},
InAppBrowserClassOptions optionsFallback}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); this.throwIsAlreadyOpened(message: 'Cannot open $url!');
...@@ -65,21 +69,33 @@ class ChromeSafariBrowser { ...@@ -65,21 +69,33 @@ class ChromeSafariBrowser {
Map<String, dynamic> optionsFallbackMap = {}; Map<String, dynamic> optionsFallbackMap = {};
if (optionsFallback != null) { if (optionsFallback != null) {
optionsFallbackMap.addAll(optionsFallback.inAppBrowserOptions?.toMap() ?? {}); optionsFallbackMap
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); .addAll(optionsFallback.inAppBrowserOptions?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.inAppWebViewOptions
?.toMap() ??
{});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsFallbackMap.addAll(optionsFallback.androidInAppBrowserOptions?.toMap() ?? {}); optionsFallbackMap
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); .addAll(optionsFallback.androidInAppBrowserOptions?.toMap() ?? {});
} optionsFallbackMap.addAll(optionsFallback
else if (Platform.isIOS) { .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
optionsFallbackMap.addAll(optionsFallback.iosInAppBrowserOptions?.toMap() ?? {}); ?.toMap() ??
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); {});
} else if (Platform.isIOS) {
optionsFallbackMap
.addAll(optionsFallback.iosInAppBrowserOptions?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.iosInAppWebViewOptions
?.toMap() ??
{});
} }
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('uuidFallback', () => (browserFallback != null) ? browserFallback.uuid : ''); args.putIfAbsent('uuidFallback',
() => (browserFallback != null) ? browserFallback.uuid : '');
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headersFallback); args.putIfAbsent('headers', () => headersFallback);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => optionsMap);
...@@ -91,19 +107,13 @@ class ChromeSafariBrowser { ...@@ -91,19 +107,13 @@ class ChromeSafariBrowser {
} }
///Event fires when the [ChromeSafariBrowser] is opened. ///Event fires when the [ChromeSafariBrowser] is opened.
void onOpened() { void onOpened() {}
}
///Event fires when the [ChromeSafariBrowser] is loaded. ///Event fires when the [ChromeSafariBrowser] is loaded.
void onLoaded() { void onLoaded() {}
}
///Event fires when the [ChromeSafariBrowser] is closed. ///Event fires when the [ChromeSafariBrowser] is closed.
void onClosed() { void onClosed() {}
}
///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`. ///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`.
bool isOpened() { bool isOpened() {
...@@ -112,13 +122,17 @@ class ChromeSafariBrowser { ...@@ -112,13 +122,17 @@ class ChromeSafariBrowser {
void throwIsAlreadyOpened({String message = ''}) { void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) { if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is already opened.'
]);
} }
} }
void throwIsNotOpened({String message = ''}) { void throwIsNotOpened({String message = ''}) {
if (!this.isOpened()) { if (!this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is not opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is not opened.'
]);
} }
} }
} }
\ No newline at end of file
...@@ -11,27 +11,22 @@ import 'types.dart'; ...@@ -11,27 +11,22 @@ import 'types.dart';
class ContentBlocker { class ContentBlocker {
///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action. ///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action.
ContentBlockerTrigger trigger; ContentBlockerTrigger trigger;
///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched. ///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
ContentBlockerAction action; ContentBlockerAction action;
ContentBlocker({@required this.trigger,@required this.action}); ContentBlocker({@required this.trigger, @required this.action});
Map<String, Map<String, dynamic>> toMap() { Map<String, Map<String, dynamic>> toMap() {
return { return {"trigger": trigger.toMap(), "action": action.toMap()};
"trigger": trigger.toMap(),
"action": action.toMap()
};
} }
static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) { static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) {
return ContentBlocker( return ContentBlocker(
trigger: ContentBlockerTrigger.fromMap( trigger: ContentBlockerTrigger.fromMap(
Map<String, dynamic>.from(map["trigger"]) Map<String, dynamic>.from(map["trigger"])),
),
action: ContentBlockerAction.fromMap( action: ContentBlockerAction.fromMap(
Map<String, dynamic>.from(map["action"]) Map<String, dynamic>.from(map["action"])));
)
);
} }
} }
...@@ -42,29 +37,42 @@ class ContentBlocker { ...@@ -42,29 +37,42 @@ class ContentBlocker {
class ContentBlockerTrigger { class ContentBlockerTrigger {
///A regular expression pattern to match the URL against. ///A regular expression pattern to match the URL against.
String urlFilter; String urlFilter;
///Used only by iOS. A Boolean value. The default value is false. ///Used only by iOS. A Boolean value. The default value is false.
bool urlFilterIsCaseSensitive; bool urlFilterIsCaseSensitive;
///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match. ///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match.
///If not specified, the rule matches all resource types. ///If not specified, the rule matches all resource types.
List<ContentBlockerTriggerResourceType> resourceType; List<ContentBlockerTriggerResourceType> resourceType;
///A list of strings matched to a URL's domain; limits action to a list of specific domains. ///A list of strings matched to a URL's domain; limits action to a list of specific domains.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain].
List<String> ifDomain; List<String> ifDomain;
///A list of strings matched to a URL's domain; acts on any site except domains in a provided list. ///A list of strings matched to a URL's domain; acts on any site except domains in a provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain].
List<String> unlessDomain; List<String> unlessDomain;
///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types. ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types.
List<ContentBlockerTriggerLoadType> loadType; List<ContentBlockerTriggerLoadType> loadType;
///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns. ///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl].
List<String> ifTopUrl; List<String> ifTopUrl;
///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list. ///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl].
List<String> unlessTopUrl; List<String> unlessTopUrl;
ContentBlockerTrigger({@required String urlFilter, bool urlFilterIsCaseSensitive = false, List<ContentBlockerTriggerResourceType> resourceType = const [], ContentBlockerTrigger(
List<String> ifDomain = const [], List<String> unlessDomain = const [], List<ContentBlockerTriggerLoadType> loadType = const [], {@required String urlFilter,
List<String> ifTopUrl = const [], List<String> unlessTopUrl = const []}) { bool urlFilterIsCaseSensitive = false,
List<ContentBlockerTriggerResourceType> resourceType = const [],
List<String> ifDomain = const [],
List<String> unlessDomain = const [],
List<ContentBlockerTriggerLoadType> loadType = const [],
List<String> ifTopUrl = const [],
List<String> unlessTopUrl = const []}) {
this.urlFilter = urlFilter; this.urlFilter = urlFilter;
assert(this.urlFilter != null); assert(this.urlFilter != null);
this.resourceType = resourceType; this.resourceType = resourceType;
...@@ -101,7 +109,9 @@ class ContentBlockerTrigger { ...@@ -101,7 +109,9 @@ class ContentBlockerTrigger {
}; };
map.keys map.keys
.where((key) => map[key] == null || (map[key] is List && (map[key] as List).length == 0)) // filter keys .where((key) =>
map[key] == null ||
(map[key] is List && (map[key] as List).length == 0)) // filter keys
.toList() // create a copy to avoid concurrent modifications .toList() // create a copy to avoid concurrent modifications
.forEach(map.remove); .forEach(map.remove);
...@@ -112,7 +122,8 @@ class ContentBlockerTrigger { ...@@ -112,7 +122,8 @@ class ContentBlockerTrigger {
List<ContentBlockerTriggerResourceType> resourceType = []; List<ContentBlockerTriggerResourceType> resourceType = [];
List<ContentBlockerTriggerLoadType> loadType = []; List<ContentBlockerTriggerLoadType> loadType = [];
List<String> resourceTypeStringList = List<String>.from(map["resource-type"] ?? []); List<String> resourceTypeStringList =
List<String>.from(map["resource-type"] ?? []);
resourceTypeStringList.forEach((type) { resourceTypeStringList.forEach((type) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type)); resourceType.add(ContentBlockerTriggerResourceType.fromValue(type));
}); });
...@@ -130,8 +141,7 @@ class ContentBlockerTrigger { ...@@ -130,8 +141,7 @@ class ContentBlockerTrigger {
resourceType: resourceType, resourceType: resourceType,
loadType: loadType, loadType: loadType,
ifTopUrl: List<String>.from(map["if-top-url"] ?? []), ifTopUrl: List<String>.from(map["if-top-url"] ?? []),
unlessTopUrl: List<String>.from(map["unless-top-url"] ?? []) unlessTopUrl: List<String>.from(map["unless-top-url"] ?? []));
);
} }
} }
...@@ -143,11 +153,13 @@ class ContentBlockerTrigger { ...@@ -143,11 +153,13 @@ class ContentBlockerTrigger {
class ContentBlockerAction { class ContentBlockerAction {
///Type of the action. ///Type of the action.
ContentBlockerActionType type; ContentBlockerActionType type;
///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored. ///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored.
///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas. ///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas.
String selector; String selector;
ContentBlockerAction({@required ContentBlockerActionType type, String selector}) { ContentBlockerAction(
{@required ContentBlockerActionType type, String selector}) {
this.type = type; this.type = type;
assert(this.type != null); assert(this.type != null);
if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) { if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) {
...@@ -157,13 +169,12 @@ class ContentBlockerAction { ...@@ -157,13 +169,12 @@ class ContentBlockerAction {
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
Map<String, dynamic> map = { Map<String, dynamic> map = {"type": type.toValue(), "selector": selector};
"type": type.toValue(),
"selector": selector
};
map.keys map.keys
.where((key) => map[key] == null || (map[key] is List && (map[key] as List).length == 0)) // filter keys .where((key) =>
map[key] == null ||
(map[key] is List && (map[key] as List).length == 0)) // filter keys
.toList() // create a copy to avoid concurrent modifications .toList() // create a copy to avoid concurrent modifications
.forEach(map.remove); .forEach(map.remove);
...@@ -173,7 +184,6 @@ class ContentBlockerAction { ...@@ -173,7 +184,6 @@ class ContentBlockerAction {
static ContentBlockerAction fromMap(Map<String, dynamic> map) { static ContentBlockerAction fromMap(Map<String, dynamic> map) {
return ContentBlockerAction( return ContentBlockerAction(
type: ContentBlockerActionType.fromValue(map["type"]), type: ContentBlockerActionType.fromValue(map["type"]),
selector: map["selector"] selector: map["selector"]);
);
} }
} }
\ No newline at end of file
...@@ -10,7 +10,8 @@ import 'types.dart'; ...@@ -10,7 +10,8 @@ import 'types.dart';
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager { class CookieManager {
static CookieManager _instance; static CookieManager _instance;
static const MethodChannel _channel = const MethodChannel('com.pichillilorenzo/flutter_inappwebview_cookiemanager'); static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_cookiemanager');
///Gets the cookie manager shared instance. ///Gets the cookie manager shared instance.
static CookieManager instance() { static CookieManager instance() {
...@@ -23,21 +24,22 @@ class CookieManager { ...@@ -23,21 +24,22 @@ class CookieManager {
return _instance; return _instance;
} }
static Future<dynamic> _handleMethod(MethodCall call) async { static Future<dynamic> _handleMethod(MethodCall call) async {}
}
///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie. The cookie being set will be ignored if it is expired. ///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie. The cookie being set will be ignored if it is expired.
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url]. ///If [domain] is `null`, its default value will be the domain name of [url].
Future<void> setCookie({@required String url, @required String name, @required String value, Future<void> setCookie(
String domain, {@required String url,
String path = "/", @required String name,
int expiresDate, @required String value,
int maxAge, String domain,
bool isSecure }) async { String path = "/",
if (domain == null) int expiresDate,
domain = _getDomainName(url); int maxAge,
bool isSecure}) async {
if (domain == null) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
...@@ -64,17 +66,20 @@ class CookieManager { ...@@ -64,17 +66,20 @@ class CookieManager {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap = await _channel.invokeMethod('getCookies', args); List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>(); cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
List<Cookie> cookies = []; List<Cookie> cookies = [];
for(var i = 0; i < cookieListMap.length; i++) { for (var i = 0; i < cookieListMap.length; i++) {
cookies.add(Cookie(name: cookieListMap[i]["name"], value: cookieListMap[i]["value"])); cookies.add(Cookie(
name: cookieListMap[i]["name"], value: cookieListMap[i]["value"]));
} }
return cookies; return cookies;
} }
///Gets a cookie by its [name] for the given [url]. ///Gets a cookie by its [name] for the given [url].
Future<Cookie> getCookie({@required String url, @required String name}) async { Future<Cookie> getCookie(
{@required String url, @required String name}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
...@@ -82,7 +87,7 @@ class CookieManager { ...@@ -82,7 +87,7 @@ class CookieManager {
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookies = await _channel.invokeMethod('getCookies', args); List<dynamic> cookies = await _channel.invokeMethod('getCookies', args);
cookies = cookies.cast<Map<dynamic, dynamic>>(); cookies = cookies.cast<Map<dynamic, dynamic>>();
for(var i = 0; i < cookies.length; i++) { for (var i = 0; i < cookies.length; i++) {
cookies[i] = cookies[i].cast<String, dynamic>(); cookies[i] = cookies[i].cast<String, dynamic>();
if (cookies[i]["name"] == name) if (cookies[i]["name"] == name)
return Cookie(name: cookies[i]["name"], value: cookies[i]["value"]); return Cookie(name: cookies[i]["name"], value: cookies[i]["value"]);
...@@ -94,9 +99,12 @@ class CookieManager { ...@@ -94,9 +99,12 @@ class CookieManager {
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookie({@required String url, @required String name, String domain = "", String path = "/"}) async { Future<void> deleteCookie(
if (domain == null || domain.isEmpty) {@required String url,
domain = _getDomainName(url); @required String name,
String domain = "",
String path = "/"}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
...@@ -115,9 +123,9 @@ class CookieManager { ...@@ -115,9 +123,9 @@ class CookieManager {
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookies({@required String url, String domain = "", String path = "/"}) async { Future<void> deleteCookies(
if (domain == null || domain.isEmpty) {@required String url, String domain = "", String path = "/"}) async {
domain = _getDomainName(url); if (domain == null || domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(domain != null && url.isNotEmpty); assert(domain != null && url.isNotEmpty);
...@@ -139,8 +147,7 @@ class CookieManager { ...@@ -139,8 +147,7 @@ class CookieManager {
String _getDomainName(String url) { String _getDomainName(String url) {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
String domain = uri.host; String domain = uri.host;
if (domain == null) if (domain == null) return "";
return "";
return domain.startsWith("www.") ? domain.substring(4) : domain; return domain.startsWith("www.") ? domain.substring(4) : domain;
} }
} }
\ No newline at end of file
...@@ -11,7 +11,8 @@ import 'package:flutter/services.dart'; ...@@ -11,7 +11,8 @@ import 'package:flutter/services.dart';
///doesn't offer the same functionalities as iOS `URLCredentialStorage`. ///doesn't offer the same functionalities as iOS `URLCredentialStorage`.
class HttpAuthCredentialDatabase { class HttpAuthCredentialDatabase {
static HttpAuthCredentialDatabase _instance; static HttpAuthCredentialDatabase _instance;
static const MethodChannel _channel = const MethodChannel('com.pichillilorenzo/flutter_inappwebview_credential_database'); static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_credential_database');
///Gets the database shared instance. ///Gets the database shared instance.
static HttpAuthCredentialDatabase instance() { static HttpAuthCredentialDatabase instance() {
...@@ -24,44 +25,57 @@ class HttpAuthCredentialDatabase { ...@@ -24,44 +25,57 @@ class HttpAuthCredentialDatabase {
return _instance; return _instance;
} }
static Future<dynamic> _handleMethod(MethodCall call) async { static Future<dynamic> _handleMethod(MethodCall call) async {}
}
///Gets a map list of all HTTP auth credentials saved. ///Gets a map list of all HTTP auth credentials saved.
///Each map contains the key `protectionSpace` of type [ProtectionSpace] ///Each map contains the key `protectionSpace` of type [ProtectionSpace]
///and the key `credentials` of type `List<HttpAuthCredential>` that contains all the HTTP auth credentials saved for that `protectionSpace`. ///and the key `credentials` of type `List<HttpAuthCredential>` that contains all the HTTP auth credentials saved for that `protectionSpace`.
Future<List<Map<String, dynamic>>> getAllAuthCredentials() async { Future<List<Map<String, dynamic>>> getAllAuthCredentials() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
List<dynamic> allCredentials = await _channel.invokeMethod('getAllAuthCredentials', args); List<dynamic> allCredentials =
await _channel.invokeMethod('getAllAuthCredentials', args);
List<Map<String, dynamic>> result = []; List<Map<String, dynamic>> result = [];
for (Map<dynamic, dynamic> map in allCredentials) { for (Map<dynamic, dynamic> map in allCredentials) {
Map<dynamic, dynamic> protectionSpace = map["protectionSpace"]; Map<dynamic, dynamic> protectionSpace = map["protectionSpace"];
List<dynamic> credentials = map["credentials"]; List<dynamic> credentials = map["credentials"];
result.add({ result.add({
"protectionSpace": ProtectionSpace(host: protectionSpace["host"], protocol: protectionSpace["protocol"], realm: protectionSpace["realm"], port: protectionSpace["port"]), "protectionSpace": ProtectionSpace(
"credentials": credentials.map((credential) => HttpAuthCredential(username: credential["username"], password: credential["password"])).toList() host: protectionSpace["host"],
protocol: protectionSpace["protocol"],
realm: protectionSpace["realm"],
port: protectionSpace["port"]),
"credentials": credentials
.map((credential) => HttpAuthCredential(
username: credential["username"],
password: credential["password"]))
.toList()
}); });
} }
return result; return result;
} }
///Gets all the HTTP auth credentials saved for that [protectionSpace]. ///Gets all the HTTP auth credentials saved for that [protectionSpace].
Future<List<HttpAuthCredential>> getHttpAuthCredentials({@required ProtectionSpace protectionSpace}) async { Future<List<HttpAuthCredential>> getHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
args.putIfAbsent("realm", () => protectionSpace.realm); args.putIfAbsent("realm", () => protectionSpace.realm);
args.putIfAbsent("port", () => protectionSpace.port); args.putIfAbsent("port", () => protectionSpace.port);
List<dynamic> credentialList = await _channel.invokeMethod('getHttpAuthCredentials', args); List<dynamic> credentialList =
await _channel.invokeMethod('getHttpAuthCredentials', args);
List<HttpAuthCredential> credentials = []; List<HttpAuthCredential> credentials = [];
for (Map<dynamic, dynamic> credential in credentialList) { for (Map<dynamic, dynamic> credential in credentialList) {
credentials.add(HttpAuthCredential(username: credential["username"], password: credential["password"])); credentials.add(HttpAuthCredential(
username: credential["username"], password: credential["password"]));
} }
return credentials; return credentials;
} }
///Saves an HTTP auth [credential] for that [protectionSpace]. ///Saves an HTTP auth [credential] for that [protectionSpace].
Future<void> setHttpAuthCredential({@required ProtectionSpace protectionSpace, @required HttpAuthCredential credential}) async { Future<void> setHttpAuthCredential(
{@required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
...@@ -73,7 +87,9 @@ class HttpAuthCredentialDatabase { ...@@ -73,7 +87,9 @@ class HttpAuthCredentialDatabase {
} }
///Removes an HTTP auth [credential] for that [protectionSpace]. ///Removes an HTTP auth [credential] for that [protectionSpace].
Future<void> removeHttpAuthCredential({@required ProtectionSpace protectionSpace, @required HttpAuthCredential credential}) async { Future<void> removeHttpAuthCredential(
{@required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
...@@ -85,7 +101,8 @@ class HttpAuthCredentialDatabase { ...@@ -85,7 +101,8 @@ class HttpAuthCredentialDatabase {
} }
///Removes all the HTTP auth credentials saved for that [protectionSpace]. ///Removes all the HTTP auth credentials saved for that [protectionSpace].
Future<void> removeHttpAuthCredentials({@required ProtectionSpace protectionSpace}) async { Future<void> removeHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
...@@ -99,4 +116,4 @@ class HttpAuthCredentialDatabase { ...@@ -99,4 +116,4 @@ class HttpAuthCredentialDatabase {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('clearAllAuthCredentials', args); await _channel.invokeMethod('clearAllAuthCredentials', args);
} }
} }
\ No newline at end of file
This diff is collapsed.
...@@ -8,7 +8,6 @@ import 'package:mime/mime.dart'; ...@@ -8,7 +8,6 @@ import 'package:mime/mime.dart';
/// ///
///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`. ///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`.
class InAppLocalhostServer { class InAppLocalhostServer {
HttpServer _server; HttpServer _server;
int _port = 8080; int _port = 8080;
...@@ -28,7 +27,6 @@ class InAppLocalhostServer { ...@@ -28,7 +27,6 @@ class InAppLocalhostServer {
///``` ///```
///The `NSAllowsLocalNetworking` key is available since **iOS 10**. ///The `NSAllowsLocalNetworking` key is available since **iOS 10**.
Future<void> start() async { Future<void> start() async {
if (this._server != null) { if (this._server != null) {
throw Exception('Server already started on http://localhost:$_port'); throw Exception('Server already started on http://localhost:$_port');
} }
...@@ -48,8 +46,7 @@ class InAppLocalhostServer { ...@@ -48,8 +46,7 @@ class InAppLocalhostServer {
path += (path.endsWith('/')) ? 'index.html' : ''; path += (path.endsWith('/')) ? 'index.html' : '';
try { try {
body = (await rootBundle.load(path)) body = (await rootBundle.load(path)).buffer.asUint8List();
.buffer.asUint8List();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
request.response.close(); request.response.close();
...@@ -57,14 +54,17 @@ class InAppLocalhostServer { ...@@ -57,14 +54,17 @@ class InAppLocalhostServer {
} }
var contentType = ['text', 'html']; var contentType = ['text', 'html'];
if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) { if (!request.requestedUri.path.endsWith('/') &&
var mimeType = lookupMimeType(request.requestedUri.path, headerBytes: body); request.requestedUri.pathSegments.isNotEmpty) {
var mimeType =
lookupMimeType(request.requestedUri.path, headerBytes: body);
if (mimeType != null) { if (mimeType != null) {
contentType = mimeType.split('/'); contentType = mimeType.split('/');
} }
} }
request.response.headers.contentType = new ContentType(contentType[0], contentType[1], charset: 'utf-8'); request.response.headers.contentType =
new ContentType(contentType[0], contentType[1], charset: 'utf-8');
request.response.add(body); request.response.add(body);
request.response.close(); request.response.close();
}); });
...@@ -84,5 +84,4 @@ class InAppLocalhostServer { ...@@ -84,5 +84,4 @@ class InAppLocalhostServer {
this._server = null; this._server = null;
} }
} }
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
name: flutter_inappwebview name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview or open an in-app browser window. description: A Flutter plugin that allows you to add an inline webview or open an in-app browser window.
version: 2.0.1 version: 2.0.1+1
author: Lorenzo Pichilli <pichillilorenzo@gmail.com> author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
......
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