added Find Interaction Controller
This commit is contained in:
parent
06ef336c28
commit
f5a048cb69
|
@ -5,8 +5,9 @@
|
|||
- Added `ProxyController` for Android
|
||||
- Added `PrintJobController` to manage print jobs
|
||||
- Added `WebAuthenticationSession` for iOS
|
||||
- Added `FindInteractionController` for Android and iOS
|
||||
- Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState`, `isInFullscreen`, `getCameraCaptureState`, `setCameraCaptureState`, `getMicrophoneCaptureState`, `setMicrophoneCaptureState` WebView controller methods
|
||||
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy`, `willSuppressErrorPage`, `algorithmicDarkeningAllowed`, `requestedWithHeaderMode`, `enterpriseAuthenticationAppLinkPolicyEnabled` WebView settings
|
||||
- Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS`, `forceDarkStrategy`, `willSuppressErrorPage`, `algorithmicDarkeningAllowed`, `requestedWithHeaderMode`, `enterpriseAuthenticationAppLinkPolicyEnabled`, `isElementFullscreenEnabled`, `isFindInteractionEnabled` WebView settings
|
||||
- Added `onCameraCaptureStateChanged`, `onMicrophoneCaptureStateChanged` WebView events
|
||||
- Added support for `onPermissionRequest` event on iOS 15.0+
|
||||
- Added `debugLoggingSettings` static property for WebView and ChromeSafariBrowser
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.find_interaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class FindInteractionChannelDelegate extends ChannelDelegateImpl {
|
||||
@Nullable
|
||||
private FindInteractionController findInteractionController;
|
||||
|
||||
public FindInteractionChannelDelegate(@NonNull FindInteractionController findInteractionController, @NonNull MethodChannel channel) {
|
||||
super(channel);
|
||||
this.findInteractionController = findInteractionController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
|
||||
switch (call.method) {
|
||||
case "findAllAsync":
|
||||
if (findInteractionController != null && findInteractionController.webView != null) {
|
||||
String find = (String) call.argument("find");
|
||||
findInteractionController.webView.findAllAsync(find);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "findNext":
|
||||
if (findInteractionController != null && findInteractionController.webView != null) {
|
||||
Boolean forward = (Boolean) call.argument("forward");
|
||||
findInteractionController.webView.findNext(forward);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "clearMatches":
|
||||
if (findInteractionController != null && findInteractionController.webView != null) {
|
||||
findInteractionController.webView.clearMatches();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("activeMatchOrdinal", activeMatchOrdinal);
|
||||
obj.put("numberOfMatches", numberOfMatches);
|
||||
obj.put("isDoneCounting", isDoneCounting);
|
||||
channel.invokeMethod("onFindResultReceived", obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
findInteractionController = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.find_interaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
|
||||
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class FindInteractionController implements Disposable {
|
||||
static final String LOG_TAG = "FindInteractionController";
|
||||
public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_find_interaction_";
|
||||
|
||||
@Nullable
|
||||
public InAppWebViewInterface webView;
|
||||
@Nullable
|
||||
public FindInteractionChannelDelegate channelDelegate;
|
||||
@Nullable
|
||||
public FindInteractionSettings settings;
|
||||
|
||||
public FindInteractionController(@NonNull InAppWebViewInterface webView, @NonNull InAppWebViewFlutterPlugin plugin,
|
||||
@NonNull Object id, @Nullable FindInteractionSettings settings) {
|
||||
this.webView = webView;
|
||||
this.settings = settings;
|
||||
final MethodChannel channel = new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id);
|
||||
this.channelDelegate = new FindInteractionChannelDelegate(this, channel);
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (channelDelegate != null) {
|
||||
channelDelegate.dispose();
|
||||
channelDelegate = null;
|
||||
}
|
||||
webView = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.find_interaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FindInteractionSettings implements ISettings<FindInteractionController> {
|
||||
public static final String LOG_TAG = "FindInteractionSettings";
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FindInteractionSettings parse(@NonNull Map<String, Object> settings) {
|
||||
// for (Map.Entry<String, Object> pair : settings.entrySet()) {
|
||||
// String key = pair.getKey();
|
||||
// Object value = pair.getValue();
|
||||
// if (value == null) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// switch (key) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
return settings;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Map<String, Object> getRealSettings(@NonNull FindInteractionController findInteractionController) {
|
||||
Map<String, Object> realSettings = toMap();
|
||||
return realSettings;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
|
@ -109,6 +110,10 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
|||
webView.inAppBrowserDelegate = this;
|
||||
webView.plugin = manager.plugin;
|
||||
|
||||
FindInteractionController findInteractionController = new FindInteractionController(webView, manager.plugin, id, null);
|
||||
webView.findInteractionController = findInteractionController;
|
||||
findInteractionController.prepare();
|
||||
|
||||
final MethodChannel channel = new MethodChannel(manager.plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id);
|
||||
channelDelegate = new InAppBrowserChannelDelegate(channel);
|
||||
webView.channelDelegate = new WebViewChannelDelegate(webView, channel);
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.webkit.WebViewCompat;
|
|||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionChannelDelegate;
|
||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserActivity;
|
||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserSettings;
|
||||
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
|
||||
|
@ -73,24 +74,31 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
|
||||
switch (call.method) {
|
||||
case "getUrl":
|
||||
WebViewChannelDelegateMethods method = null;
|
||||
try {
|
||||
method = WebViewChannelDelegateMethods.valueOf(call.method);
|
||||
} catch (IllegalArgumentException e) {
|
||||
result.notImplemented();
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case getUrl:
|
||||
result.success((webView != null) ? webView.getUrl() : null);
|
||||
break;
|
||||
case "getTitle":
|
||||
case getTitle:
|
||||
result.success((webView != null) ? webView.getTitle() : null);
|
||||
break;
|
||||
case "getProgress":
|
||||
case getProgress:
|
||||
result.success((webView != null) ? webView.getProgress() : null);
|
||||
break;
|
||||
case "loadUrl":
|
||||
case loadUrl:
|
||||
if (webView != null) {
|
||||
Map<String, Object> urlRequest = (Map<String, Object>) call.argument("urlRequest");
|
||||
webView.loadUrl(URLRequest.fromMap(urlRequest));
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "postUrl":
|
||||
case postUrl:
|
||||
if (webView != null) {
|
||||
String url = (String) call.argument("url");
|
||||
byte[] postData = (byte[]) call.argument("postData");
|
||||
|
@ -98,7 +106,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "loadData":
|
||||
case loadData:
|
||||
if (webView != null) {
|
||||
String data = (String) call.argument("data");
|
||||
String mimeType = (String) call.argument("mimeType");
|
||||
|
@ -109,7 +117,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "loadFile":
|
||||
case loadFile:
|
||||
if (webView != null) {
|
||||
String assetFilePath = (String) call.argument("assetFilePath");
|
||||
try {
|
||||
|
@ -122,7 +130,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "evaluateJavascript":
|
||||
case evaluateJavascript:
|
||||
if (webView != null) {
|
||||
String source = (String) call.argument("source");
|
||||
Map<String, Object> contentWorldMap = (Map<String, Object>) call.argument("contentWorld");
|
||||
|
@ -138,7 +146,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "injectJavascriptFileFromUrl":
|
||||
case injectJavascriptFileFromUrl:
|
||||
if (webView != null) {
|
||||
String urlFile = (String) call.argument("urlFile");
|
||||
Map<String, Object> scriptHtmlTagAttributes = (Map<String, Object>) call.argument("scriptHtmlTagAttributes");
|
||||
|
@ -146,14 +154,14 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "injectCSSCode":
|
||||
case injectCSSCode:
|
||||
if (webView != null) {
|
||||
String source = (String) call.argument("source");
|
||||
webView.injectCSSCode(source);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "injectCSSFileFromUrl":
|
||||
case injectCSSFileFromUrl:
|
||||
if (webView != null) {
|
||||
String urlFile = (String) call.argument("urlFile");
|
||||
Map<String, Object> cssLinkHtmlTagAttributes = (Map<String, Object>) call.argument("cssLinkHtmlTagAttributes");
|
||||
|
@ -161,44 +169,44 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "reload":
|
||||
case reload:
|
||||
if (webView != null)
|
||||
webView.reload();
|
||||
result.success(true);
|
||||
break;
|
||||
case "goBack":
|
||||
case goBack:
|
||||
if (webView != null)
|
||||
webView.goBack();
|
||||
result.success(true);
|
||||
break;
|
||||
case "canGoBack":
|
||||
case canGoBack:
|
||||
result.success((webView != null) && webView.canGoBack());
|
||||
break;
|
||||
case "goForward":
|
||||
case goForward:
|
||||
if (webView != null)
|
||||
webView.goForward();
|
||||
result.success(true);
|
||||
break;
|
||||
case "canGoForward":
|
||||
case canGoForward:
|
||||
result.success((webView != null) && webView.canGoForward());
|
||||
break;
|
||||
case "goBackOrForward":
|
||||
case goBackOrForward:
|
||||
if (webView != null)
|
||||
webView.goBackOrForward((Integer) call.argument("steps"));
|
||||
result.success(true);
|
||||
break;
|
||||
case "canGoBackOrForward":
|
||||
case canGoBackOrForward:
|
||||
result.success((webView != null) && webView.canGoBackOrForward((Integer) call.argument("steps")));
|
||||
break;
|
||||
case "stopLoading":
|
||||
case stopLoading:
|
||||
if (webView != null)
|
||||
webView.stopLoading();
|
||||
result.success(true);
|
||||
break;
|
||||
case "isLoading":
|
||||
case isLoading:
|
||||
result.success((webView != null) && webView.isLoading());
|
||||
break;
|
||||
case "takeScreenshot":
|
||||
case takeScreenshot:
|
||||
if (webView != null) {
|
||||
Map<String, Object> screenshotConfiguration = (Map<String, Object>) call.argument("screenshotConfiguration");
|
||||
webView.takeScreenshot(screenshotConfiguration, result);
|
||||
|
@ -206,7 +214,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
else
|
||||
result.success(null);
|
||||
break;
|
||||
case "setSettings":
|
||||
case setSettings:
|
||||
if (webView != null && webView.getInAppBrowserDelegate() instanceof InAppBrowserActivity) {
|
||||
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.getInAppBrowserDelegate();
|
||||
InAppBrowserSettings inAppBrowserSettings = new InAppBrowserSettings();
|
||||
|
@ -221,7 +229,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "getSettings":
|
||||
case getSettings:
|
||||
if (webView != null && webView.getInAppBrowserDelegate() instanceof InAppBrowserActivity) {
|
||||
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.getInAppBrowserDelegate();
|
||||
result.success(inAppBrowserActivity.getCustomSettings());
|
||||
|
@ -229,7 +237,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success((webView != null) ? webView.getCustomSettings() : null);
|
||||
}
|
||||
break;
|
||||
case "close":
|
||||
case close:
|
||||
if (webView != null && webView.getInAppBrowserDelegate() instanceof InAppBrowserActivity) {
|
||||
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.getInAppBrowserDelegate();
|
||||
inAppBrowserActivity.close(result);
|
||||
|
@ -237,7 +245,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.notImplemented();
|
||||
}
|
||||
break;
|
||||
case "show":
|
||||
case show:
|
||||
if (webView != null && webView.getInAppBrowserDelegate() instanceof InAppBrowserActivity) {
|
||||
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.getInAppBrowserDelegate();
|
||||
inAppBrowserActivity.show();
|
||||
|
@ -246,7 +254,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.notImplemented();
|
||||
}
|
||||
break;
|
||||
case "hide":
|
||||
case hide:
|
||||
if (webView != null && webView.getInAppBrowserDelegate() instanceof InAppBrowserActivity) {
|
||||
InAppBrowserActivity inAppBrowserActivity = (InAppBrowserActivity) webView.getInAppBrowserDelegate();
|
||||
inAppBrowserActivity.hide();
|
||||
|
@ -255,10 +263,10 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.notImplemented();
|
||||
}
|
||||
break;
|
||||
case "getCopyBackForwardList":
|
||||
case getCopyBackForwardList:
|
||||
result.success((webView != null) ? webView.getCopyBackForwardList() : null);
|
||||
break;
|
||||
case "startSafeBrowsing":
|
||||
case startSafeBrowsing:
|
||||
if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
|
||||
WebViewCompat.startSafeBrowsing(webView.getContext(), new ValueCallback<Boolean>() {
|
||||
@Override
|
||||
|
@ -271,37 +279,37 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "clearCache":
|
||||
case clearCache:
|
||||
if (webView != null)
|
||||
webView.clearAllCache();
|
||||
result.success(true);
|
||||
break;
|
||||
case "clearSslPreferences":
|
||||
case clearSslPreferences:
|
||||
if (webView != null)
|
||||
webView.clearSslPreferences();
|
||||
result.success(true);
|
||||
break;
|
||||
case "findAllAsync":
|
||||
case findAllAsync:
|
||||
if (webView != null) {
|
||||
String find = (String) call.argument("find");
|
||||
webView.findAllAsync(find);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "findNext":
|
||||
case findNext:
|
||||
if (webView != null) {
|
||||
Boolean forward = (Boolean) call.argument("forward");
|
||||
webView.findNext(forward);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "clearMatches":
|
||||
case clearMatches:
|
||||
if (webView != null) {
|
||||
webView.clearMatches();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "scrollTo":
|
||||
case scrollTo:
|
||||
if (webView != null) {
|
||||
Integer x = (Integer) call.argument("x");
|
||||
Integer y = (Integer) call.argument("y");
|
||||
|
@ -310,7 +318,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "scrollBy":
|
||||
case scrollBy:
|
||||
if (webView != null) {
|
||||
Integer x = (Integer) call.argument("x");
|
||||
Integer y = (Integer) call.argument("y");
|
||||
|
@ -319,31 +327,31 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "pause":
|
||||
case pause:
|
||||
if (webView != null) {
|
||||
webView.onPause();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "resume":
|
||||
case resume:
|
||||
if (webView != null) {
|
||||
webView.onResume();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "pauseTimers":
|
||||
case pauseTimers:
|
||||
if (webView != null) {
|
||||
webView.pauseTimers();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "resumeTimers":
|
||||
case resumeTimers:
|
||||
if (webView != null) {
|
||||
webView.resumeTimers();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "printCurrentPage":
|
||||
case printCurrentPage:
|
||||
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
PrintJobSettings settings = new PrintJobSettings();
|
||||
Map<String, Object> settingsMap = (Map<String, Object>) call.argument("settings");
|
||||
|
@ -355,31 +363,31 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getContentHeight":
|
||||
case getContentHeight:
|
||||
if (webView instanceof InAppWebView) {
|
||||
result.success(webView.getContentHeight());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "zoomBy":
|
||||
case zoomBy:
|
||||
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
double zoomFactor = (double) call.argument("zoomFactor");
|
||||
webView.zoomBy((float) zoomFactor);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "getOriginalUrl":
|
||||
case getOriginalUrl:
|
||||
result.success((webView != null) ? webView.getOriginalUrl() : null);
|
||||
break;
|
||||
case "getZoomScale":
|
||||
case getZoomScale:
|
||||
if (webView instanceof InAppWebView) {
|
||||
result.success(webView.getZoomScale());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getSelectedText":
|
||||
case getSelectedText:
|
||||
if ((webView instanceof InAppWebView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)) {
|
||||
webView.getSelectedText(new ValueCallback<String>() {
|
||||
@Override
|
||||
|
@ -391,14 +399,14 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getHitTestResult":
|
||||
case getHitTestResult:
|
||||
if (webView instanceof InAppWebView) {
|
||||
result.success(HitTestResult.fromWebViewHitTestResult(webView.getHitTestResult()).toMap());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "pageDown":
|
||||
case pageDown:
|
||||
if (webView != null) {
|
||||
boolean bottom = (boolean) call.argument("bottom");
|
||||
result.success(webView.pageDown(bottom));
|
||||
|
@ -406,7 +414,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "pageUp":
|
||||
case pageUp:
|
||||
if (webView != null) {
|
||||
boolean top = (boolean) call.argument("top");
|
||||
result.success(webView.pageUp(top));
|
||||
|
@ -414,7 +422,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "saveWebArchive":
|
||||
case saveWebArchive:
|
||||
if (webView != null) {
|
||||
String filePath = (String) call.argument("filePath");
|
||||
boolean autoname = (boolean) call.argument("autoname");
|
||||
|
@ -428,75 +436,75 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "zoomIn":
|
||||
case zoomIn:
|
||||
if (webView != null) {
|
||||
result.success(webView.zoomIn());
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "zoomOut":
|
||||
case zoomOut:
|
||||
if (webView != null) {
|
||||
result.success(webView.zoomOut());
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "clearFocus":
|
||||
case clearFocus:
|
||||
if (webView != null) {
|
||||
webView.clearFocus();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "setContextMenu":
|
||||
case setContextMenu:
|
||||
if (webView != null) {
|
||||
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
|
||||
webView.setContextMenu(contextMenu);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "requestFocusNodeHref":
|
||||
case requestFocusNodeHref:
|
||||
if (webView != null) {
|
||||
result.success(webView.requestFocusNodeHref());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "requestImageRef":
|
||||
case requestImageRef:
|
||||
if (webView != null) {
|
||||
result.success(webView.requestImageRef());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getScrollX":
|
||||
case getScrollX:
|
||||
if (webView != null) {
|
||||
result.success(webView.getScrollX());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getScrollY":
|
||||
case getScrollY:
|
||||
if (webView != null) {
|
||||
result.success(webView.getScrollY());
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "getCertificate":
|
||||
case getCertificate:
|
||||
if (webView != null) {
|
||||
result.success(SslCertificateExt.toMap(webView.getCertificate()));
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "clearHistory":
|
||||
case clearHistory:
|
||||
if (webView != null) {
|
||||
webView.clearHistory();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "addUserScript":
|
||||
case addUserScript:
|
||||
if (webView != null && webView.getUserContentController() != null) {
|
||||
Map<String, Object> userScriptMap = (Map<String, Object>) call.argument("userScript");
|
||||
UserScript userScript = UserScript.fromMap(userScriptMap);
|
||||
|
@ -505,7 +513,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "removeUserScript":
|
||||
case removeUserScript:
|
||||
if (webView != null && webView.getUserContentController() != null) {
|
||||
Integer index = (Integer) call.argument("index");
|
||||
Map<String, Object> userScriptMap = (Map<String, Object>) call.argument("userScript");
|
||||
|
@ -515,20 +523,20 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "removeUserScriptsByGroupName":
|
||||
case removeUserScriptsByGroupName:
|
||||
if (webView != null && webView.getUserContentController() != null) {
|
||||
String groupName = (String) call.argument("groupName");
|
||||
webView.getUserContentController().removeUserOnlyScriptsByGroupName(groupName);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "removeAllUserScripts":
|
||||
case removeAllUserScripts:
|
||||
if (webView != null && webView.getUserContentController() != null) {
|
||||
webView.getUserContentController().removeAllUserOnlyScripts();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "callAsyncJavaScript":
|
||||
case callAsyncJavaScript:
|
||||
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
String functionBody = (String) call.argument("functionBody");
|
||||
Map<String, Object> functionArguments = (Map<String, Object>) call.argument("arguments");
|
||||
|
@ -545,7 +553,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "isSecureContext":
|
||||
case isSecureContext:
|
||||
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
webView.isSecureContext(new ValueCallback<Boolean>() {
|
||||
@Override
|
||||
|
@ -557,7 +565,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "createWebMessageChannel":
|
||||
case createWebMessageChannel:
|
||||
if (webView != null) {
|
||||
if (webView instanceof InAppWebView && WebViewFeature.isFeatureSupported(WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL)) {
|
||||
result.success(webView.createCompatWebMessageChannel().toMap());
|
||||
|
@ -568,7 +576,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(null);
|
||||
}
|
||||
break;
|
||||
case "postWebMessage":
|
||||
case postWebMessage:
|
||||
if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) {
|
||||
Map<String, Object> message = (Map<String, Object>) call.argument("message");
|
||||
String targetOrigin = (String) call.argument("targetOrigin");
|
||||
|
@ -600,7 +608,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(true);
|
||||
}
|
||||
break;
|
||||
case "addWebMessageListener":
|
||||
case addWebMessageListener:
|
||||
if (webView != null) {
|
||||
Map<String, Object> webMessageListenerMap = (Map<String, Object>) call.argument("webMessageListener");
|
||||
WebMessageListener webMessageListener = WebMessageListener.fromMap(webView, webView.getPlugin().messenger, webMessageListenerMap);
|
||||
|
@ -618,32 +626,35 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(true);
|
||||
}
|
||||
break;
|
||||
case "canScrollVertically":
|
||||
case canScrollVertically:
|
||||
if (webView != null) {
|
||||
result.success(webView.canScrollVertically());
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "canScrollHorizontally":
|
||||
case canScrollHorizontally:
|
||||
if (webView != null) {
|
||||
result.success(webView.canScrollHorizontally());
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "isInFullscreen":
|
||||
case isInFullscreen:
|
||||
if (webView != null) {
|
||||
result.success(webView.isInFullscreen());
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link FindInteractionChannelDelegate#onFindResultReceived} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.webview;
|
||||
|
||||
public enum WebViewChannelDelegateMethods {
|
||||
getUrl,
|
||||
getTitle,
|
||||
getProgress,
|
||||
loadUrl,
|
||||
postUrl,
|
||||
loadData,
|
||||
loadFile,
|
||||
evaluateJavascript,
|
||||
injectJavascriptFileFromUrl,
|
||||
injectCSSCode,
|
||||
injectCSSFileFromUrl,
|
||||
reload,
|
||||
goBack,
|
||||
canGoBack,
|
||||
goForward,
|
||||
canGoForward,
|
||||
goBackOrForward,
|
||||
canGoBackOrForward,
|
||||
stopLoading,
|
||||
isLoading,
|
||||
takeScreenshot,
|
||||
setSettings,
|
||||
getSettings,
|
||||
close,
|
||||
show,
|
||||
hide,
|
||||
getCopyBackForwardList,
|
||||
startSafeBrowsing,
|
||||
clearCache,
|
||||
clearSslPreferences,
|
||||
findAllAsync,
|
||||
findNext,
|
||||
clearMatches,
|
||||
scrollTo,
|
||||
scrollBy,
|
||||
pause,
|
||||
resume,
|
||||
pauseTimers,
|
||||
resumeTimers,
|
||||
printCurrentPage,
|
||||
getContentHeight,
|
||||
zoomBy,
|
||||
getOriginalUrl,
|
||||
getZoomScale,
|
||||
getSelectedText,
|
||||
getHitTestResult,
|
||||
pageDown,
|
||||
pageUp,
|
||||
saveWebArchive,
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
clearFocus,
|
||||
setContextMenu,
|
||||
requestFocusNodeHref,
|
||||
requestImageRef,
|
||||
getScrollX,
|
||||
getScrollY,
|
||||
getCertificate,
|
||||
clearHistory,
|
||||
addUserScript,
|
||||
removeUserScript,
|
||||
removeUserScriptsByGroupName,
|
||||
removeAllUserScripts,
|
||||
callAsyncJavaScript,
|
||||
isSecureContext,
|
||||
createWebMessageChannel,
|
||||
postWebMessage,
|
||||
addWebMessageListener,
|
||||
canScrollVertically,
|
||||
canScrollHorizontally,
|
||||
isInFullscreen
|
||||
}
|
|
@ -19,6 +19,7 @@ import androidx.webkit.WebViewCompat;
|
|||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController;
|
||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshSettings;
|
||||
|
@ -84,6 +85,10 @@ public class FlutterWebView implements PlatformWebView {
|
|||
pullToRefreshLayout.prepare();
|
||||
}
|
||||
|
||||
FindInteractionController findInteractionController = new FindInteractionController(webView, plugin, id, null);
|
||||
webView.findInteractionController = findInteractionController;
|
||||
findInteractionController.prepare();
|
||||
|
||||
webView.prepare();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ import androidx.webkit.WebViewCompat;
|
|||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController;
|
||||
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController;
|
||||
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings;
|
||||
|
@ -167,6 +168,9 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
|||
|
||||
private List<UserScript> initialUserOnlyScript = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
public FindInteractionController findInteractionController;
|
||||
|
||||
public InAppWebView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
@ -406,7 +410,10 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
|||
setFindListener(new FindListener() {
|
||||
@Override
|
||||
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
|
||||
if (channelDelegate != null) channelDelegate.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
if (findInteractionController != null && findInteractionController.channelDelegate != null)
|
||||
findInteractionController.channelDelegate.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
if (channelDelegate != null)
|
||||
channelDelegate.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1929,6 +1936,10 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
|||
@Override
|
||||
public void dispose() {
|
||||
userContentController.dispose();
|
||||
if (findInteractionController != null) {
|
||||
findInteractionController.dispose();
|
||||
findInteractionController = null;
|
||||
}
|
||||
if (windowId != null) {
|
||||
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void findInteractions() {
|
||||
final shouldSkip = kIsWeb
|
||||
? true
|
||||
: ![
|
||||
TargetPlatform.android,
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.macOS,
|
||||
].contains(defaultTargetPlatform);
|
||||
|
||||
testWidgets('find interactions', (WidgetTester tester) async {
|
||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
final findInteractionController = FindInteractionController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialFile: "test_assets/in_app_webview_initial_file_test.html",
|
||||
findInteractionController: findInteractionController,
|
||||
initialSettings: InAppWebViewSettings(
|
||||
clearCache: true,
|
||||
isFindInteractionEnabled: true
|
||||
),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onLoadStop: (controller, url) {
|
||||
pageLoaded.complete();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await pageLoaded.future;
|
||||
|
||||
await tester.pump();
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
const firstSearchText = "InAppWebViewInitialFileTest";
|
||||
await expectLater(findInteractionController.findAllAsync(find: firstSearchText), completes);
|
||||
if ([TargetPlatform.iOS, TargetPlatform.macOS].contains(defaultTargetPlatform)) {
|
||||
expect(await findInteractionController.getSearchText(), firstSearchText);
|
||||
final session = await findInteractionController.getActiveFindSession();
|
||||
expect(session!.resultCount, 2);
|
||||
}
|
||||
await expectLater(findInteractionController.findNext(forward: true), completes);
|
||||
await expectLater(findInteractionController.findNext(forward: false), completes);
|
||||
await expectLater(findInteractionController.clearMatches(), completes);
|
||||
|
||||
if ([TargetPlatform.iOS, TargetPlatform.macOS].contains(defaultTargetPlatform)) {
|
||||
const secondSearchText = "text";
|
||||
await expectLater(
|
||||
findInteractionController.setSearchText(secondSearchText), completes);
|
||||
await expectLater(
|
||||
findInteractionController.presentFindNavigator(), completes);
|
||||
expect(await findInteractionController.getSearchText(), secondSearchText);
|
||||
expect(await findInteractionController.isFindNavigatorVisible(), true);
|
||||
await expectLater(findInteractionController.updateResultCount(), completes);
|
||||
await expectLater(
|
||||
findInteractionController.dismissFindNavigator(), completes);
|
||||
expect(await findInteractionController.isFindNavigatorVisible(), false);
|
||||
}
|
||||
}, skip: shouldSkip);
|
||||
|
||||
testWidgets('onFindResultReceived', (WidgetTester tester) async {
|
||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
final Completer<int> numberOfMatchesCompleter = Completer<int>();
|
||||
final findInteractionController = FindInteractionController(
|
||||
onFindResultReceived: (controller, int activeMatchOrdinal,
|
||||
int numberOfMatches, bool isDoneCounting) async {
|
||||
if (isDoneCounting && !numberOfMatchesCompleter.isCompleted) {
|
||||
numberOfMatchesCompleter.complete(numberOfMatches);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialFile: "test_assets/in_app_webview_initial_file_test.html",
|
||||
initialSettings: InAppWebViewSettings(
|
||||
clearCache: true,
|
||||
isFindInteractionEnabled: false
|
||||
),
|
||||
findInteractionController: findInteractionController,
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onLoadStop: (controller, url) {
|
||||
pageLoaded.complete();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
var controller = await controllerCompleter.future;
|
||||
await pageLoaded.future;
|
||||
|
||||
await tester.pump();
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
await controller.findAllAsync(find: "InAppWebViewInitialFileTest");
|
||||
final int numberOfMatches = await numberOfMatchesCompleter.future;
|
||||
expect(numberOfMatches, 2);
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'find_interactions.dart';
|
||||
|
||||
void main() {
|
||||
final shouldSkip = kIsWeb;
|
||||
|
||||
group('FindInteractionController', () {
|
||||
findInteractions();
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -37,7 +37,6 @@ import 'load_file_url.dart';
|
|||
import 'load_url.dart';
|
||||
import 'on_console_message.dart';
|
||||
import 'on_download_start_request.dart';
|
||||
import 'on_find_result_received.dart';
|
||||
import 'on_js_before_unload.dart';
|
||||
import 'on_received_error.dart';
|
||||
import 'on_received_http_error.dart';
|
||||
|
@ -106,7 +105,6 @@ void main() {
|
|||
contentBlocker();
|
||||
httpAuthCredentialDatabase();
|
||||
onConsoleMessage();
|
||||
onFindResultReceived();
|
||||
onDownloadStartRequest();
|
||||
javascriptDialogs();
|
||||
onReceivedHttpError();
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void onFindResultReceived() {
|
||||
final shouldSkip = kIsWeb
|
||||
? true
|
||||
: ![
|
||||
TargetPlatform.android,
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.macOS,
|
||||
].contains(defaultTargetPlatform);
|
||||
|
||||
testWidgets('onFindResultReceived', (WidgetTester tester) async {
|
||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
final Completer<int> numberOfMatchesCompleter = Completer<int>();
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialFile: "test_assets/in_app_webview_initial_file_test.html",
|
||||
initialSettings: InAppWebViewSettings(
|
||||
clearCache: true,
|
||||
),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onLoadStop: (controller, url) {
|
||||
pageLoaded.complete();
|
||||
},
|
||||
onFindResultReceived: (controller, int activeMatchOrdinal,
|
||||
int numberOfMatches, bool isDoneCounting) async {
|
||||
if (isDoneCounting && !numberOfMatchesCompleter.isCompleted) {
|
||||
numberOfMatchesCompleter.complete(numberOfMatches);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
var controller = await controllerCompleter.future;
|
||||
await pageLoaded.future;
|
||||
|
||||
await tester.pump();
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
await controller.findAllAsync(find: "InAppWebViewInitialFileTest");
|
||||
final int numberOfMatches = await numberOfMatchesCompleter.future;
|
||||
expect(numberOfMatches, 2);
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
|||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'in_app_webview/main.dart' as in_app_webview_tests;
|
||||
import 'find_interaction_controller/main.dart' as find_interaction_controller_tests;
|
||||
import 'service_worker_controller/main.dart' as service_worker_controller_tests;
|
||||
import 'proxy_controller/main.dart' as proxy_controller_tests;
|
||||
import 'headless_in_app_webview/main.dart' as headless_in_app_webview_tests;
|
||||
|
@ -26,8 +27,13 @@ void main() {
|
|||
ChromeSafariBrowser.debugLoggingSettings.maxLogMessageLength = 7000;
|
||||
WebAuthenticationSession.debugLoggingSettings.usePrint = true;
|
||||
WebAuthenticationSession.debugLoggingSettings.maxLogMessageLength = 7000;
|
||||
PullToRefreshController.debugLoggingSettings.usePrint = true;
|
||||
PullToRefreshController.debugLoggingSettings.maxLogMessageLength = 7000;
|
||||
FindInteractionController.debugLoggingSettings.usePrint = true;
|
||||
FindInteractionController.debugLoggingSettings.maxLogMessageLength = 7000;
|
||||
|
||||
in_app_webview_tests.main();
|
||||
find_interaction_controller_tests.main();
|
||||
service_worker_controller_tests.main();
|
||||
proxy_controller_tests.main();
|
||||
headless_in_app_webview_tests.main();
|
||||
|
|
|
@ -19,12 +19,15 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
InAppWebViewSettings settings = InAppWebViewSettings(
|
||||
useShouldOverrideUrlLoading: true,
|
||||
mediaPlaybackRequiresUserGesture: false,
|
||||
isFindInteractionEnabled: false,
|
||||
allowsInlineMediaPlayback: true,
|
||||
iframeAllow: "camera; microphone",
|
||||
iframeAllowFullscreen: true
|
||||
);
|
||||
|
||||
PullToRefreshController? pullToRefreshController;
|
||||
FindInteractionController? findInteractionController;
|
||||
|
||||
late ContextMenu contextMenu;
|
||||
String url = "";
|
||||
double progress = 0;
|
||||
|
@ -79,6 +82,14 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
}
|
||||
},
|
||||
);
|
||||
|
||||
findInteractionController = kIsWeb
|
||||
? null
|
||||
: FindInteractionController(
|
||||
onFindResultReceived: (controller, activeMatchOrdinal, numberOfMatches, isDoneCounting) => {
|
||||
print("$activeMatchOrdinal $numberOfMatches $isDoneCounting")
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -114,7 +125,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
InAppWebView(
|
||||
key: webViewKey,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter/')),
|
||||
URLRequest(url: Uri.parse('https://developer.apple.com/videos/play/wwdc2022/10049/?time=264')),
|
||||
// initialUrlRequest:
|
||||
// URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')),
|
||||
// initialFile: "assets/index.html",
|
||||
|
@ -122,6 +133,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
initialSettings: settings,
|
||||
// contextMenu: contextMenu,
|
||||
pullToRefreshController: pullToRefreshController,
|
||||
findInteractionController: findInteractionController,
|
||||
onWebViewCreated: (controller) async {
|
||||
webViewController = controller;
|
||||
},
|
||||
|
@ -167,6 +179,32 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
await findInteractionController?.findAllAsync(find: "video");
|
||||
// print(await findInteractionController?.getActiveFindSession());
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
findInteractionController?.findNext(forward: true);
|
||||
findInteractionController?.findNext(forward: true);
|
||||
findInteractionController?.findNext(forward: true);
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
// findInteractionController?.clearMatches();
|
||||
findInteractionController?.findNext(forward: true);
|
||||
findInteractionController?.findNext(forward: true);
|
||||
findInteractionController?.findNext(forward: true);
|
||||
findInteractionController?.findNext(forward: true);
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
findInteractionController?.clearMatches();
|
||||
// print(await findInteractionController?.getSearchText());
|
||||
// findInteractionController?.findNext(forward: true);
|
||||
// findInteractionController?.findNext(forward: false);
|
||||
// findInteractionController?.setSearchText("text");
|
||||
// print(await findInteractionController?.getSearchText());
|
||||
// print(await findInteractionController?.isFindNavigatorVisible());
|
||||
// findInteractionController?.updateResultCount();
|
||||
// findInteractionController?.clearMatches();
|
||||
// findInteractionController?.presentFindNavigator();
|
||||
// await Future.delayed(Duration(milliseconds: 500));
|
||||
// findInteractionController?.dismissFindNavigator();
|
||||
// print(await findInteractionController?.isFindNavigatorVisible());
|
||||
},
|
||||
onReceivedError: (controller, request, error) {
|
||||
pullToRefreshController?.endRefreshing();
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
//
|
||||
// FindInteractionChannelDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FindInteractionChannelDelegate : ChannelDelegate {
|
||||
private weak var findInteractionController: FindInteractionController?
|
||||
|
||||
public init(findInteractionController: FindInteractionController, channel: FlutterMethodChannel) {
|
||||
super.init(channel: channel)
|
||||
self.findInteractionController = findInteractionController
|
||||
}
|
||||
|
||||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "findAllAsync":
|
||||
if let findInteractionController = findInteractionController {
|
||||
let find = arguments!["find"] as! String
|
||||
findInteractionController.findAllAsync(find: find, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "findNext":
|
||||
if let findInteractionController = findInteractionController {
|
||||
let forward = arguments!["forward"] as! Bool
|
||||
findInteractionController.findNext(forward: forward, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "clearMatches":
|
||||
if let findInteractionController = findInteractionController {
|
||||
findInteractionController.clearMatches(completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "FindInteractionChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
})
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "setSearchText":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
let searchText = arguments!["searchText"] as? String
|
||||
interaction.searchText = searchText
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getSearchText":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
result(interaction.searchText)
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "isFindNavigatorVisible":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
result(interaction.isFindNavigatorVisible)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "updateResultCount":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
interaction.updateResultCount()
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "presentFindNavigator":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
interaction.presentFindNavigator(showingReplace: false)
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "dismissFindNavigator":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
interaction.dismissFindNavigator()
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getActiveFindSession":
|
||||
if #available(iOS 16.0, *) {
|
||||
if let interaction = findInteractionController?.webView?.findInteraction {
|
||||
result(interaction.activeFindSession?.toMap())
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Bool) {
|
||||
let arguments: [String : Any?] = [
|
||||
"activeMatchOrdinal": activeMatchOrdinal,
|
||||
"numberOfMatches": numberOfMatches,
|
||||
"isDoneCounting": isDoneCounting
|
||||
]
|
||||
channel?.invokeMethod("onFindResultReceived", arguments: arguments)
|
||||
}
|
||||
|
||||
public override func dispose() {
|
||||
super.dispose()
|
||||
findInteractionController = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// FindInteractionController.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
|
||||
public class FindInteractionController : NSObject, Disposable {
|
||||
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_find_interaction_";
|
||||
var webView: InAppWebView?
|
||||
var channelDelegate: FindInteractionChannelDelegate?
|
||||
var settings: FindInteractionSettings?
|
||||
var shouldCallOnRefresh = false
|
||||
|
||||
public init(registrar: FlutterPluginRegistrar, id: Any, webView: InAppWebView, settings: FindInteractionSettings?) {
|
||||
super.init()
|
||||
self.webView = webView
|
||||
self.settings = settings
|
||||
let channel = FlutterMethodChannel(name: FindInteractionController.METHOD_CHANNEL_NAME_PREFIX + String(describing: id),
|
||||
binaryMessenger: registrar.messenger())
|
||||
self.channelDelegate = FindInteractionChannelDelegate(findInteractionController: self, channel: channel)
|
||||
}
|
||||
|
||||
public func prepare() {
|
||||
// if let settings = settings {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
public func findAllAsync(find: String?, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if #available(iOS 16.0, *), webView.isFindInteractionEnabled {
|
||||
if let interaction = webView.findInteraction {
|
||||
interaction.searchText = find
|
||||
interaction.presentFindNavigator(showingReplace: false)
|
||||
}
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
} else {
|
||||
let startSearch = "window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsync('\(find ?? "")');"
|
||||
webView.evaluateJavaScript(startSearch, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public func findNext(forward: Bool, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if #available(iOS 16.0, *), webView.isFindInteractionEnabled {
|
||||
if let interaction = webView.findInteraction {
|
||||
if forward {
|
||||
interaction.findNext()
|
||||
} else {
|
||||
interaction.findPrevious()
|
||||
}
|
||||
}
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
} else {
|
||||
webView.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findNext(\(forward ? "true" : "false"));", completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public func clearMatches(completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
guard let webView else {
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
if #available(iOS 16.0, *), webView.isFindInteractionEnabled {
|
||||
if let interaction = webView.findInteraction {
|
||||
interaction.searchText = nil
|
||||
interaction.dismissFindNavigator()
|
||||
}
|
||||
if let completionHandler = completionHandler {
|
||||
completionHandler(nil, nil)
|
||||
}
|
||||
} else {
|
||||
webView.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches();", completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channelDelegate?.dispose()
|
||||
channelDelegate = nil
|
||||
webView = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("FindInteractionControl - dealloc")
|
||||
dispose()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// FindInteractionSettings.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FindInteractionSettings : ISettings<FindInteractionController> {
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(settings: [String: Any?]) -> FindInteractionSettings {
|
||||
let _ = super.parse(settings: settings)
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealSettings(obj: FindInteractionController?) -> [String: Any?] {
|
||||
let realSettings: [String: Any?] = toMap()
|
||||
return realSettings
|
||||
}
|
||||
}
|
|
@ -77,6 +77,12 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
|||
pullToRefreshControl.delegate = webView
|
||||
pullToRefreshControl.prepare()
|
||||
|
||||
let findInteractionController = FindInteractionController(
|
||||
registrar: SwiftFlutterPlugin.instance!.registrar!,
|
||||
id: id, webView: webView, settings: nil)
|
||||
webView.findInteractionController = findInteractionController
|
||||
findInteractionController.prepare()
|
||||
|
||||
prepareWebView()
|
||||
webView.windowCreated = true
|
||||
|
||||
|
|
|
@ -61,6 +61,12 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView, Disposable
|
|||
webView!.pullToRefreshControl = pullToRefreshControl
|
||||
pullToRefreshControl.delegate = webView!
|
||||
pullToRefreshControl.prepare()
|
||||
|
||||
let findInteractionController = FindInteractionController(
|
||||
registrar: SwiftFlutterPlugin.instance!.registrar!,
|
||||
id: viewId, webView: webView!, settings: nil)
|
||||
webView!.findInteractionController = findInteractionController
|
||||
findInteractionController.prepare()
|
||||
|
||||
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
myView!.autoresizesSubviews = true
|
||||
|
|
|
@ -12,7 +12,8 @@ import WebKit
|
|||
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
||||
WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate,
|
||||
WKDownloadDelegate,
|
||||
PullToRefreshDelegate, Disposable {
|
||||
PullToRefreshDelegate,
|
||||
Disposable {
|
||||
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"
|
||||
|
||||
var id: Any? // viewId
|
||||
|
@ -22,6 +23,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
var channelDelegate: WebViewChannelDelegate?
|
||||
var settings: InAppWebViewSettings?
|
||||
var pullToRefreshControl: PullToRefreshControl?
|
||||
var findInteractionController: FindInteractionController?
|
||||
var webMessageChannels: [String:WebMessageChannel] = [:]
|
||||
var webMessageListeners: [WebMessageListener] = []
|
||||
var currentOriginalUrl: URL?
|
||||
|
@ -329,7 +331,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
name: UIMenuController.didHideMenuNotification,
|
||||
object: nil)
|
||||
|
||||
// if #available(iOS 15.0, *) {
|
||||
// TODO: Still not working on iOS 16.0!
|
||||
// if #available(iOS 16.0, *) {
|
||||
// addObserver(self,
|
||||
// forKeyPath: #keyPath(WKWebView.fullscreenState),
|
||||
// options: .new,
|
||||
|
@ -413,6 +416,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
}
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
isFindInteractionEnabled = settings.isFindInteractionEnabled
|
||||
}
|
||||
|
||||
// debugging is always enabled for iOS,
|
||||
// there isn't any option to set about it such as on Android.
|
||||
|
||||
|
@ -456,6 +463,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
|
||||
if #available(iOS 15.4, *) {
|
||||
configuration.preferences.isSiteSpecificQuirksModeEnabled = settings.isSiteSpecificQuirksModeEnabled
|
||||
configuration.preferences.isElementFullscreenEnabled = settings.isElementFullscreenEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -669,7 +677,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
}
|
||||
}
|
||||
}
|
||||
// else if keyPath == #keyPath(WKWebView.fullscreenState) {
|
||||
} else if #available(iOS 16.0, *) {
|
||||
// TODO: Still not working on iOS 16.0!
|
||||
// if keyPath == #keyPath(WKWebView.fullscreenState) {
|
||||
// if fullscreenState == .enteringFullscreen {
|
||||
// channelDelegate?.onEnterFullscreen()
|
||||
// } else if fullscreenState == .exitingFullscreen {
|
||||
|
@ -2513,6 +2523,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
}
|
||||
|
||||
@objc func onEnterFullscreen(_ notification: Notification) {
|
||||
// TODO: Still not working on iOS 16.0!
|
||||
// if #available(iOS 16.0, *) {
|
||||
// channelDelegate?.onEnterFullscreen()
|
||||
// inFullscreen = true
|
||||
// }
|
||||
// else
|
||||
if (isVideoPlayerWindow(notification.object as AnyObject?)) {
|
||||
channelDelegate?.onEnterFullscreen()
|
||||
inFullscreen = true
|
||||
|
@ -2520,6 +2536,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
|||
}
|
||||
|
||||
@objc func onExitFullscreen(_ notification: Notification) {
|
||||
// TODO: Still not working on iOS 16.0!
|
||||
// if #available(iOS 16.0, *) {
|
||||
// channelDelegate?.onExitFullscreen()
|
||||
// inFullscreen = false
|
||||
// }
|
||||
// else
|
||||
if (isVideoPlayerWindow(notification.object as AnyObject?)) {
|
||||
channelDelegate?.onExitFullscreen()
|
||||
inFullscreen = false
|
||||
|
@ -2648,6 +2670,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
if let wId = _windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
|
||||
webView = webViewTransport.webView
|
||||
}
|
||||
webView.findInteractionController?.channelDelegate?.onFindResultReceived(activeMatchOrdinal: activeMatchOrdinal, numberOfMatches: numberOfMatches, isDoneCounting: isDoneCounting)
|
||||
webView.channelDelegate?.onFindResultReceived(activeMatchOrdinal: activeMatchOrdinal, numberOfMatches: numberOfMatches, isDoneCounting: isDoneCounting)
|
||||
} else if message.name == "onCallAsyncJavaScriptResultBelowIOS14Received" {
|
||||
let body = message.body as! [String: Any?]
|
||||
|
@ -2701,19 +2724,6 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
}
|
||||
}
|
||||
|
||||
public func findAllAsync(find: String?, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
let startSearch = "window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsync('\(find ?? "")');"
|
||||
evaluateJavaScript(startSearch, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func findNext(forward: Bool, completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findNext(\(forward ? "true" : "false"));", completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func clearMatches(completionHandler: ((Any?, Error?) -> Void)?) {
|
||||
evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._clearMatches();", completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func scrollTo(x: Int, y: Int, animated: Bool) {
|
||||
scrollView.setContentOffset(CGPoint(x: x, y: y), animated: animated)
|
||||
}
|
||||
|
@ -3004,8 +3014,11 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
if #available(iOS 15.0, *) {
|
||||
removeObserver(self, forKeyPath: #keyPath(WKWebView.cameraCaptureState))
|
||||
removeObserver(self, forKeyPath: #keyPath(WKWebView.microphoneCaptureState))
|
||||
// removeObserver(self, forKeyPath: #keyPath(WKWebView.fullscreenState))
|
||||
}
|
||||
// TODO: Still not working on iOS 16.0!
|
||||
// if #available(iOS 16.0, *) {
|
||||
// removeObserver(self, forKeyPath: #keyPath(WKWebView.fullscreenState))
|
||||
// }
|
||||
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
|
||||
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
|
||||
resumeTimers()
|
||||
|
@ -3044,6 +3057,8 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
disablePullToRefresh()
|
||||
pullToRefreshControl?.dispose()
|
||||
pullToRefreshControl = nil
|
||||
findInteractionController?.dispose()
|
||||
findInteractionController = nil
|
||||
uiDelegate = nil
|
||||
navigationDelegate = nil
|
||||
scrollView.delegate = nil
|
||||
|
|
|
@ -74,6 +74,8 @@ public class InAppWebViewSettings: ISettings<InAppWebView> {
|
|||
var isTextInteractionEnabled = true
|
||||
var isSiteSpecificQuirksModeEnabled = true
|
||||
var upgradeKnownHostsToHTTPS = true
|
||||
var isElementFullscreenEnabled = true
|
||||
var isFindInteractionEnabled = false
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
|
@ -146,6 +148,10 @@ public class InAppWebViewSettings: ISettings<InAppWebView> {
|
|||
}
|
||||
if #available(iOS 15.4, *) {
|
||||
realSettings["isSiteSpecificQuirksModeEnabled"] = configuration.preferences.isSiteSpecificQuirksModeEnabled
|
||||
realSettings["isElementFullscreenEnabled"] = configuration.preferences.isElementFullscreenEnabled
|
||||
}
|
||||
if #available(iOS 16.0, *) {
|
||||
realSettings["isFindInteractionEnabled"] = webView.isFindInteractionEnabled
|
||||
}
|
||||
}
|
||||
return realSettings
|
||||
|
|
|
@ -19,17 +19,22 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "getUrl":
|
||||
guard let method = WebViewChannelDelegateMethods.init(rawValue: call.method) else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
switch method {
|
||||
case .getUrl:
|
||||
result(webView?.url?.absoluteString)
|
||||
break
|
||||
case "getTitle":
|
||||
case .getTitle:
|
||||
result(webView?.title)
|
||||
break
|
||||
case "getProgress":
|
||||
case .getProgress:
|
||||
result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil )
|
||||
break
|
||||
case "loadUrl":
|
||||
case .loadUrl:
|
||||
let urlRequest = arguments!["urlRequest"] as! [String:Any?]
|
||||
let allowingReadAccessTo = arguments!["allowingReadAccessTo"] as? String
|
||||
var allowingReadAccessToURL: URL? = nil
|
||||
|
@ -39,7 +44,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
webView?.loadUrl(urlRequest: URLRequest.init(fromPluginMap: urlRequest), allowingReadAccessTo: allowingReadAccessToURL)
|
||||
result(true)
|
||||
break
|
||||
case "postUrl":
|
||||
case .postUrl:
|
||||
if let webView = webView {
|
||||
let url = arguments!["url"] as! String
|
||||
let postData = arguments!["postData"] as! FlutterStandardTypedData
|
||||
|
@ -47,7 +52,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
result(true)
|
||||
break
|
||||
case "loadData":
|
||||
case .loadData:
|
||||
let data = arguments!["data"] as! String
|
||||
let mimeType = arguments!["mimeType"] as! String
|
||||
let encoding = arguments!["encoding"] as! String
|
||||
|
@ -60,7 +65,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL)
|
||||
result(true)
|
||||
break
|
||||
case "loadFile":
|
||||
case .loadFile:
|
||||
let assetFilePath = arguments!["assetFilePath"] as! String
|
||||
|
||||
do {
|
||||
|
@ -72,7 +77,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
result(true)
|
||||
break
|
||||
case "evaluateJavascript":
|
||||
case .evaluateJavascript:
|
||||
if let webView = webView {
|
||||
let source = arguments!["source"] as! String
|
||||
let contentWorldMap = arguments!["contentWorld"] as? [String:Any?]
|
||||
|
@ -91,58 +96,58 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "injectJavascriptFileFromUrl":
|
||||
case .injectJavascriptFileFromUrl:
|
||||
let urlFile = arguments!["urlFile"] as! String
|
||||
let scriptHtmlTagAttributes = arguments!["scriptHtmlTagAttributes"] as? [String:Any?]
|
||||
webView?.injectJavascriptFileFromUrl(urlFile: urlFile, scriptHtmlTagAttributes: scriptHtmlTagAttributes)
|
||||
result(true)
|
||||
break
|
||||
case "injectCSSCode":
|
||||
case .injectCSSCode:
|
||||
let source = arguments!["source"] as! String
|
||||
webView?.injectCSSCode(source: source)
|
||||
result(true)
|
||||
break
|
||||
case "injectCSSFileFromUrl":
|
||||
case .injectCSSFileFromUrl:
|
||||
let urlFile = arguments!["urlFile"] as! String
|
||||
let cssLinkHtmlTagAttributes = arguments!["cssLinkHtmlTagAttributes"] as? [String:Any?]
|
||||
webView?.injectCSSFileFromUrl(urlFile: urlFile, cssLinkHtmlTagAttributes: cssLinkHtmlTagAttributes)
|
||||
result(true)
|
||||
break
|
||||
case "reload":
|
||||
case .reload:
|
||||
webView?.reload()
|
||||
result(true)
|
||||
break
|
||||
case "goBack":
|
||||
case .goBack:
|
||||
webView?.goBack()
|
||||
result(true)
|
||||
break
|
||||
case "canGoBack":
|
||||
case .canGoBack:
|
||||
result(webView?.canGoBack ?? false)
|
||||
break
|
||||
case "goForward":
|
||||
case .goForward:
|
||||
webView?.goForward()
|
||||
result(true)
|
||||
break
|
||||
case "canGoForward":
|
||||
case .canGoForward:
|
||||
result(webView?.canGoForward ?? false)
|
||||
break
|
||||
case "goBackOrForward":
|
||||
case .goBackOrForward:
|
||||
let steps = arguments!["steps"] as! Int
|
||||
webView?.goBackOrForward(steps: steps)
|
||||
result(true)
|
||||
break
|
||||
case "canGoBackOrForward":
|
||||
case .canGoBackOrForward:
|
||||
let steps = arguments!["steps"] as! Int
|
||||
result(webView?.canGoBackOrForward(steps: steps) ?? false)
|
||||
break
|
||||
case "stopLoading":
|
||||
case .stopLoading:
|
||||
webView?.stopLoading()
|
||||
result(true)
|
||||
break
|
||||
case "isLoading":
|
||||
case .isLoading:
|
||||
result(webView?.isLoading ?? false)
|
||||
break
|
||||
case "takeScreenshot":
|
||||
case .takeScreenshot:
|
||||
if let webView = webView, #available(iOS 11.0, *) {
|
||||
let screenshotConfiguration = arguments!["screenshotConfiguration"] as? [String: Any?]
|
||||
webView.takeScreenshot(with: screenshotConfiguration, completionHandler: { (screenshot) -> Void in
|
||||
|
@ -153,7 +158,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "setSettings":
|
||||
case .setSettings:
|
||||
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
|
||||
let inAppBrowserSettings = InAppBrowserSettings()
|
||||
let inAppBrowserSettingsMap = arguments!["settings"] as! [String: Any]
|
||||
|
@ -167,14 +172,14 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
result(true)
|
||||
break
|
||||
case "getSettings":
|
||||
case .getSettings:
|
||||
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
|
||||
result(iabController.getSettings())
|
||||
} else {
|
||||
result(webView?.getSettings())
|
||||
}
|
||||
break
|
||||
case "close":
|
||||
case .close:
|
||||
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
|
||||
iabController.close {
|
||||
result(true)
|
||||
|
@ -183,7 +188,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
break
|
||||
case "show":
|
||||
case .show:
|
||||
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
|
||||
iabController.show {
|
||||
result(true)
|
||||
|
@ -192,7 +197,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
break
|
||||
case "hide":
|
||||
case .hide:
|
||||
if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController {
|
||||
iabController.hide {
|
||||
result(true)
|
||||
|
@ -201,13 +206,13 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
break
|
||||
case "getCopyBackForwardList":
|
||||
case .getCopyBackForwardList:
|
||||
result(webView?.getCopyBackForwardList())
|
||||
break
|
||||
case "findAllAsync":
|
||||
if let webView = webView {
|
||||
case .findAllAsync:
|
||||
if let webView = webView, let findInteractionController = webView.findInteractionController {
|
||||
let find = arguments!["find"] as! String
|
||||
webView.findAllAsync(find: find, completionHandler: {(value, error) in
|
||||
findInteractionController.findAllAsync(find: find, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
|
@ -218,10 +223,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "findNext":
|
||||
if let webView = webView {
|
||||
case .findNext:
|
||||
if let webView = webView, let findInteractionController = webView.findInteractionController {
|
||||
let forward = arguments!["forward"] as! Bool
|
||||
webView.findNext(forward: forward, completionHandler: {(value, error) in
|
||||
findInteractionController.findNext(forward: forward, completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
|
@ -232,9 +237,9 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "clearMatches":
|
||||
if let webView = webView {
|
||||
webView.clearMatches(completionHandler: {(value, error) in
|
||||
case .clearMatches:
|
||||
if let webView = webView, let findInteractionController = webView.findInteractionController {
|
||||
findInteractionController.clearMatches(completionHandler: {(value, error) in
|
||||
if error != nil {
|
||||
result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil))
|
||||
return
|
||||
|
@ -245,33 +250,33 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "clearCache":
|
||||
case .clearCache:
|
||||
webView?.clearCache()
|
||||
result(true)
|
||||
break
|
||||
case "scrollTo":
|
||||
case .scrollTo:
|
||||
let x = arguments!["x"] as! Int
|
||||
let y = arguments!["y"] as! Int
|
||||
let animated = arguments!["animated"] as! Bool
|
||||
webView?.scrollTo(x: x, y: y, animated: animated)
|
||||
result(true)
|
||||
break
|
||||
case "scrollBy":
|
||||
case .scrollBy:
|
||||
let x = arguments!["x"] as! Int
|
||||
let y = arguments!["y"] as! Int
|
||||
let animated = arguments!["animated"] as! Bool
|
||||
webView?.scrollBy(x: x, y: y, animated: animated)
|
||||
result(true)
|
||||
break
|
||||
case "pauseTimers":
|
||||
case .pauseTimers:
|
||||
webView?.pauseTimers()
|
||||
result(true)
|
||||
break
|
||||
case "resumeTimers":
|
||||
case .resumeTimers:
|
||||
webView?.resumeTimers()
|
||||
result(true)
|
||||
break
|
||||
case "printCurrentPage":
|
||||
case .printCurrentPage:
|
||||
if let webView = webView {
|
||||
let settings = PrintJobSettings()
|
||||
if let settingsMap = arguments!["settings"] as? [String: Any?] {
|
||||
|
@ -282,29 +287,29 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getContentHeight":
|
||||
case .getContentHeight:
|
||||
result(webView?.getContentHeight())
|
||||
break
|
||||
case "zoomBy":
|
||||
case .zoomBy:
|
||||
let zoomFactor = (arguments!["zoomFactor"] as! NSNumber).floatValue
|
||||
let animated = arguments!["animated"] as! Bool
|
||||
webView?.zoomBy(zoomFactor: zoomFactor, animated: animated)
|
||||
result(true)
|
||||
break
|
||||
case "reloadFromOrigin":
|
||||
case .reloadFromOrigin:
|
||||
webView?.reloadFromOrigin()
|
||||
result(true)
|
||||
break
|
||||
case "getOriginalUrl":
|
||||
case .getOriginalUrl:
|
||||
result(webView?.getOriginalUrl()?.absoluteString)
|
||||
break
|
||||
case "getZoomScale":
|
||||
case .getZoomScale:
|
||||
result(webView?.getZoomScale())
|
||||
break
|
||||
case "hasOnlySecureContent":
|
||||
case .hasOnlySecureContent:
|
||||
result(webView?.hasOnlySecureContent ?? false)
|
||||
break
|
||||
case "getSelectedText":
|
||||
case .getSelectedText:
|
||||
if let webView = webView {
|
||||
webView.getSelectedText { (value, error) in
|
||||
if let err = error {
|
||||
|
@ -319,7 +324,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getHitTestResult":
|
||||
case .getHitTestResult:
|
||||
if let webView = webView {
|
||||
webView.getHitTestResult { (hitTestResult) in
|
||||
result(hitTestResult.toMap())
|
||||
|
@ -329,11 +334,11 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "clearFocus":
|
||||
case .clearFocus:
|
||||
webView?.clearFocus()
|
||||
result(true)
|
||||
break
|
||||
case "setContextMenu":
|
||||
case .setContextMenu:
|
||||
if let webView = webView {
|
||||
let contextMenu = arguments!["contextMenu"] as? [String: Any]
|
||||
webView.contextMenu = contextMenu
|
||||
|
@ -342,7 +347,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "requestFocusNodeHref":
|
||||
case .requestFocusNodeHref:
|
||||
if let webView = webView {
|
||||
webView.requestFocusNodeHref { (value, error) in
|
||||
if let err = error {
|
||||
|
@ -356,7 +361,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "requestImageRef":
|
||||
case .requestImageRef:
|
||||
if let webView = webView {
|
||||
webView.requestImageRef { (value, error) in
|
||||
if let err = error {
|
||||
|
@ -370,24 +375,24 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getScrollX":
|
||||
case .getScrollX:
|
||||
if let webView = webView {
|
||||
result(Int(webView.scrollView.contentOffset.x))
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getScrollY":
|
||||
case .getScrollY:
|
||||
if let webView = webView {
|
||||
result(Int(webView.scrollView.contentOffset.y))
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getCertificate":
|
||||
case .getCertificate:
|
||||
result(webView?.getCertificate()?.toMap())
|
||||
break
|
||||
case "addUserScript":
|
||||
case .addUserScript:
|
||||
if let webView = webView {
|
||||
let userScriptMap = arguments!["userScript"] as! [String: Any?]
|
||||
let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView.windowId)!
|
||||
|
@ -396,23 +401,23 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
result(true)
|
||||
break
|
||||
case "removeUserScript":
|
||||
case .removeUserScript:
|
||||
let index = arguments!["index"] as! Int
|
||||
let userScriptMap = arguments!["userScript"] as! [String: Any?]
|
||||
let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView?.windowId)!
|
||||
webView?.configuration.userContentController.removeUserOnlyScript(at: index, injectionTime: userScript.injectionTime)
|
||||
result(true)
|
||||
break
|
||||
case "removeUserScriptsByGroupName":
|
||||
case .removeUserScriptsByGroupName:
|
||||
let groupName = arguments!["groupName"] as! String
|
||||
webView?.configuration.userContentController.removeUserOnlyScripts(with: groupName)
|
||||
result(true)
|
||||
break
|
||||
case "removeAllUserScripts":
|
||||
case .removeAllUserScripts:
|
||||
webView?.configuration.userContentController.removeAllUserOnlyScripts()
|
||||
result(true)
|
||||
break
|
||||
case "callAsyncJavaScript":
|
||||
case .callAsyncJavaScript:
|
||||
if let webView = webView, #available(iOS 10.3, *) {
|
||||
if #available(iOS 14.0, *) {
|
||||
let functionBody = arguments!["functionBody"] as! String
|
||||
|
@ -436,7 +441,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "createPdf":
|
||||
case .createPdf:
|
||||
if let webView = webView, #available(iOS 14.0, *) {
|
||||
let configuration = arguments!["pdfConfiguration"] as? [String: Any?]
|
||||
webView.createPdf(configuration: configuration, completionHandler: { (pdf) -> Void in
|
||||
|
@ -447,7 +452,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "createWebArchiveData":
|
||||
case .createWebArchiveData:
|
||||
if let webView = webView, #available(iOS 14.0, *) {
|
||||
webView.createWebArchiveData(dataCompletionHandler: { (webArchiveData) -> Void in
|
||||
result(webArchiveData)
|
||||
|
@ -457,7 +462,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "saveWebArchive":
|
||||
case .saveWebArchive:
|
||||
if let webView = webView, #available(iOS 14.0, *) {
|
||||
let filePath = arguments!["filePath"] as! String
|
||||
let autoname = arguments!["autoname"] as! Bool
|
||||
|
@ -469,7 +474,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "isSecureContext":
|
||||
case .isSecureContext:
|
||||
if let webView = webView {
|
||||
webView.isSecureContext(completionHandler: { (isSecureContext) in
|
||||
result(isSecureContext)
|
||||
|
@ -479,7 +484,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "createWebMessageChannel":
|
||||
case .createWebMessageChannel:
|
||||
if let webView = webView {
|
||||
let _ = webView.createWebMessageChannel { (webMessageChannel) in
|
||||
result(webMessageChannel.toMap())
|
||||
|
@ -488,7 +493,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "postWebMessage":
|
||||
case .postWebMessage:
|
||||
if let webView = webView {
|
||||
let message = arguments!["message"] as! [String: Any?]
|
||||
let targetOrigin = arguments!["targetOrigin"] as! String
|
||||
|
@ -516,7 +521,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "addWebMessageListener":
|
||||
case .addWebMessageListener:
|
||||
if let webView = webView {
|
||||
let webMessageListenerMap = arguments!["webMessageListener"] as! [String: Any?]
|
||||
let webMessageListener = WebMessageListener.fromMap(map: webMessageListenerMap)!
|
||||
|
@ -530,21 +535,21 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "canScrollVertically":
|
||||
case .canScrollVertically:
|
||||
if let webView = webView {
|
||||
result(webView.canScrollVertically())
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "canScrollHorizontally":
|
||||
case .canScrollHorizontally:
|
||||
if let webView = webView {
|
||||
result(webView.canScrollHorizontally())
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "pauseAllMediaPlayback":
|
||||
case .pauseAllMediaPlayback:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
webView.pauseAllMediaPlayback(completionHandler: { () -> Void in
|
||||
result(true)
|
||||
|
@ -553,7 +558,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "setAllMediaPlaybackSuspended":
|
||||
case .setAllMediaPlaybackSuspended:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
let suspended = arguments!["suspended"] as! Bool
|
||||
webView.setAllMediaPlaybackSuspended(suspended, completionHandler: { () -> Void in
|
||||
|
@ -563,7 +568,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "closeAllMediaPresentations":
|
||||
case .closeAllMediaPresentations:
|
||||
if let webView = self.webView, #available(iOS 14.5, *) {
|
||||
// closeAllMediaPresentations with completionHandler v15.0 makes the app crash
|
||||
// with error EXC_BAD_ACCESS, so use closeAllMediaPresentations v14.5
|
||||
|
@ -573,7 +578,7 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "requestMediaPlaybackState":
|
||||
case .requestMediaPlaybackState:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
webView.requestMediaPlaybackState(completionHandler: { (state) -> Void in
|
||||
result(state.rawValue)
|
||||
|
@ -582,32 +587,33 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(nil)
|
||||
}
|
||||
break
|
||||
case "getMetaThemeColor":
|
||||
case .getMetaThemeColor:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
result(webView.themeColor?.hexString)
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "isInFullscreen":
|
||||
// if let webView = webView, #available(iOS 15.0, *) {
|
||||
// result(webView.fullscreenState == .inFullscreen)
|
||||
// }
|
||||
case .isInFullscreen:
|
||||
if let webView = webView {
|
||||
result(webView.inFullscreen)
|
||||
if #available(iOS 16.0, *) {
|
||||
result(webView.fullscreenState == .inFullscreen)
|
||||
} else {
|
||||
result(webView.inFullscreen)
|
||||
}
|
||||
}
|
||||
else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
case "getCameraCaptureState":
|
||||
case .getCameraCaptureState:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
result(webView.cameraCaptureState.rawValue)
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "setCameraCaptureState":
|
||||
case .setCameraCaptureState:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none
|
||||
webView.setCameraCaptureState(state) {
|
||||
|
@ -617,14 +623,14 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
case "getMicrophoneCaptureState":
|
||||
case .getMicrophoneCaptureState:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
result(webView.microphoneCaptureState.rawValue)
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
break
|
||||
case "setMicrophoneCaptureState":
|
||||
case .setMicrophoneCaptureState:
|
||||
if let webView = webView, #available(iOS 15.0, *) {
|
||||
let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none
|
||||
webView.setMicrophoneCaptureState(state) {
|
||||
|
@ -634,12 +640,10 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
result(false)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Use FindInteractionChannelDelegate.onFindResultReceived instead.")
|
||||
public func onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Bool) {
|
||||
let arguments: [String : Any?] = [
|
||||
"activeMatchOrdinal": activeMatchOrdinal,
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// WebViewChannelDelegateMethods.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 08/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum WebViewChannelDelegateMethods: String {
|
||||
case getUrl = "getUrl"
|
||||
case getTitle = "getTitle"
|
||||
case getProgress = "getProgress"
|
||||
case loadUrl = "loadUrl"
|
||||
case postUrl = "postUrl"
|
||||
case loadData = "loadData"
|
||||
case loadFile = "loadFile"
|
||||
case evaluateJavascript = "evaluateJavascript"
|
||||
case injectJavascriptFileFromUrl = "injectJavascriptFileFromUrl"
|
||||
case injectCSSCode = "injectCSSCode"
|
||||
case injectCSSFileFromUrl = "injectCSSFileFromUrl"
|
||||
case reload = "reload"
|
||||
case goBack = "goBack"
|
||||
case canGoBack = "canGoBack"
|
||||
case goForward = "goForward"
|
||||
case canGoForward = "canGoForward"
|
||||
case goBackOrForward = "goBackOrForward"
|
||||
case canGoBackOrForward = "canGoBackOrForward"
|
||||
case stopLoading = "stopLoading"
|
||||
case isLoading = "isLoading"
|
||||
case takeScreenshot = "takeScreenshot"
|
||||
case setSettings = "setSettings"
|
||||
case getSettings = "getSettings"
|
||||
case close = "close"
|
||||
case show = "show"
|
||||
case hide = "hide"
|
||||
case getCopyBackForwardList = "getCopyBackForwardList"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.findAllAsync instead.")
|
||||
case findAllAsync = "findAllAsync"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.findNext instead.")
|
||||
case findNext = "findNext"
|
||||
@available(*, deprecated, message: "Use FindInteractionController.clearMatches instead.")
|
||||
case clearMatches = "clearMatches"
|
||||
case clearCache = "clearCache"
|
||||
case scrollTo = "scrollTo"
|
||||
case scrollBy = "scrollBy"
|
||||
case pauseTimers = "pauseTimers"
|
||||
case resumeTimers = "resumeTimers"
|
||||
case printCurrentPage = "printCurrentPage"
|
||||
case getContentHeight = "getContentHeight"
|
||||
case zoomBy = "zoomBy"
|
||||
case reloadFromOrigin = "reloadFromOrigin"
|
||||
case getOriginalUrl = "getOriginalUrl"
|
||||
case getZoomScale = "getZoomScale"
|
||||
case hasOnlySecureContent = "hasOnlySecureContent"
|
||||
case getSelectedText = "getSelectedText"
|
||||
case getHitTestResult = "getHitTestResult"
|
||||
case clearFocus = "clearFocus"
|
||||
case setContextMenu = "setContextMenu"
|
||||
case requestFocusNodeHref = "requestFocusNodeHref"
|
||||
case requestImageRef = "requestImageRef"
|
||||
case getScrollX = "getScrollX"
|
||||
case getScrollY = "getScrollY"
|
||||
case getCertificate = "getCertificate"
|
||||
case addUserScript = "addUserScript"
|
||||
case removeUserScript = "removeUserScript"
|
||||
case removeUserScriptsByGroupName = "removeUserScriptsByGroupName"
|
||||
case removeAllUserScripts = "removeAllUserScripts"
|
||||
case callAsyncJavaScript = "callAsyncJavaScript"
|
||||
case createPdf = "createPdf"
|
||||
case createWebArchiveData = "createWebArchiveData"
|
||||
case saveWebArchive = "saveWebArchive"
|
||||
case isSecureContext = "isSecureContext"
|
||||
case createWebMessageChannel = "createWebMessageChannel"
|
||||
case postWebMessage = "postWebMessage"
|
||||
case addWebMessageListener = "addWebMessageListener"
|
||||
case canScrollVertically = "canScrollVertically"
|
||||
case canScrollHorizontally = "canScrollHorizontally"
|
||||
case pauseAllMediaPlayback = "pauseAllMediaPlayback"
|
||||
case setAllMediaPlaybackSuspended = "setAllMediaPlaybackSuspended"
|
||||
case closeAllMediaPresentations = "closeAllMediaPresentations"
|
||||
case requestMediaPlaybackState = "requestMediaPlaybackState"
|
||||
case getMetaThemeColor = "getMetaThemeColor"
|
||||
case isInFullscreen = "isInFullscreen"
|
||||
case getCameraCaptureState = "getCameraCaptureState"
|
||||
case setCameraCaptureState = "setCameraCaptureState"
|
||||
case getMicrophoneCaptureState = "getMicrophoneCaptureState"
|
||||
case setMicrophoneCaptureState = "setMicrophoneCaptureState"
|
||||
}
|
|
@ -42,7 +42,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._findAllAsyncForElement = function(element, key
|
|||
|
||||
span.setAttribute(
|
||||
"id",
|
||||
"WKWEBVIEW_SEARCH_WORD_" + \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)
|
||||
"\(JAVASCRIPT_BRIDGE_NAME)_SEARCH_WORD_" + \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE)
|
||||
);
|
||||
span.setAttribute("class", "\(JAVASCRIPT_BRIDGE_NAME)_Highlight");
|
||||
var backgroundColor = \(FIND_TEXT_HIGHLIGHT_SEARCH_RESULT_COUNT_JS_SOURCE) == 0 ? "#FF9732" : "#FFFF00";
|
||||
|
|
|
@ -28,17 +28,17 @@ public class PullToRefreshControl : UIRefreshControl, Disposable {
|
|||
}
|
||||
|
||||
public func prepare() {
|
||||
if let options = settings {
|
||||
if options.enabled {
|
||||
if let settings = settings {
|
||||
if settings.enabled {
|
||||
delegate?.enablePullToRefresh()
|
||||
}
|
||||
if let color = options.color, !color.isEmpty {
|
||||
if let color = settings.color, !color.isEmpty {
|
||||
tintColor = UIColor(hexString: color)
|
||||
}
|
||||
if let backgroundTintColor = options.backgroundColor, !backgroundTintColor.isEmpty {
|
||||
if let backgroundTintColor = settings.backgroundColor, !backgroundTintColor.isEmpty {
|
||||
backgroundColor = UIColor(hexString: backgroundTintColor)
|
||||
}
|
||||
if let attributedTitleMap = options.attributedTitle {
|
||||
if let attributedTitleMap = settings.attributedTitle {
|
||||
attributedTitle = NSAttributedString.fromMap(map: attributedTitleMap)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// UIFindSession.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 07/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
extension UIFindSession {
|
||||
public func toMap () -> [String:Any?] {
|
||||
return [
|
||||
"resultCount": resultCount,
|
||||
"highlightedResultIndex": highlightedResultIndex,
|
||||
"searchResultDisplayStyle": searchResultDisplayStyle.rawValue
|
||||
]
|
||||
}
|
||||
}
|
|
@ -62,6 +62,10 @@ class WebViewFeature {
|
|||
static const PROXY_OVERRIDE =
|
||||
WebViewFeature._internal('PROXY_OVERRIDE', 'PROXY_OVERRIDE');
|
||||
|
||||
///This feature covers [ProxySettings.reverseBypassEnabled].
|
||||
static const PROXY_OVERRIDE_REVERSE_BYPASS = WebViewFeature._internal(
|
||||
'PROXY_OVERRIDE_REVERSE_BYPASS', 'PROXY_OVERRIDE_REVERSE_BYPASS');
|
||||
|
||||
///
|
||||
static const RECEIVE_HTTP_ERROR =
|
||||
WebViewFeature._internal('RECEIVE_HTTP_ERROR', 'RECEIVE_HTTP_ERROR');
|
||||
|
@ -223,6 +227,7 @@ class WebViewFeature {
|
|||
WebViewFeature.OFF_SCREEN_PRERASTER,
|
||||
WebViewFeature.POST_WEB_MESSAGE,
|
||||
WebViewFeature.PROXY_OVERRIDE,
|
||||
WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS,
|
||||
WebViewFeature.RECEIVE_HTTP_ERROR,
|
||||
WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR,
|
||||
WebViewFeature.SAFE_BROWSING_ALLOWLIST,
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import '../debug_logging_settings.dart';
|
||||
import '../types/main.dart';
|
||||
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class FindInteractionController {
|
||||
MethodChannel? _channel;
|
||||
|
||||
///Debug settings.
|
||||
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
|
||||
|
||||
///Event fired as find-on-page operations progress.
|
||||
///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true.
|
||||
///
|
||||
///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match.
|
||||
///
|
||||
///[numberOfMatches] represents how many matches have been found.
|
||||
///
|
||||
///[isDoneCounting] whether the find operation has actually completed.
|
||||
///
|
||||
///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`, this event will not be called.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean)))
|
||||
///- iOS
|
||||
final void Function(
|
||||
FindInteractionController controller,
|
||||
int activeMatchOrdinal,
|
||||
int numberOfMatches,
|
||||
bool isDoneCounting)? onFindResultReceived;
|
||||
|
||||
FindInteractionController({this.onFindResultReceived}) {}
|
||||
|
||||
void initMethodChannel(dynamic id) {
|
||||
this._channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id');
|
||||
|
||||
this._channel?.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethod(call);
|
||||
} on Error catch (e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_debugLog(String method, dynamic args) {
|
||||
if (FindInteractionController.debugLoggingSettings.enabled) {
|
||||
for (var regExp
|
||||
in FindInteractionController.debugLoggingSettings.excludeFilter) {
|
||||
if (regExp.hasMatch(method)) return;
|
||||
}
|
||||
var maxLogMessageLength =
|
||||
FindInteractionController.debugLoggingSettings.maxLogMessageLength;
|
||||
String message = "FindInteractionController " +
|
||||
" calling \"" +
|
||||
method.toString() +
|
||||
"\" using " +
|
||||
args.toString();
|
||||
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
|
||||
message = message.substring(0, maxLogMessageLength) + "...";
|
||||
}
|
||||
if (!FindInteractionController.debugLoggingSettings.usePrint) {
|
||||
developer.log(message, name: this.runtimeType.toString());
|
||||
} else {
|
||||
print("[${this.runtimeType.toString()}] $message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
_debugLog(call.method, call.arguments);
|
||||
|
||||
switch (call.method) {
|
||||
case "onFindResultReceived":
|
||||
if (onFindResultReceived != null) {
|
||||
int activeMatchOrdinal = call.arguments["activeMatchOrdinal"];
|
||||
int numberOfMatches = call.arguments["numberOfMatches"];
|
||||
bool isDoneCounting = call.arguments["isDoneCounting"];
|
||||
onFindResultReceived!(
|
||||
this, activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Finds all instances of find on the page and highlights them. Notifies [FindInteractionController.onFindResultReceived] listener.
|
||||
///
|
||||
///[find] represents the string to find.
|
||||
///
|
||||
///**NOTE**: on Android native WebView, it finds all instances asynchronously. Successive calls to this will cancel any pending searches.
|
||||
///
|
||||
///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`,
|
||||
///it uses the built-in find interaction native UI,
|
||||
///otherwise this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.findAllAsync](https://developer.android.com/reference/android/webkit/WebView#findAllAsync(java.lang.String)))
|
||||
///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2) with [Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2))
|
||||
Future<void> findAllAsync({required String find}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('find', () => find);
|
||||
await _channel?.invokeMethod('findAllAsync', args);
|
||||
}
|
||||
|
||||
///Highlights and scrolls to the next match found by [findAllAsync]. Notifies [FindInteractionController.onFindResultReceived] listener.
|
||||
///
|
||||
///[forward] represents the direction to search.
|
||||
///
|
||||
///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`,
|
||||
///it uses the built-in find interaction native UI,
|
||||
///otherwise this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.findNext](https://developer.android.com/reference/android/webkit/WebView#findNext(boolean)))
|
||||
///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.findNext](https://developer.apple.com/documentation/uikit/uifindinteraction/3975829-findnext?changes=_2) and ([Official API - UIFindInteraction.findPrevious](https://developer.apple.com/documentation/uikit/uifindinteraction/3975830-findprevious?changes=_2)))
|
||||
Future<void> findNext({required bool forward}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('forward', () => forward);
|
||||
await _channel?.invokeMethod('findNext', args);
|
||||
}
|
||||
|
||||
///Clears the highlighting surrounding text matches created by [findAllAsync].
|
||||
///
|
||||
///**NOTE**: on iOS, if [InAppWebViewSettings.isFindInteractionEnabled] is `true`,
|
||||
///it uses the built-in find interaction native UI,
|
||||
///otherwise this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.clearMatches](https://developer.android.com/reference/android/webkit/WebView#clearMatches()))
|
||||
///- iOS (if [InAppWebViewSettings.isFindInteractionEnabled] is `true`: [Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2))
|
||||
Future<void> clearMatches() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel?.invokeMethod('clearMatches', args);
|
||||
}
|
||||
|
||||
///Pre-populate the system find panel's search text field with a search query.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2))
|
||||
Future<void> setSearchText(String? searchText) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('searchText', () => searchText);
|
||||
await _channel?.invokeMethod('setSearchText', args);
|
||||
}
|
||||
|
||||
///Get the system find panel's search text field value.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.searchText](https://developer.apple.com/documentation/uikit/uifindinteraction/3975834-searchtext?changes=_2))
|
||||
Future<String?> getSearchText() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _channel?.invokeMethod('getSearchText', args);
|
||||
}
|
||||
|
||||
///A Boolean value that indicates when the find panel displays onscreen.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.isFindNavigatorVisible](https://developer.apple.com/documentation/uikit/uifindinteraction/3975828-isfindnavigatorvisible?changes=_2))
|
||||
Future<bool?> isFindNavigatorVisible() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _channel?.invokeMethod('isFindNavigatorVisible', args);
|
||||
}
|
||||
|
||||
///Updates the results the interface displays for the active search.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.updateResultCount](https://developer.apple.com/documentation/uikit/uifindinteraction/3975835-updateresultcount?changes=_2))
|
||||
Future<void> updateResultCount() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel?.invokeMethod('updateResultCount', args);
|
||||
}
|
||||
|
||||
///Begins a search, displaying the find panel.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2))
|
||||
Future<void> presentFindNavigator() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel?.invokeMethod('presentFindNavigator', args);
|
||||
}
|
||||
|
||||
///Dismisses the find panel, if present.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2))
|
||||
Future<void> dismissFindNavigator() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel?.invokeMethod('dismissFindNavigator', args);
|
||||
}
|
||||
|
||||
///If there's a currently active find session (implying [isFindNavigatorVisible] is `true`), returns the active find session.
|
||||
///
|
||||
///**NOTE**: available only on iOS and only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS ([Official API - UIFindInteraction.activeFindSession](https://developer.apple.com/documentation/uikit/uifindinteraction/3975825-activefindsession?changes=_7____4_8&language=objc))
|
||||
Future<FindSession?> getActiveFindSession() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
Map<String, dynamic>? result =
|
||||
(await _channel?.invokeMethod('getActiveFindSession', args))
|
||||
?.cast<String, dynamic>();
|
||||
return FindSession.fromMap(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export 'find_interaction_controller.dart';
|
|
@ -6,6 +6,7 @@ import 'dart:developer' as developer;
|
|||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../pull_to_refresh/main.dart';
|
||||
import '../types/main.dart';
|
||||
|
||||
|
@ -60,6 +61,9 @@ class InAppBrowser {
|
|||
///Represents the pull-to-refresh feature controller.
|
||||
PullToRefreshController? pullToRefreshController;
|
||||
|
||||
///Represents the find interaction feature controller.
|
||||
FindInteractionController? findInteractionController;
|
||||
|
||||
///Initial list of user scripts to be loaded at start or end of a page loading.
|
||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
|
||||
|
@ -129,6 +133,7 @@ class InAppBrowser {
|
|||
_debugLog(call.method, call.arguments);
|
||||
this._isOpened = true;
|
||||
this.pullToRefreshController?.initMethodChannel(id);
|
||||
this.findInteractionController?.initMethodChannel(id);
|
||||
onBrowserCreated();
|
||||
break;
|
||||
case "onExit":
|
||||
|
@ -662,18 +667,8 @@ class InAppBrowser {
|
|||
return null;
|
||||
}
|
||||
|
||||
///Event fired as find-on-page operations progress.
|
||||
///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true.
|
||||
///
|
||||
///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match.
|
||||
///
|
||||
///[numberOfMatches] represents how many matches have been found.
|
||||
///
|
||||
///[isDoneCounting] whether the find operation has actually completed.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean)))
|
||||
///- iOS
|
||||
///Use [FindInteractionController.onFindResultReceived] instead.
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
void onFindResultReceived(
|
||||
int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) {}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_inappwebview/src/util.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../types/main.dart';
|
||||
import '../print_job/main.dart';
|
||||
import 'webview.dart';
|
||||
|
@ -61,6 +62,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
this.contextMenu,
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController,
|
||||
this.findInteractionController,
|
||||
this.implementation = WebViewImplementation.NATIVE,
|
||||
this.onWebViewCreated,
|
||||
this.onLoadStart,
|
||||
|
@ -87,6 +89,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
this.onReceivedHttpAuthRequest,
|
||||
this.onReceivedServerTrustAuthRequest,
|
||||
this.onReceivedClientCertRequest,
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
this.onFindResultReceived,
|
||||
this.shouldInterceptAjaxRequest,
|
||||
this.onAjaxReadyStateChange,
|
||||
|
@ -173,6 +176,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
switch (call.method) {
|
||||
case "onWebViewCreated":
|
||||
pullToRefreshController?.initMethodChannel(id);
|
||||
findInteractionController?.initMethodChannel(id);
|
||||
if (onWebViewCreated != null) {
|
||||
onWebViewCreated!(webViewController);
|
||||
}
|
||||
|
@ -323,6 +327,9 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
@override
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
@override
|
||||
final FindInteractionController? findInteractionController;
|
||||
|
||||
@override
|
||||
final WebViewImplementation implementation;
|
||||
|
||||
|
@ -422,6 +429,8 @@ class HeadlessInAppWebView implements WebView, Disposable {
|
|||
void Function(InAppWebViewController controller,
|
||||
DownloadStartRequest downloadStartRequest)? onDownloadStartRequest;
|
||||
|
||||
///Use [FindInteractionController.onFindResultReceived] instead.
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
@override
|
||||
void Function(InAppWebViewController controller, int activeMatchOrdinal,
|
||||
int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../web/web_platform_manager.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
|
@ -50,6 +51,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
this.initialSettings,
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController,
|
||||
this.findInteractionController,
|
||||
this.implementation = WebViewImplementation.NATIVE,
|
||||
this.contextMenu,
|
||||
this.onWebViewCreated,
|
||||
|
@ -77,6 +79,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
this.onReceivedHttpAuthRequest,
|
||||
this.onReceivedServerTrustAuthRequest,
|
||||
this.onReceivedClientCertRequest,
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
this.onFindResultReceived,
|
||||
this.shouldInterceptAjaxRequest,
|
||||
this.onAjaxReadyStateChange,
|
||||
|
@ -206,6 +209,9 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
@override
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
@override
|
||||
final FindInteractionController? findInteractionController;
|
||||
|
||||
@override
|
||||
final ContextMenu? contextMenu;
|
||||
|
||||
|
@ -294,6 +300,8 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
final void Function(InAppWebViewController controller,
|
||||
DownloadStartRequest downloadStartRequest)? onDownloadStartRequest;
|
||||
|
||||
///Use [FindInteractionController.onFindResultReceived] instead.
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
@override
|
||||
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
|
||||
int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
|
||||
|
@ -725,6 +733,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
void _onPlatformViewCreated(int id) {
|
||||
_controller = InAppWebViewController(id, widget);
|
||||
widget.pullToRefreshController?.initMethodChannel(id);
|
||||
widget.findInteractionController?.initMethodChannel(id);
|
||||
if (widget.onWebViewCreated != null) {
|
||||
widget.onWebViewCreated!(_controller!);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import 'webview.dart';
|
|||
import '_static_channel.dart';
|
||||
|
||||
import '../print_job/main.dart';
|
||||
import '../find_interaction/main.dart';
|
||||
|
||||
///List of forbidden names for JavaScript handlers.
|
||||
// ignore: non_constant_identifier_names
|
||||
|
@ -804,17 +805,34 @@ class InAppWebViewController {
|
|||
}
|
||||
break;
|
||||
case "onFindResultReceived":
|
||||
if ((_webview != null && _webview!.onFindResultReceived != null) ||
|
||||
if ((_webview != null && (_webview!.onFindResultReceived != null ||
|
||||
(_webview!.findInteractionController != null && _webview!.findInteractionController!.onFindResultReceived != null))) ||
|
||||
_inAppBrowser != null) {
|
||||
int activeMatchOrdinal = call.arguments["activeMatchOrdinal"];
|
||||
int numberOfMatches = call.arguments["numberOfMatches"];
|
||||
bool isDoneCounting = call.arguments["isDoneCounting"];
|
||||
if (_webview != null && _webview!.onFindResultReceived != null)
|
||||
_webview!.onFindResultReceived!(
|
||||
if (_webview != null) {
|
||||
if (_webview!.findInteractionController != null &&
|
||||
_webview!.findInteractionController!.onFindResultReceived !=
|
||||
null)
|
||||
_webview!.findInteractionController!.onFindResultReceived!(
|
||||
_webview!.findInteractionController!, activeMatchOrdinal,
|
||||
numberOfMatches, isDoneCounting);
|
||||
else
|
||||
_webview!.onFindResultReceived!(
|
||||
this, activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
else
|
||||
_inAppBrowser!.onFindResultReceived(
|
||||
activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
}
|
||||
else {
|
||||
if (_inAppBrowser!.findInteractionController != null &&
|
||||
_inAppBrowser!.findInteractionController!
|
||||
.onFindResultReceived != null)
|
||||
_inAppBrowser!.findInteractionController!.onFindResultReceived!(
|
||||
_webview!.findInteractionController!, activeMatchOrdinal,
|
||||
numberOfMatches, isDoneCounting);
|
||||
else
|
||||
_inAppBrowser!.onFindResultReceived(
|
||||
activeMatchOrdinal, numberOfMatches, isDoneCounting);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "onPermissionRequest":
|
||||
|
@ -2159,45 +2177,24 @@ class InAppWebViewController {
|
|||
await _channel.invokeMethod('clearCache', args);
|
||||
}
|
||||
|
||||
///Finds all instances of find on the page and highlights them. Notifies [WebView.onFindResultReceived] listener.
|
||||
///
|
||||
///[find] represents the string to find.
|
||||
///
|
||||
///**NOTE**: on Android native WebView, it finds all instances asynchronously. Successive calls to this will cancel any pending searches.
|
||||
///
|
||||
///**NOTE**: on iOS, this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.findAllAsync](https://developer.android.com/reference/android/webkit/WebView#findAllAsync(java.lang.String)))
|
||||
///- iOS
|
||||
///Use [FindInteractionController.findAllAsync] instead.
|
||||
@Deprecated("Use FindInteractionController.findAllAsync instead")
|
||||
Future<void> findAllAsync({required String find}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('find', () => find);
|
||||
await _channel.invokeMethod('findAllAsync', args);
|
||||
}
|
||||
|
||||
///Highlights and scrolls to the next match found by [findAllAsync]. Notifies [WebView.onFindResultReceived] listener.
|
||||
///
|
||||
///[forward] represents the direction to search.
|
||||
///
|
||||
///**NOTE**: on iOS, this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.findNext](https://developer.android.com/reference/android/webkit/WebView#findNext(boolean)))
|
||||
///- iOS
|
||||
///Use [FindInteractionController.findNext] instead.
|
||||
@Deprecated("Use FindInteractionController.findNext instead")
|
||||
Future<void> findNext({required bool forward}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('forward', () => forward);
|
||||
await _channel.invokeMethod('findNext', args);
|
||||
}
|
||||
|
||||
///Clears the highlighting surrounding text matches created by [findAllAsync()].
|
||||
///
|
||||
///**NOTE**: on iOS, this is implemented using CSS and Javascript.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.clearMatches](https://developer.android.com/reference/android/webkit/WebView#clearMatches()))
|
||||
///- iOS
|
||||
///Use [FindInteractionController.clearMatches] instead.
|
||||
@Deprecated("Use FindInteractionController.clearMatches instead")
|
||||
Future<void> clearMatches() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel.invokeMethod('clearMatches', args);
|
||||
|
|
|
@ -1057,6 +1057,26 @@ class InAppWebViewSettings {
|
|||
///- iOS
|
||||
bool upgradeKnownHostsToHTTPS;
|
||||
|
||||
///Sets whether fullscreen API is enabled or not.
|
||||
///
|
||||
///The default value is `true`.
|
||||
///
|
||||
///**NOTE**: available on iOS 15.4+.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS
|
||||
bool isElementFullscreenEnabled;
|
||||
|
||||
///Sets whether the web view's built-in find interaction native UI is enabled or not.
|
||||
///
|
||||
///The default value is `false`.
|
||||
///
|
||||
///**NOTE**: available on iOS 16.0+.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- iOS
|
||||
bool isFindInteractionEnabled;
|
||||
|
||||
///Specifies a feature policy for the `<iframe>`.
|
||||
///The policy defines what features are available to the `<iframe>` based on the origin of the request
|
||||
///(e.g. access to the microphone, camera, battery, web-share API, etc.).
|
||||
|
@ -1220,6 +1240,8 @@ class InAppWebViewSettings {
|
|||
this.isTextInteractionEnabled = true,
|
||||
this.isSiteSpecificQuirksModeEnabled = true,
|
||||
this.upgradeKnownHostsToHTTPS = true,
|
||||
this.isElementFullscreenEnabled = true,
|
||||
this.isFindInteractionEnabled = false,
|
||||
this.iframeAllow,
|
||||
this.iframeAllowFullscreen,
|
||||
this.iframeSandbox,
|
||||
|
@ -1373,6 +1395,8 @@ class InAppWebViewSettings {
|
|||
"isTextInteractionEnabled": isTextInteractionEnabled,
|
||||
"isSiteSpecificQuirksModeEnabled": isSiteSpecificQuirksModeEnabled,
|
||||
"upgradeKnownHostsToHTTPS": upgradeKnownHostsToHTTPS,
|
||||
"isElementFullscreenEnabled": isElementFullscreenEnabled,
|
||||
"isFindInteractionEnabled": isFindInteractionEnabled,
|
||||
"iframeAllow": iframeAllow,
|
||||
"iframeAllowFullscreen": iframeAllowFullscreen,
|
||||
"iframeSandbox": iframeSandbox?.map((e) => e.toNativeValue()).toList(),
|
||||
|
@ -1576,6 +1600,8 @@ class InAppWebViewSettings {
|
|||
settings.isSiteSpecificQuirksModeEnabled =
|
||||
map["isSiteSpecificQuirksModeEnabled"];
|
||||
settings.upgradeKnownHostsToHTTPS = map["upgradeKnownHostsToHTTPS"];
|
||||
settings.isElementFullscreenEnabled = map["isElementFullscreenEnabled"];
|
||||
settings.isFindInteractionEnabled = map["isFindInteractionEnabled"];
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
|
|
|
@ -4,4 +4,5 @@ export 'in_app_webview_controller.dart';
|
|||
export 'in_app_webview_settings.dart';
|
||||
export 'headless_in_app_webview.dart';
|
||||
export 'android/main.dart';
|
||||
export 'apple/main.dart';
|
||||
export 'apple/main.dart';
|
||||
export '../find_interaction/find_interaction_controller.dart';
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../find_interaction/find_interaction_controller.dart';
|
||||
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
|
@ -333,18 +334,8 @@ abstract class WebView {
|
|||
InAppWebViewController controller, ClientCertChallenge challenge)?
|
||||
onReceivedClientCertRequest;
|
||||
|
||||
///Event fired as find-on-page operations progress.
|
||||
///The listener may be notified multiple times while the operation is underway, and the [numberOfMatches] value should not be considered final unless [isDoneCounting] is true.
|
||||
///
|
||||
///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match.
|
||||
///
|
||||
///[numberOfMatches] represents how many matches have been found.
|
||||
///
|
||||
///[isDoneCounting] whether the find operation has actually completed.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.FindListener.onFindResultReceived](https://developer.android.com/reference/android/webkit/WebView.FindListener#onFindResultReceived(int,%20int,%20boolean)))
|
||||
///- iOS
|
||||
///Use [FindInteractionController.onFindResultReceived] instead.
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
|
||||
int numberOfMatches, bool isDoneCounting)? onFindResultReceived;
|
||||
|
||||
|
@ -960,6 +951,13 @@ abstract class WebView {
|
|||
///- iOS
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
///Represents the find interaction feature controller.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
final FindInteractionController? findInteractionController;
|
||||
|
||||
///Represents the WebView native implementation to be used.
|
||||
///The default value is [WebViewImplementation.NATIVE].
|
||||
final WebViewImplementation implementation;
|
||||
|
@ -994,6 +992,7 @@ abstract class WebView {
|
|||
this.onReceivedHttpAuthRequest,
|
||||
this.onReceivedServerTrustAuthRequest,
|
||||
this.onReceivedClientCertRequest,
|
||||
@Deprecated('Use FindInteractionController.onFindResultReceived instead')
|
||||
this.onFindResultReceived,
|
||||
this.shouldInterceptAjaxRequest,
|
||||
this.onAjaxReadyStateChange,
|
||||
|
@ -1076,5 +1075,6 @@ abstract class WebView {
|
|||
this.contextMenu,
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController,
|
||||
this.findInteractionController,
|
||||
this.implementation = WebViewImplementation.NATIVE});
|
||||
}
|
||||
|
|
|
@ -17,3 +17,4 @@ export 'web_message/main.dart';
|
|||
export 'web_authentication_session/main.dart';
|
||||
export 'print_job/main.dart';
|
||||
export 'debug_logging_settings.dart';
|
||||
export 'find_interaction/main.dart';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:ui';
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import '../in_app_webview/webview.dart';
|
||||
|
@ -7,6 +8,7 @@ import '../util.dart';
|
|||
import '../types/main.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import 'pull_to_refresh_settings.dart';
|
||||
import '../debug_logging_settings.dart';
|
||||
|
||||
///A standard controller that can initiate the refreshing of a scroll view’s contents.
|
||||
///This should be used whenever the user can refresh the contents of a WebView via a vertical swipe gesture.
|
||||
|
@ -26,6 +28,9 @@ class PullToRefreshController {
|
|||
late PullToRefreshSettings settings;
|
||||
MethodChannel? _channel;
|
||||
|
||||
///Debug settings.
|
||||
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
|
||||
|
||||
///Event called when a swipe gesture triggers a refresh.
|
||||
final void Function()? onRefresh;
|
||||
|
||||
|
@ -40,7 +45,46 @@ class PullToRefreshController {
|
|||
this.settings = settings ?? PullToRefreshSettings();
|
||||
}
|
||||
|
||||
void initMethodChannel(dynamic id) {
|
||||
this._channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id');
|
||||
this._channel?.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethod(call);
|
||||
} on Error catch (e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_debugLog(String method, dynamic args) {
|
||||
if (PullToRefreshController.debugLoggingSettings.enabled) {
|
||||
for (var regExp
|
||||
in PullToRefreshController.debugLoggingSettings.excludeFilter) {
|
||||
if (regExp.hasMatch(method)) return;
|
||||
}
|
||||
var maxLogMessageLength =
|
||||
PullToRefreshController.debugLoggingSettings.maxLogMessageLength;
|
||||
String message = "PullToRefreshController " +
|
||||
" calling \"" +
|
||||
method.toString() +
|
||||
"\" using " +
|
||||
args.toString();
|
||||
if (maxLogMessageLength >= 0 && message.length > maxLogMessageLength) {
|
||||
message = message.substring(0, maxLogMessageLength) + "...";
|
||||
}
|
||||
if (!PullToRefreshController.debugLoggingSettings.usePrint) {
|
||||
developer.log(message, name: this.runtimeType.toString());
|
||||
} else {
|
||||
print("[${this.runtimeType.toString()}] $message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
_debugLog(call.method, call.arguments);
|
||||
|
||||
switch (call.method) {
|
||||
case "onRefresh":
|
||||
if (onRefresh != null) onRefresh!();
|
||||
|
@ -162,17 +206,4 @@ class PullToRefreshController {
|
|||
args.putIfAbsent('attributedTitle', () => attributedTitle.toMap());
|
||||
await _channel?.invokeMethod('setStyledTitle', args);
|
||||
}
|
||||
|
||||
void initMethodChannel(dynamic id) {
|
||||
this._channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id');
|
||||
this._channel?.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethod(call);
|
||||
} on Error catch (e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||
|
||||
import 'search_result_display_style.dart';
|
||||
|
||||
part 'find_session.g.dart';
|
||||
|
||||
@ExchangeableObject()
|
||||
class FindSession_ {
|
||||
///Returns the total number of results.
|
||||
int resultCount;
|
||||
|
||||
///Returns the index of the currently highlighted result.
|
||||
///If no result is currently highlighted.
|
||||
int highlightedResultIndex;
|
||||
|
||||
/// Defines how results are reported through the find panel's UI.
|
||||
SearchResultDisplayStyle_ searchResultDisplayStyle;
|
||||
|
||||
FindSession_({
|
||||
required this.resultCount,
|
||||
required this.highlightedResultIndex,
|
||||
required this.searchResultDisplayStyle
|
||||
});
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'find_session.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// ExchangeableObjectGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class FindSession {
|
||||
///Returns the total number of results.
|
||||
int resultCount;
|
||||
|
||||
///Returns the index of the currently highlighted result.
|
||||
///If no result is currently highlighted.
|
||||
int highlightedResultIndex;
|
||||
|
||||
/// Defines how results are reported through the find panel's UI.
|
||||
SearchResultDisplayStyle searchResultDisplayStyle;
|
||||
FindSession(
|
||||
{required this.resultCount,
|
||||
required this.highlightedResultIndex,
|
||||
required this.searchResultDisplayStyle});
|
||||
|
||||
///Gets a possible [FindSession] instance from a [Map] value.
|
||||
static FindSession? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = FindSession(
|
||||
resultCount: map['resultCount'],
|
||||
highlightedResultIndex: map['highlightedResultIndex'],
|
||||
searchResultDisplayStyle: SearchResultDisplayStyle.fromNativeValue(
|
||||
map['searchResultDisplayStyle'])!,
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"resultCount": resultCount,
|
||||
"highlightedResultIndex": highlightedResultIndex,
|
||||
"searchResultDisplayStyle": searchResultDisplayStyle.toNativeValue(),
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FindSession{resultCount: $resultCount, highlightedResultIndex: $highlightedResultIndex, searchResultDisplayStyle: $searchResultDisplayStyle}';
|
||||
}
|
||||
}
|
|
@ -161,3 +161,5 @@ export 'webview_package_info.dart' show WebViewPackageInfo, AndroidWebViewPackag
|
|||
export 'webview_render_process_action.dart' show WebViewRenderProcessAction;
|
||||
export 'window_features.dart' show WindowFeatures, IOSWKWindowFeatures;
|
||||
export 'requested_with_header_mode.dart' show RequestedWithHeaderMode;
|
||||
export 'find_session.dart' show FindSession;
|
||||
export 'search_result_display_style.dart' show SearchResultDisplayStyle;
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||
|
||||
part 'search_result_display_style.g.dart';
|
||||
|
||||
///Constants that describe the results summary the find panel UI includes.
|
||||
@ExchangeableEnum()
|
||||
class SearchResultDisplayStyle_ {
|
||||
// ignore: unused_field
|
||||
final int _value;
|
||||
const SearchResultDisplayStyle_._internal(this._value);
|
||||
|
||||
///The find panel includes the total number of results the session reports and the index of the target result.
|
||||
static const CURRENT_AND_TOTAL = const SearchResultDisplayStyle_._internal(0);
|
||||
|
||||
///The find panel includes the total number of results the session reports.
|
||||
static const TOTAL = const SearchResultDisplayStyle_._internal(1);
|
||||
|
||||
///The find panel doesn’t include the number of results the session reports.
|
||||
static const NONE = const SearchResultDisplayStyle_._internal(2);
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'search_result_display_style.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// ExchangeableEnumGenerator
|
||||
// **************************************************************************
|
||||
|
||||
///Constants that describe the results summary the find panel UI includes.
|
||||
class SearchResultDisplayStyle {
|
||||
final int _value;
|
||||
final int _nativeValue;
|
||||
const SearchResultDisplayStyle._internal(this._value, this._nativeValue);
|
||||
// ignore: unused_element
|
||||
factory SearchResultDisplayStyle._internalMultiPlatform(
|
||||
int value, Function nativeValue) =>
|
||||
SearchResultDisplayStyle._internal(value, nativeValue());
|
||||
|
||||
///The find panel includes the total number of results the session reports and the index of the target result.
|
||||
static const CURRENT_AND_TOTAL = SearchResultDisplayStyle._internal(0, 0);
|
||||
|
||||
///The find panel includes the total number of results the session reports.
|
||||
static const TOTAL = SearchResultDisplayStyle._internal(1, 1);
|
||||
|
||||
///The find panel doesn’t include the number of results the session reports.
|
||||
static const NONE = SearchResultDisplayStyle._internal(2, 2);
|
||||
|
||||
///Set of all values of [SearchResultDisplayStyle].
|
||||
static final Set<SearchResultDisplayStyle> values = [
|
||||
SearchResultDisplayStyle.CURRENT_AND_TOTAL,
|
||||
SearchResultDisplayStyle.TOTAL,
|
||||
SearchResultDisplayStyle.NONE,
|
||||
].toSet();
|
||||
|
||||
///Gets a possible [SearchResultDisplayStyle] instance from [int] value.
|
||||
static SearchResultDisplayStyle? fromValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return SearchResultDisplayStyle.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets a possible [SearchResultDisplayStyle] instance from a native value.
|
||||
static SearchResultDisplayStyle? fromNativeValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return SearchResultDisplayStyle.values
|
||||
.firstWhere((element) => element.toNativeValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets [int] value.
|
||||
int toValue() => _value;
|
||||
|
||||
///Gets [int] native value.
|
||||
int toNativeValue() => _nativeValue;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (_value) {
|
||||
case 0:
|
||||
return 'CURRENT_AND_TOTAL';
|
||||
case 1:
|
||||
return 'TOTAL';
|
||||
case 2:
|
||||
return 'NONE';
|
||||
}
|
||||
return _value.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue