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 _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 findAllAsync({required String find}) async { Map args = {}; 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 findNext({required bool forward}) async { Map args = {}; 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 clearMatches() async { Map args = {}; 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 setSearchText(String? searchText) async { Map args = {}; 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 getSearchText() async { Map args = {}; 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 isFindNavigatorVisible() async { Map args = {}; 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 updateResultCount() async { Map args = {}; 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 presentFindNavigator() async { Map args = {}; 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 dismissFindNavigator() async { Map args = {}; await _channel?.invokeMethod('dismissFindNavigator', args); } ///If there's a currently active find session (on iOS, implying [isFindNavigatorVisible] is `true`), returns the active find session. /// ///**NOTE**: available on iOS only if [InAppWebViewSettings.isFindInteractionEnabled] is `true`. /// ///**Supported Platforms/Implementations**: ///- Android native WebView ///- iOS ([Official API - UIFindInteraction.activeFindSession](https://developer.apple.com/documentation/uikit/uifindinteraction/3975825-activefindsession?changes=_7____4_8&language=objc)) Future getActiveFindSession() async { Map args = {}; Map? result = (await _channel?.invokeMethod('getActiveFindSession', args)) ?.cast(); return FindSession.fromMap(result); } }