diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java index 804af946..fadcbf13 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java @@ -34,7 +34,7 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { public ServiceWorkerManager(final InAppWebViewFlutterPlugin plugin) { this.plugin = plugin; - channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller"); + channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller"); channel.setMethodCallHandler(this); if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) { serviceWorkerController = ServiceWorkerControllerCompat.getInstance(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java index f42a3321..3e98b427 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/WebViewFeatureManager.java @@ -17,7 +17,7 @@ public class WebViewFeatureManager implements MethodChannel.MethodCallHandler { public WebViewFeatureManager(final InAppWebViewFlutterPlugin plugin) { this.plugin = plugin; - channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_android_webviewfeature"); + channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_webviewfeature"); channel.setMethodCallHandler(this); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java index f900c087..a7712e55 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java @@ -6,7 +6,6 @@ import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; -import android.util.Log; import androidx.browser.customtabs.CustomTabColorSchemeParams; import androidx.browser.customtabs.CustomTabsCallback; @@ -29,7 +28,7 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. public MethodChannel channel; public String id; public CustomTabsIntent.Builder builder; - public ChromeCustomTabsOptions options; + public ChromeCustomTabsSettings customSettings; public CustomTabActivityHelper customTabActivityHelper; public CustomTabsSession customTabsSession; protected final int CHROME_CUSTOM_TAB_REQUEST_CODE = 100; @@ -56,8 +55,8 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. final String url = b.getString("url"); - options = new ChromeCustomTabsOptions(); - options.parse((HashMap) b.getSerializable("options")); + customSettings = new ChromeCustomTabsSettings(); + customSettings.parse((HashMap) b.getSerializable("settings")); final List> menuItemList = (List>) b.getSerializable("menuItemList"); @@ -154,23 +153,23 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. } private void prepareCustomTabs(List> menuItemList) { - if (options.addDefaultShareMenuItem != null) { - builder.setShareState(options.addDefaultShareMenuItem ? + if (customSettings.addDefaultShareMenuItem != null) { + builder.setShareState(customSettings.addDefaultShareMenuItem ? CustomTabsIntent.SHARE_STATE_ON : CustomTabsIntent.SHARE_STATE_OFF); } else { - builder.setShareState(options.shareState); + builder.setShareState(customSettings.shareState); } - if (options.toolbarBackgroundColor != null && !options.toolbarBackgroundColor.isEmpty()) { + if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) { CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder(); builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder - .setToolbarColor(Color.parseColor(options.toolbarBackgroundColor)) + .setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor)) .build()); } - builder.setShowTitle(options.showTitle); - builder.setUrlBarHidingEnabled(options.enableUrlBarHiding); - builder.setInstantAppsEnabled(options.instantAppsEnabled); + builder.setShowTitle(customSettings.showTitle); + builder.setUrlBarHidingEnabled(customSettings.enableUrlBarHiding); + builder.setInstantAppsEnabled(customSettings.instantAppsEnabled); for (HashMap menuItem : menuItemList) { int id = (int) menuItem.get("id"); @@ -180,12 +179,12 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. } private void prepareCustomTabsIntent(CustomTabsIntent customTabsIntent) { - if (options.packageName != null) - customTabsIntent.intent.setPackage(options.packageName); + if (customSettings.packageName != null) + customTabsIntent.intent.setPackage(customSettings.packageName); else customTabsIntent.intent.setPackage(CustomTabsHelper.getPackageNameToUse(this)); - if (options.keepAliveEnabled) + if (customSettings.keepAliveEnabled) CustomTabsHelper.addKeepAliveExtra(this, customTabsIntent.intent); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsSettings.java similarity index 95% rename from android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsOptions.java rename to android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsSettings.java index 5da5a347..0d4c0845 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsOptions.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsSettings.java @@ -14,9 +14,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class ChromeCustomTabsOptions implements IWebViewSettings { +public class ChromeCustomTabsSettings implements IWebViewSettings { - final static String LOG_TAG = "ChromeCustomTabsOptions"; + final static String LOG_TAG = "ChromeCustomTabsSettings"; @Deprecated public Boolean addDefaultShareMenuItem; @@ -36,7 +36,7 @@ public class ChromeCustomTabsOptions implements IWebViewSettings options) { + public ChromeCustomTabsSettings parse(Map options) { for (Map.Entry pair : options.entrySet()) { String key = pair.getKey(); Object value = pair.getValue(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java index ec06f5e9..574150bb 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java @@ -43,9 +43,9 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl case "open": { String url = (String) call.argument("url"); - HashMap options = (HashMap) call.argument("options"); + HashMap settings = (HashMap) call.argument("settings"); List> menuItemList = (List>) call.argument("menuItemList"); - open(plugin.activity, id, url, options, menuItemList, result); + open(plugin.activity, id, url, settings, menuItemList, result); } break; case "isAvailable": @@ -56,7 +56,7 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl } } - public void open(Activity activity, String id, String url, HashMap options, + public void open(Activity activity, String id, String url, HashMap settings, List> menuItemList, MethodChannel.Result result) { Intent intent = null; @@ -65,17 +65,17 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl extras.putBoolean("isData", false); extras.putString("id", id); extras.putString("managerId", this.id); - extras.putSerializable("options", options); + extras.putSerializable("settings", settings); extras.putSerializable("menuItemList", (Serializable) menuItemList); - Boolean isSingleInstance = (Boolean) Util.getOrDefault(options, "isSingleInstance", false); - Boolean isTrustedWebActivity = (Boolean) Util.getOrDefault(options, "isTrustedWebActivity", false); + Boolean isSingleInstance = (Boolean) Util.getOrDefault(settings, "isSingleInstance", false); + Boolean isTrustedWebActivity = (Boolean) Util.getOrDefault(settings, "isTrustedWebActivity", false); if (CustomTabActivityHelper.isAvailable(activity)) { intent = new Intent(activity, !isSingleInstance ? (!isTrustedWebActivity ? ChromeCustomTabsActivity.class : TrustedWebActivity.class) : (!isTrustedWebActivity ? ChromeCustomTabsActivitySingleInstance.class : TrustedWebActivitySingleInstance.class)); intent.putExtras(extras); - Boolean noHistory = (Boolean) Util.getOrDefault(options, "noHistory", false); + Boolean noHistory = (Boolean) Util.getOrDefault(settings, "noHistory", false); if (noHistory) { intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java index 16c656f6..72bcf3f8 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java @@ -33,32 +33,32 @@ public class TrustedWebActivity extends ChromeCustomTabsActivity { } private void prepareCustomTabs() { - if (options.toolbarBackgroundColor != null && !options.toolbarBackgroundColor.isEmpty()) { + if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) { CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder(); builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder - .setToolbarColor(Color.parseColor(options.toolbarBackgroundColor)) + .setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor)) .build()); } - if (options.additionalTrustedOrigins != null && !options.additionalTrustedOrigins.isEmpty()) { - builder.setAdditionalTrustedOrigins(options.additionalTrustedOrigins); + if (customSettings.additionalTrustedOrigins != null && !customSettings.additionalTrustedOrigins.isEmpty()) { + builder.setAdditionalTrustedOrigins(customSettings.additionalTrustedOrigins); } - if (options.displayMode != null) { - builder.setDisplayMode(options.displayMode); + if (customSettings.displayMode != null) { + builder.setDisplayMode(customSettings.displayMode); } - builder.setScreenOrientation(options.screenOrientation); + builder.setScreenOrientation(customSettings.screenOrientation); } private void prepareCustomTabsIntent(TrustedWebActivityIntent trustedWebActivityIntent) { Intent intent = trustedWebActivityIntent.getIntent(); - if (options.packageName != null) - intent.setPackage(options.packageName); + if (customSettings.packageName != null) + intent.setPackage(customSettings.packageName); else intent.setPackage(CustomTabsHelper.getPackageNameToUse(this)); - if (options.keepAliveEnabled) + if (customSettings.keepAliveEnabled) CustomTabsHelper.addKeepAliveExtra(this, intent); } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserManager.java index 26a82615..8a5d8a75 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserManager.java @@ -170,7 +170,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { String encoding = (String) arguments.get("encoding"); String baseUrl = (String) arguments.get("baseUrl"); String historyUrl = (String) arguments.get("historyUrl"); - Map options = (Map) arguments.get("options"); + Map settings = (Map) arguments.get("settings"); Map contextMenu = (Map) arguments.get("contextMenu"); Integer windowId = (Integer) arguments.get("windowId"); List> initialUserScripts = (List>) arguments.get("initialUserScripts"); @@ -187,7 +187,7 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler { extras.putString("initialHistoryUrl", historyUrl); extras.putString("id", id); extras.putString("managerId", this.id); - extras.putSerializable("options", (Serializable) options); + extras.putSerializable("settings", (Serializable) settings); extras.putSerializable("contextMenu", (Serializable) contextMenu); extras.putInt("windowId", windowId != null ? windowId : -1); extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts); diff --git a/ios/Classes/PullToRefresh/PullToRefreshControl.swift b/ios/Classes/PullToRefresh/PullToRefreshControl.swift index 854b02f3..4e2a9e4d 100644 --- a/ios/Classes/PullToRefresh/PullToRefreshControl.swift +++ b/ios/Classes/PullToRefresh/PullToRefreshControl.swift @@ -80,7 +80,7 @@ public class PullToRefreshControl : UIRefreshControl, FlutterPlugin { backgroundColor = UIColor(hexString: color) result(true) break - case "setAttributedTitle": + case "setStyledTitle": let attributedTitleMap = arguments!["attributedTitle"] as! [String: Any?] attributedTitle = NSAttributedString.fromMap(map: attributedTitleMap) result(true) diff --git a/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift b/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift index 137d0105..316889a8 100755 --- a/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift +++ b/ios/Classes/SafariViewController/ChromeSafariBrowserManager.swift @@ -34,9 +34,9 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { case "open": let id: String = arguments!["id"] as! String let url = arguments!["url"] as! String - let options = arguments!["options"] as! [String: Any?] + let settings = arguments!["settings"] as! [String: Any?] let menuItemList = arguments!["menuItemList"] as! [[String: Any]] - open(id: id, url: url, options: options, menuItemList: menuItemList, result: result) + open(id: id, url: url, settings: settings, menuItemList: menuItemList, result: result) break case "isAvailable": if #available(iOS 9.0, *) { @@ -51,7 +51,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { } } - public func open(id: String, url: String, options: [String: Any?], menuItemList: [[String: Any]], result: @escaping FlutterResult) { + public func open(id: String, url: String, settings: [String: Any?], menuItemList: [[String: Any]], result: @escaping FlutterResult) { let absoluteUrl = URL(string: url)!.absoluteURL if #available(iOS 9.0, *) { @@ -59,15 +59,15 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { if let flutterViewController = UIApplication.shared.delegate?.window.unsafelyUnwrapped?.rootViewController { // flutterViewController could be casted to FlutterViewController if needed - let safariOptions = SafariBrowserOptions() - let _ = safariOptions.parse(options: options) + let safariSettings = SafariBrowserSettings() + let _ = safariSettings.parse(settings: settings) let safari: SafariViewController if #available(iOS 11.0, *) { let config = SFSafariViewController.Configuration() - config.entersReaderIfAvailable = safariOptions.entersReaderIfAvailable - config.barCollapsingEnabled = safariOptions.barCollapsingEnabled + config.entersReaderIfAvailable = safariSettings.entersReaderIfAvailable + config.barCollapsingEnabled = safariSettings.barCollapsingEnabled safari = SafariViewController(url: absoluteUrl, configuration: config) } else { @@ -79,7 +79,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin { safari.menuItemList = menuItemList safari.prepareMethodChannel() safari.delegate = safari - safari.safariOptions = safariOptions + safari.safariSettings = safariSettings safari.prepareSafariBrowser() flutterViewController.present(safari, animated: true) { diff --git a/ios/Classes/SafariViewController/SafariBrowserOptions.swift b/ios/Classes/SafariViewController/SafariBrowserSettings.swift similarity index 95% rename from ios/Classes/SafariViewController/SafariBrowserOptions.swift rename to ios/Classes/SafariViewController/SafariBrowserSettings.swift index 4f8b5aa4..af67ee76 100755 --- a/ios/Classes/SafariViewController/SafariBrowserOptions.swift +++ b/ios/Classes/SafariViewController/SafariBrowserSettings.swift @@ -9,7 +9,7 @@ import Foundation @available(iOS 9.0, *) @objcMembers -public class SafariBrowserOptions: IWebViewSettings { +public class SafariBrowserSettings: IWebViewSettings { var entersReaderIfAvailable = false var barCollapsingEnabled = false diff --git a/ios/Classes/SafariViewController/SafariViewController.swift b/ios/Classes/SafariViewController/SafariViewController.swift index 0dbea527..60bbe044 100755 --- a/ios/Classes/SafariViewController/SafariViewController.swift +++ b/ios/Classes/SafariViewController/SafariViewController.swift @@ -12,7 +12,7 @@ import SafariServices public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafariViewControllerDelegate { var channel: FlutterMethodChannel? - var safariOptions: SafariBrowserOptions? + var safariSettings: SafariBrowserSettings? var id: String = "" var menuItemList: [[String: Any]] = [] @@ -55,25 +55,25 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa func prepareSafariBrowser() { - guard let safariOptions = safariOptions else { + guard let safariSettings = safariSettings else { return } if #available(iOS 11.0, *) { - self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: safariOptions.dismissButtonStyle)! + self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: safariSettings.dismissButtonStyle)! } if #available(iOS 10.0, *) { - if let preferredBarTintColor = safariOptions.preferredBarTintColor, !preferredBarTintColor.isEmpty { + if let preferredBarTintColor = safariSettings.preferredBarTintColor, !preferredBarTintColor.isEmpty { self.preferredBarTintColor = UIColor(hexString: preferredBarTintColor) } - if let preferredControlTintColor = safariOptions.preferredControlTintColor, !preferredControlTintColor.isEmpty { + if let preferredControlTintColor = safariSettings.preferredControlTintColor, !preferredControlTintColor.isEmpty { self.preferredControlTintColor = UIColor(hexString: preferredControlTintColor) } } - self.modalPresentationStyle = UIModalPresentationStyle(rawValue: safariOptions.presentationStyle)! - self.modalTransitionStyle = UIModalTransitionStyle(rawValue: safariOptions.transitionStyle)! + self.modalPresentationStyle = UIModalPresentationStyle(rawValue: safariSettings.presentationStyle)! + self.modalTransitionStyle = UIModalTransitionStyle(rawValue: safariSettings.transitionStyle)! } func close(result: FlutterResult?) { diff --git a/lib/src/android/service_worker_controller.dart b/lib/src/android/service_worker_controller.dart index 2d364e45..4711c8fa 100644 --- a/lib/src/android/service_worker_controller.dart +++ b/lib/src/android/service_worker_controller.dart @@ -3,15 +3,184 @@ import 'package:flutter/services.dart'; import 'webview_feature.dart'; import '../types.dart'; +///Class that manages Service Workers used by [WebView]. +/// +///**NOTE**: available on Android 24+. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView ([Official API - ServiceWorkerControllerCompat](https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat)) +class ServiceWorkerController { + static ServiceWorkerController? _instance; + static const MethodChannel _channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller'); + + ServiceWorkerClient? serviceWorkerClient; + + ///Gets the [ServiceWorkerController] shared instance. + static ServiceWorkerController instance() { + return (_instance != null) ? _instance! : _init(); + } + + static ServiceWorkerController _init() { + _channel.setMethodCallHandler(_handleMethod); + _instance = ServiceWorkerController(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async { + ServiceWorkerController controller = + ServiceWorkerController.instance(); + ServiceWorkerClient? serviceWorkerClient = + controller.serviceWorkerClient; + + switch (call.method) { + case "shouldInterceptRequest": + if (serviceWorkerClient != null && + serviceWorkerClient.shouldInterceptRequest != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + return (await serviceWorkerClient.shouldInterceptRequest!(request)) + ?.toMap(); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + + return null; + } + + ///Gets whether Service Workers support content URL access. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowContentAccess() + static Future getAllowContentAccess() async { + Map args = {}; + return await _channel.invokeMethod('getAllowContentAccess', args); + } + + ///Gets whether Service Workers support file access. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_FILE_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getAllowFileAccess() + static Future getAllowFileAccess() async { + Map args = {}; + return await _channel.invokeMethod('getAllowFileAccess', args); + } + + ///Gets whether Service Workers are prohibited from loading any resources from the network. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getBlockNetworkLoads() + static Future getBlockNetworkLoads() async { + Map args = {}; + return await _channel.invokeMethod('getBlockNetworkLoads', args); + } + + ///Gets the current setting for overriding the cache mode. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_CACHE_MODE]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#getCacheMode() + static Future getCacheMode() async { + Map args = {}; + return CacheMode.fromValue( + await _channel.invokeMethod('getCacheMode', args)); + } + + ///Enables or disables content URL access from Service Workers. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowContentAccess(boolean) + static Future setAllowContentAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await _channel.invokeMethod('setAllowContentAccess', args); + } + + ///Enables or disables file access within Service Workers. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_FILE_ACCESS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setAllowFileAccess(boolean) + static Future setAllowFileAccess(bool allow) async { + Map args = {}; + args.putIfAbsent("allow", () => allow); + await _channel.invokeMethod('setAllowFileAccess', args); + } + + ///Sets whether Service Workers should not load resources from the network. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setBlockNetworkLoads(boolean) + static Future setBlockNetworkLoads(bool flag) async { + Map args = {}; + args.putIfAbsent("flag", () => flag); + await _channel.invokeMethod('setBlockNetworkLoads', args); + } + + ///Overrides the way the cache is used. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SERVICE_WORKER_CACHE_MODE]. + /// + ///**NOTE**: available on Android 24+. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerWebSettingsCompat#setCacheMode(int) + static Future setCacheMode(CacheMode mode) async { + Map args = {}; + args.putIfAbsent("mode", () => mode.toValue()); + await _channel.invokeMethod('setCacheMode', args); + } +} + +///Class used by clients to capture Service Worker related callbacks. +/// +///**NOTE**: available on Android 24+. +/// +///**Supported Platforms/Implementations**: +///- Android native WebView ([Official API - ServiceWorkerClientCompat](https://developer.android.com/reference/androidx/webkit/ServiceWorkerClientCompat)) +class ServiceWorkerClient { + ///Notify the host application of a resource request and allow the application to return the data. + ///If the return value is `null`, the Service Worker will continue to load the resource as usual. + ///Otherwise, the return response and data will be used. + /// + ///This method is called only if [WebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST] is supported. + ///You can check whether that flag is supported using [WebViewFeature.isFeatureSupported]. + /// + ///[request] represents an object containing the details of the request. + /// + ///**NOTE**: available on Android 24+. + final Future Function(WebResourceRequest request)? + shouldInterceptRequest; + + ServiceWorkerClient({this.shouldInterceptRequest}); +} + ///Class that represents an Android-specific class that manages Service Workers used by [WebView]. /// ///**NOTE**: available on Android 24+. /// ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerControllerCompat +/// +///Use [ServiceWorkerController] instead. +@Deprecated("Use ServiceWorkerController instead") class AndroidServiceWorkerController { static AndroidServiceWorkerController? _instance; static const MethodChannel _channel = const MethodChannel( - 'com.pichillilorenzo/flutter_inappwebview_android_serviceworkercontroller'); + 'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller'); AndroidServiceWorkerClient? serviceWorkerClient; @@ -150,6 +319,8 @@ class AndroidServiceWorkerController { ///**NOTE**: available on Android 24+. /// ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/ServiceWorkerClientCompat +///Use [ServiceWorkerClient] instead. +@Deprecated("Use ServiceWorkerClient instead") class AndroidServiceWorkerClient { ///Notify the host application of a resource request and allow the application to return the data. ///If the return value is `null`, the Service Worker will continue to load the resource as usual. diff --git a/lib/src/android/webview_feature.dart b/lib/src/android/webview_feature.dart index 650b241e..064c1019 100644 --- a/lib/src/android/webview_feature.dart +++ b/lib/src/android/webview_feature.dart @@ -2,9 +2,269 @@ import 'dart:async'; import 'package:flutter/services.dart'; ///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device. +class WebViewFeature { + static const MethodChannel _channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webviewfeature'); + + final String _value; + + const WebViewFeature._internal(this._value); + + static final Set values = [ + WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, + WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, + WebViewFeature.FORCE_DARK, + WebViewFeature.FORCE_DARK_STRATEGY, + WebViewFeature.GET_WEB_CHROME_CLIENT, + WebViewFeature.GET_WEB_VIEW_CLIENT, + WebViewFeature.GET_WEB_VIEW_RENDERER, + WebViewFeature.MULTI_PROCESS, + WebViewFeature.OFF_SCREEN_PRERASTER, + WebViewFeature.POST_WEB_MESSAGE, + WebViewFeature.PROXY_OVERRIDE, + WebViewFeature.RECEIVE_HTTP_ERROR, + WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR, + WebViewFeature.SAFE_BROWSING_ALLOWLIST, + WebViewFeature.SAFE_BROWSING_ENABLE, + WebViewFeature.SAFE_BROWSING_HIT, + WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, + WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, + WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, + WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, + WebViewFeature.SERVICE_WORKER_BASIC_USAGE, + WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, + WebViewFeature.SERVICE_WORKER_CACHE_MODE, + WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, + WebViewFeature.SERVICE_WORKER_FILE_ACCESS, + WebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST, + WebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS, + WebViewFeature.START_SAFE_BROWSING, + WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, + WebViewFeature.VISUAL_STATE_CALLBACK, + WebViewFeature.WEB_MESSAGE_CALLBACK_ON_MESSAGE, + WebViewFeature.WEB_MESSAGE_LISTENER, + WebViewFeature.WEB_MESSAGE_PORT_CLOSE, + WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, + WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, + WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, + WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, + WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, + WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, + WebViewFeature.WEB_VIEW_RENDERER_TERMINATE, + ].toSet(); + + static WebViewFeature? fromValue(String? value) { + if (value != null) { + try { + return WebViewFeature.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + String toValue() => _value; + + @override + String toString() => _value; + + /// + static const CREATE_WEB_MESSAGE_CHANNEL = + const WebViewFeature._internal("CREATE_WEB_MESSAGE_CHANNEL"); + + /// + static const DISABLED_ACTION_MODE_MENU_ITEMS = + const WebViewFeature._internal("DISABLED_ACTION_MODE_MENU_ITEMS"); + + /// + static const FORCE_DARK = const WebViewFeature._internal("FORCE_DARK"); + + /// + static const FORCE_DARK_STRATEGY = + const WebViewFeature._internal("FORCE_DARK_STRATEGY"); + + /// + static const GET_WEB_CHROME_CLIENT = + const WebViewFeature._internal("GET_WEB_CHROME_CLIENT"); + + /// + static const GET_WEB_VIEW_CLIENT = + const WebViewFeature._internal("GET_WEB_VIEW_CLIENT"); + + /// + static const GET_WEB_VIEW_RENDERER = + const WebViewFeature._internal("GET_WEB_VIEW_RENDERER"); + + /// + static const MULTI_PROCESS = + const WebViewFeature._internal("MULTI_PROCESS"); + + /// + static const OFF_SCREEN_PRERASTER = + const WebViewFeature._internal("OFF_SCREEN_PRERASTER"); + + /// + static const POST_WEB_MESSAGE = + const WebViewFeature._internal("POST_WEB_MESSAGE"); + + /// + static const PROXY_OVERRIDE = + const WebViewFeature._internal("PROXY_OVERRIDE"); + + /// + static const RECEIVE_HTTP_ERROR = + const WebViewFeature._internal("RECEIVE_HTTP_ERROR"); + + /// + static const RECEIVE_WEB_RESOURCE_ERROR = + const WebViewFeature._internal("RECEIVE_WEB_RESOURCE_ERROR"); + + /// + static const SAFE_BROWSING_ALLOWLIST = + const WebViewFeature._internal("SAFE_BROWSING_ALLOWLIST"); + + /// + static const SAFE_BROWSING_ENABLE = + const WebViewFeature._internal("SAFE_BROWSING_ENABLE"); + + /// + static const SAFE_BROWSING_HIT = + const WebViewFeature._internal("SAFE_BROWSING_HIT"); + + /// + static const SAFE_BROWSING_PRIVACY_POLICY_URL = + const WebViewFeature._internal("SAFE_BROWSING_PRIVACY_POLICY_URL"); + + /// + static const SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = + const WebViewFeature._internal( + "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY"); + + /// + static const SAFE_BROWSING_RESPONSE_PROCEED = + const WebViewFeature._internal("SAFE_BROWSING_RESPONSE_PROCEED"); + + /// + static const SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = + const WebViewFeature._internal( + "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL"); + + ///Use [SAFE_BROWSING_ALLOWLIST] instead. + @Deprecated('Use SAFE_BROWSING_ALLOWLIST instead') + static const SAFE_BROWSING_WHITELIST = + const WebViewFeature._internal("SAFE_BROWSING_WHITELIST"); + + /// + static const SERVICE_WORKER_BASIC_USAGE = + const WebViewFeature._internal("SERVICE_WORKER_BASIC_USAGE"); + + /// + static const SERVICE_WORKER_BLOCK_NETWORK_LOADS = + const WebViewFeature._internal( + "SERVICE_WORKER_BLOCK_NETWORK_LOADS"); + + /// + static const SERVICE_WORKER_CACHE_MODE = + const WebViewFeature._internal("SERVICE_WORKER_CACHE_MODE"); + + /// + static const SERVICE_WORKER_CONTENT_ACCESS = + const WebViewFeature._internal("SERVICE_WORKER_CONTENT_ACCESS"); + + /// + static const SERVICE_WORKER_FILE_ACCESS = + const WebViewFeature._internal("SERVICE_WORKER_FILE_ACCESS"); + + /// + static const SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = + const WebViewFeature._internal( + "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST"); + + /// + static const SHOULD_OVERRIDE_WITH_REDIRECTS = + const WebViewFeature._internal("SHOULD_OVERRIDE_WITH_REDIRECTS"); + + /// + static const START_SAFE_BROWSING = + const WebViewFeature._internal("START_SAFE_BROWSING"); + + /// + static const TRACING_CONTROLLER_BASIC_USAGE = + const WebViewFeature._internal("TRACING_CONTROLLER_BASIC_USAGE"); + + /// + static const VISUAL_STATE_CALLBACK = + const WebViewFeature._internal("VISUAL_STATE_CALLBACK"); + + /// + static const WEB_MESSAGE_CALLBACK_ON_MESSAGE = + const WebViewFeature._internal("WEB_MESSAGE_CALLBACK_ON_MESSAGE"); + + /// + static const WEB_MESSAGE_LISTENER = + const WebViewFeature._internal("WEB_MESSAGE_LISTENER"); + + /// + static const WEB_MESSAGE_PORT_CLOSE = + const WebViewFeature._internal("WEB_MESSAGE_PORT_CLOSE"); + + /// + static const WEB_MESSAGE_PORT_POST_MESSAGE = + const WebViewFeature._internal("WEB_MESSAGE_PORT_POST_MESSAGE"); + + /// + static const WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = + const WebViewFeature._internal( + "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK"); + + /// + static const WEB_RESOURCE_ERROR_GET_CODE = + const WebViewFeature._internal("WEB_RESOURCE_ERROR_GET_CODE"); + + /// + static const WEB_RESOURCE_ERROR_GET_DESCRIPTION = + const WebViewFeature._internal( + "WEB_RESOURCE_ERROR_GET_DESCRIPTION"); + + /// + static const WEB_RESOURCE_REQUEST_IS_REDIRECT = + const WebViewFeature._internal("WEB_RESOURCE_REQUEST_IS_REDIRECT"); + + /// + static const WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = + const WebViewFeature._internal( + "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE"); + + /// + static const WEB_VIEW_RENDERER_TERMINATE = + const WebViewFeature._internal("WEB_VIEW_RENDERER_TERMINATE"); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; + + ///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher, + ///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device, + ///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`. + /// + ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String) + static Future isFeatureSupported(WebViewFeature feature) async { + Map args = {}; + args.putIfAbsent("feature", () => feature.toValue()); + return await _channel.invokeMethod('isFeatureSupported', args); + } +} + + +///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device. +///Use [WebViewFeature] instead. +@Deprecated("Use WebViewFeature instead") class AndroidWebViewFeature { static const MethodChannel _channel = const MethodChannel( - 'com.pichillilorenzo/flutter_inappwebview_android_webviewfeature'); + 'com.pichillilorenzo/flutter_inappwebview_webviewfeature'); final String _value; diff --git a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart b/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart index 1857c54d..b338dcfb 100755 --- a/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart +++ b/lib/src/chrome_safari_browser/android/chrome_custom_tabs_options.dart @@ -3,12 +3,14 @@ import 'dart:ui'; import '../../util.dart'; import '../../types.dart'; -import '../chrome_safari_browser_options.dart'; +import '../chrome_safari_browser_settings.dart'; import '../chrome_safari_browser.dart'; import '../../in_app_webview/android/in_app_webview_options.dart'; ///This class represents all the Android-only [ChromeSafariBrowser] options available. +///Use [ChromeSafariBrowserSettings] instead. +@Deprecated('Use ChromeSafariBrowserSettings instead') class AndroidChromeCustomTabsOptions implements ChromeSafariBrowserOptions, AndroidOptions { ///Use [shareState] instead. diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser.dart b/lib/src/chrome_safari_browser/chrome_safari_browser.dart index 2f476d1d..3a0df349 100755 --- a/lib/src/chrome_safari_browser/chrome_safari_browser.dart +++ b/lib/src/chrome_safari_browser/chrome_safari_browser.dart @@ -4,7 +4,7 @@ import 'dart:collection'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/src/util.dart'; -import 'chrome_safari_browser_options.dart'; +import 'chrome_safari_browser_settings.dart'; class ChromeSafariBrowserAlreadyOpenedException implements Exception { final dynamic message; @@ -84,8 +84,13 @@ class ChromeSafariBrowser { ///[url]: The [url] to load. /// ///[options]: Options for the [ChromeSafariBrowser]. + /// + ///[settings]: Settings for the [ChromeSafariBrowser]. Future open( - {required Uri url, ChromeSafariBrowserClassOptions? options}) async { + {required Uri url, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') ChromeSafariBrowserClassOptions? options, + ChromeSafariBrowserSettings? settings}) async { assert(url.toString().isNotEmpty); this.throwIsAlreadyOpened(message: 'Cannot open $url!'); @@ -94,10 +99,13 @@ class ChromeSafariBrowser { menuItemList.add({"id": value.id, "label": value.label}); }); + var initialSettings = settings?.toMap() ?? options?.toMap() ?? + ChromeSafariBrowserSettings().toMap(); + Map args = {}; args.putIfAbsent('id', () => id); args.putIfAbsent('url', () => url.toString()); - args.putIfAbsent('options', () => options?.toMap() ?? {}); + args.putIfAbsent('settings', () => initialSettings); args.putIfAbsent('menuItemList', () => menuItemList); await _sharedChannel.invokeMethod('open', args); this._isOpened = true; diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_options.dart b/lib/src/chrome_safari_browser/chrome_safari_browser_options.dart deleted file mode 100755 index ca432ec5..00000000 --- a/lib/src/chrome_safari_browser/chrome_safari_browser_options.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/foundation.dart'; - -import 'android/chrome_custom_tabs_options.dart'; -import 'ios/safari_options.dart'; - -class ChromeSafariBrowserOptions { - Map toMap() { - return {}; - } - - static ChromeSafariBrowserOptions fromMap(Map map) { - return new ChromeSafariBrowserOptions(); - } - - ChromeSafariBrowserOptions copy() { - return ChromeSafariBrowserOptions.fromMap(this.toMap()); - } - - Map toJson() { - return this.toMap(); - } - - @override - String toString() { - return toMap().toString(); - } -} - -///Class that represents the options that can be used for an [ChromeSafariBrowser] window. -class ChromeSafariBrowserClassOptions { - ///Android-specific options. - AndroidChromeCustomTabsOptions? android; - - ///iOS-specific options. - IOSSafariOptions? ios; - - ChromeSafariBrowserClassOptions({this.android, this.ios}) { - this.android = this.android ?? AndroidChromeCustomTabsOptions(); - this.ios = this.ios ?? IOSSafariOptions(); - } - - Map toMap() { - Map options = {}; - if (defaultTargetPlatform == TargetPlatform.android) - options.addAll(this.android?.toMap() ?? {}); - else if (defaultTargetPlatform == TargetPlatform.iOS) - options.addAll(this.ios?.toMap() ?? {}); - - return options; - } - - Map toJson() { - return this.toMap(); - } - - @override - String toString() { - return toMap().toString(); - } -} diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart b/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart new file mode 100755 index 00000000..e62f6637 --- /dev/null +++ b/lib/src/chrome_safari_browser/chrome_safari_browser_settings.dart @@ -0,0 +1,306 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import '../util.dart'; +import 'android/chrome_custom_tabs_options.dart'; +import 'ios/safari_options.dart'; +import '../types.dart'; + +class ChromeSafariBrowserOptions { + Map toMap() { + return {}; + } + + static ChromeSafariBrowserOptions fromMap(Map map) { + return new ChromeSafariBrowserOptions(); + } + + ChromeSafariBrowserOptions copy() { + return ChromeSafariBrowserOptions.fromMap(this.toMap()); + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} + +///Class that represents the settings that can be used for an [ChromeSafariBrowser] window. +class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions { + + ///The share state that should be applied to the custom tab. The default value is [CustomTabsShareState.SHARE_STATE_DEFAULT]. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + CustomTabsShareState shareState; + + ///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool showTitle; + + ///Set the custom background color of the toolbar. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + Color? toolbarBackgroundColor; + + ///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool enableUrlBarHiding; + + ///Set to `true` to enable Instant Apps. The default value is `false`. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool instantAppsEnabled; + + ///Set an explicit application package name that limits + ///the components this Intent will resolve to. If left to the default + ///value of null, all components in all applications will considered. + ///If non-null, the Intent can only match the components in the given + ///application package. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + String? packageName; + + ///Set to `true` to enable Keep Alive. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool keepAliveEnabled; + + ///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool isSingleInstance; + + ///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool noHistory; + + ///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + bool isTrustedWebActivity; + + ///Sets a list of additional trusted origins that the user may navigate or be redirected to from the starting uri. + /// + ///**NOTE**: Available only in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + List additionalTrustedOrigins; + + ///Sets a display mode of a Trusted Web Activity. + /// + ///**NOTE**: Available only in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + TrustedWebActivityDisplayMode? displayMode; + + ///Sets a screen orientation. This can be used e.g. to enable the locking of an orientation lock type. + /// + ///**NOTE**: Available only in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + TrustedWebActivityScreenOrientation screenOrientation; + + ///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + bool entersReaderIfAvailable; + + ///Set to `true` to enable bar collapsing. The default value is `false`. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + bool barCollapsingEnabled; + + ///Set the custom style for the dismiss button. The default value is [DismissButtonStyle.DONE]. + /// + ///**NOTE**: available on iOS 11.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + DismissButtonStyle dismissButtonStyle; + + ///Set the custom background color of the navigation bar and the toolbar. + /// + ///**NOTE**: available on iOS 10.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + Color? preferredBarTintColor; + + ///Set the custom color of the control buttons on the navigation bar and the toolbar. + /// + ///**NOTE**: available on iOS 10.0+. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + Color? preferredControlTintColor; + + ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + ModalPresentationStyle presentationStyle; + + ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. + /// + ///**Supported Platforms/Implementations**: + ///- iOS + ModalTransitionStyle transitionStyle; + + ChromeSafariBrowserSettings( + {this.shareState = CustomTabsShareState.SHARE_STATE_DEFAULT, + this.showTitle = true, + this.toolbarBackgroundColor, + this.enableUrlBarHiding = false, + this.instantAppsEnabled = false, + this.packageName, + this.keepAliveEnabled = false, + this.isSingleInstance = false, + this.noHistory = false, + this.isTrustedWebActivity = false, + this.additionalTrustedOrigins = const [], + this.displayMode, + this.screenOrientation = TrustedWebActivityScreenOrientation.DEFAULT, + this.entersReaderIfAvailable = false, + this.barCollapsingEnabled = false, + this.dismissButtonStyle = DismissButtonStyle.DONE, + this.preferredBarTintColor, + this.preferredControlTintColor, + this.presentationStyle = ModalPresentationStyle.FULL_SCREEN, + this.transitionStyle = ModalTransitionStyle.COVER_VERTICAL}); + + @override + Map toMap() { + return { + "shareState": shareState.toValue(), + "showTitle": showTitle, + "toolbarBackgroundColor": toolbarBackgroundColor?.toHex(), + "enableUrlBarHiding": enableUrlBarHiding, + "instantAppsEnabled": instantAppsEnabled, + "packageName": packageName, + "keepAliveEnabled": keepAliveEnabled, + "isSingleInstance": isSingleInstance, + "noHistory": noHistory, + "isTrustedWebActivity": isTrustedWebActivity, + "additionalTrustedOrigins": additionalTrustedOrigins, + "displayMode": displayMode?.toMap(), + "screenOrientation": screenOrientation.toValue(), + "entersReaderIfAvailable": entersReaderIfAvailable, + "barCollapsingEnabled": barCollapsingEnabled, + "dismissButtonStyle": dismissButtonStyle.toValue(), + "preferredBarTintColor": preferredBarTintColor?.toHex(), + "preferredControlTintColor": preferredControlTintColor?.toHex(), + "presentationStyle": presentationStyle.toValue(), + "transitionStyle": transitionStyle.toValue() + }; + } + + static ChromeSafariBrowserSettings fromMap(Map map) { + ChromeSafariBrowserSettings options = + new ChromeSafariBrowserSettings(); + options.shareState = map["shareState"]; + options.showTitle = map["showTitle"]; + options.toolbarBackgroundColor = + UtilColor.fromHex(map["toolbarBackgroundColor"]); + options.enableUrlBarHiding = map["enableUrlBarHiding"]; + options.instantAppsEnabled = map["instantAppsEnabled"]; + options.packageName = map["packageName"]; + options.keepAliveEnabled = map["keepAliveEnabled"]; + options.isSingleInstance = map["isSingleInstance"]; + options.noHistory = map["noHistory"]; + options.isTrustedWebActivity = map["isTrustedWebActivity"]; + options.additionalTrustedOrigins = map["additionalTrustedOrigins"]; + switch (map["displayMode"]["type"]) { + case "IMMERSIVE_MODE": + options.displayMode = + TrustedWebActivityImmersiveDisplayMode.fromMap(map["displayMode"]); + break; + case "DEFAULT_MODE": + default: + options.displayMode = TrustedWebActivityDefaultDisplayMode(); + break; + } + options.screenOrientation = map["screenOrientation"]; + return options; + } + + @override + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } + + @override + ChromeSafariBrowserSettings copy() { + return ChromeSafariBrowserSettings.fromMap(this.toMap()); + } +} + +///Class that represents the options that can be used for an [ChromeSafariBrowser] window. +///Use [ChromeSafariBrowserSettings] instead. +@Deprecated('Use ChromeSafariBrowserSettings instead') +class ChromeSafariBrowserClassOptions { + ///Android-specific options. + AndroidChromeCustomTabsOptions? android; + + ///iOS-specific options. + IOSSafariOptions? ios; + + ChromeSafariBrowserClassOptions({this.android, this.ios}) { + this.android = this.android ?? AndroidChromeCustomTabsOptions(); + this.ios = this.ios ?? IOSSafariOptions(); + } + + Map toMap() { + Map options = {}; + if (defaultTargetPlatform == TargetPlatform.android) + options.addAll(this.android?.toMap() ?? {}); + else if (defaultTargetPlatform == TargetPlatform.iOS) + options.addAll(this.ios?.toMap() ?? {}); + + return options; + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} diff --git a/lib/src/chrome_safari_browser/ios/safari_options.dart b/lib/src/chrome_safari_browser/ios/safari_options.dart index b7a2c4a7..8385fab4 100755 --- a/lib/src/chrome_safari_browser/ios/safari_options.dart +++ b/lib/src/chrome_safari_browser/ios/safari_options.dart @@ -3,12 +3,14 @@ import 'dart:ui'; import '../../util.dart'; import '../../types.dart'; -import '../chrome_safari_browser_options.dart'; +import '../chrome_safari_browser_settings.dart'; import '../chrome_safari_browser.dart'; import '../../in_app_webview/ios/in_app_webview_options.dart'; ///This class represents all the iOS-only [ChromeSafariBrowser] options available. +///Use [ChromeSafariBrowserSettings] instead. +@Deprecated('Use ChromeSafariBrowserSettings instead') class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions { ///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`. bool entersReaderIfAvailable; diff --git a/lib/src/chrome_safari_browser/main.dart b/lib/src/chrome_safari_browser/main.dart index 1565ca1e..0316d2e9 100644 --- a/lib/src/chrome_safari_browser/main.dart +++ b/lib/src/chrome_safari_browser/main.dart @@ -1,4 +1,4 @@ export 'chrome_safari_browser.dart'; -export 'chrome_safari_browser_options.dart'; +export 'chrome_safari_browser_settings.dart'; export 'android/main.dart'; export 'ios/main.dart'; diff --git a/lib/src/http_auth_credentials_database.dart b/lib/src/http_auth_credentials_database.dart index 24b34ce0..eaf4e236 100755 --- a/lib/src/http_auth_credentials_database.dart +++ b/lib/src/http_auth_credentials_database.dart @@ -28,7 +28,11 @@ class HttpAuthCredentialDatabase { ///Gets a map list of all HTTP auth credentials saved. ///Each map contains the key `protectionSpace` of type [URLProtectionSpace] - ///and the key `credentials` of type `List` that contains all the HTTP auth credentials saved for that `protectionSpace`. + ///and the key `credentials` of type List<[URLCredential]> that contains all the HTTP auth credentials saved for that `protectionSpace`. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - URLCredentialStorage.allCredentials](https://developer.apple.com/documentation/foundation/urlcredentialstorage/1413859-allcredentials)) Future> getAllAuthCredentials() async { Map args = {}; @@ -48,6 +52,10 @@ class HttpAuthCredentialDatabase { } ///Gets all the HTTP auth credentials saved for that [protectionSpace]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future> getHttpAuthCredentials( {required URLProtectionSpace protectionSpace}) async { Map args = {}; @@ -68,6 +76,10 @@ class HttpAuthCredentialDatabase { } ///Saves an HTTP auth [credential] for that [protectionSpace]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - URLCredentialStorage.set](https://developer.apple.com/documentation/foundation/urlcredentialstorage/1407227-set)) Future setHttpAuthCredential( {required URLProtectionSpace protectionSpace, required URLCredential credential}) async { @@ -82,6 +94,10 @@ class HttpAuthCredentialDatabase { } ///Removes an HTTP auth [credential] for that [protectionSpace]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ([Official API - URLCredentialStorage.remove](https://developer.apple.com/documentation/foundation/urlcredentialstorage/1408664-remove)) Future removeHttpAuthCredential( {required URLProtectionSpace protectionSpace, required URLCredential credential}) async { @@ -96,6 +112,10 @@ class HttpAuthCredentialDatabase { } ///Removes all the HTTP auth credentials saved for that [protectionSpace]. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future removeHttpAuthCredentials( {required URLProtectionSpace protectionSpace}) async { Map args = {}; @@ -107,6 +127,10 @@ class HttpAuthCredentialDatabase { } ///Removes all the HTTP auth credentials saved in the database. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS Future clearAllAuthCredentials() async { Map args = {}; await _channel.invokeMethod('clearAllAuthCredentials', args); diff --git a/lib/src/in_app_browser/in_app_browser.dart b/lib/src/in_app_browser/in_app_browser.dart index 8827b906..552e801d 100755 --- a/lib/src/in_app_browser/in_app_browser.dart +++ b/lib/src/in_app_browser/in_app_browser.dart @@ -103,6 +103,8 @@ class InAppBrowser { ///[urlRequest]: The [urlRequest] to load. /// ///[options]: Options for the [InAppBrowser]. + /// + ///[settings]: Settings for the [InAppBrowser]. Future openUrlRequest( {required URLRequest urlRequest, // ignore: deprecated_member_use_from_same_package @@ -166,6 +168,8 @@ class InAppBrowser { ///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value. /// ///[options]: Options for the [InAppBrowser]. + /// + ///[settings]: Settings for the [InAppBrowser]. Future openFile( {required String assetFilePath, // ignore: deprecated_member_use_from_same_package @@ -203,18 +207,20 @@ class InAppBrowser { ///The [androidHistoryUrl] parameter is the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. This parameter is used only on Android. /// ///The [options] parameter specifies the options for the [InAppBrowser]. + /// + ///[settings]: Settings for the [InAppBrowser]. Future openData( {required String data, String mimeType = "text/html", String encoding = "utf8", Uri? baseUrl, - Uri? androidHistoryUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + Uri? historyUrl, // ignore: deprecated_member_use_from_same_package @Deprecated('Use settings instead') InAppBrowserClassOptions? options, InAppBrowserClassSettings? settings}) async { this.throwIfAlreadyOpened(message: 'Cannot open data!'); - var initialSettings = settings?.toMap() ?? options?.toMap() ?? InAppBrowserClassSettings().toMap(); @@ -226,7 +232,7 @@ class InAppBrowser { args.putIfAbsent('encoding', () => encoding); args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); args.putIfAbsent( - 'historyUrl', () => androidHistoryUrl?.toString() ?? "about:blank"); + 'historyUrl', () => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank"); args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); args.putIfAbsent('windowId', () => windowId); args.putIfAbsent('implementation', () => implementation.toValue()); diff --git a/lib/src/in_app_webview/android/in_app_webview_controller.dart b/lib/src/in_app_webview/android/in_app_webview_controller.dart index ae5213d4..4966db68 100644 --- a/lib/src/in_app_webview/android/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/android/in_app_webview_controller.dart @@ -18,7 +18,7 @@ abstract class AndroidInAppWebViewControllerMixin { ///This should not be called if Safe Browsing has been disabled by manifest tag or [AndroidInAppWebViewOptions.safeBrowsingEnabled]. ///This prepares resources used for Safe Browsing. /// - ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.START_SAFE_BROWSING]. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.START_SAFE_BROWSING]. /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebView.startSafeBrowsing](https://developer.android.com/reference/android/webkit/WebView#startSafeBrowsing(android.content.Context,%20android.webkit.ValueCallback%3Cjava.lang.Boolean%3E))) diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index 4c18cdc6..6f1f750a 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -1327,6 +1327,8 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe Uri? iosAllowingReadAccessTo, Uri? allowingReadAccessTo}) async { assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert(allowingReadAccessTo == null || + allowingReadAccessTo.isScheme("file")); assert(iosAllowingReadAccessTo == null || iosAllowingReadAccessTo.isScheme("file")); @@ -1385,6 +1387,8 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe @Deprecated('Use allowingReadAccessTo instead') Uri? iosAllowingReadAccessTo, Uri? allowingReadAccessTo}) async { + assert(allowingReadAccessTo == null || + allowingReadAccessTo.isScheme("file")); assert(iosAllowingReadAccessTo == null || iosAllowingReadAccessTo.isScheme("file")); @@ -2514,7 +2518,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe /// ///This method should be called when the page is loaded, for example, when the [WebView.onLoadStop] is fired, otherwise the [WebMessageChannel] won't work. /// - ///**NOTE for Android native WebView**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.CREATE_WEB_MESSAGE_CHANNEL]. + ///**NOTE for Android native WebView**: This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL]. /// ///**NOTE**: On iOS, it is implemented using JavaScript. /// @@ -2534,7 +2538,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe /// ///A target origin can be set as a wildcard ("*"). However this is not recommended. /// - ///**NOTE for Android native WebView**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.POST_WEB_MESSAGE]. + ///**NOTE for Android native WebView**: This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.POST_WEB_MESSAGE]. /// ///**NOTE**: On iOS, it is implemented using JavaScript. /// @@ -2692,7 +2696,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe /// // Flutter App /// child: InAppWebView( /// onWebViewCreated: (controller) async { - /// if (!Platform.isAndroid || await AndroidWebViewFeature.isFeatureSupported(AndroidWebViewFeature.WEB_MESSAGE_LISTENER)) { + /// if (!Platform.isAndroid || await WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { /// await controller.addWebMessageListener(WebMessageListener( /// jsObjectName: "myObject", /// onPostMessage: (message, sourceOrigin, isMainFrame, replyProxy) { @@ -2706,7 +2710,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe /// ), ///``` /// - ///**NOTE for Android**: This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.WEB_MESSAGE_LISTENER]. + ///**NOTE for Android**: This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.WEB_MESSAGE_LISTENER]. /// ///**NOTE for iOS**: This is implemented using Javascript. /// @@ -2764,7 +2768,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe ///Returns a URL pointing to the privacy policy for Safe Browsing reporting. /// - ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL]. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL]. /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebViewCompat.getSafeBrowsingPrivacyPolicyUrl](https://developer.android.com/reference/androidx/webkit/WebViewCompat#getSafeBrowsingPrivacyPolicyUrl())) @@ -2787,7 +2791,7 @@ class InAppWebViewController with AndroidInAppWebViewControllerMixin, IOSInAppWe /// ///All other rules, including wildcards, are invalid. The correct syntax for hosts is defined by [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2.2). /// - ///This method should only be called if [AndroidWebViewFeature.isFeatureSupported] returns `true` for [AndroidWebViewFeature.SAFE_BROWSING_ALLOWLIST]. + ///This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.SAFE_BROWSING_ALLOWLIST]. /// ///[hosts] represents the list of hosts. This value must never be `null`. /// diff --git a/lib/src/pull_to_refresh/pull_to_refresh_controller.dart b/lib/src/pull_to_refresh/pull_to_refresh_controller.dart index eec0a0d9..cce8bd77 100644 --- a/lib/src/pull_to_refresh/pull_to_refresh_controller.dart +++ b/lib/src/pull_to_refresh/pull_to_refresh_controller.dart @@ -132,13 +132,21 @@ class PullToRefreshController { await _channel?.invokeMethod('setSize', args); } - ///Sets the styled title text to display in the refresh control. - /// - ///**NOTE**: Available only on iOS. + ///Use [setStyledTitle] instead. + @Deprecated("Use setStyledTitle instead") Future setAttributedTitle(IOSNSAttributedString attributedTitle) async { Map args = {}; args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); - await _channel?.invokeMethod('setAttributedTitle', args); + await _channel?.invokeMethod('setStyledTitle', args); + } + + ///Sets the styled title text to display in the refresh control. + /// + ///**NOTE**: Available only on iOS. + Future setStyledTitle(AttributedString attributedTitle) async { + Map args = {}; + args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); + await _channel?.invokeMethod('setStyledTitle', args); } void initMethodChannel(dynamic id) {