added other iOS ChannelDelegate implementations, added ChromeSafariBrowserManager.browsers map native code

This commit is contained in:
Lorenzo Pichilli 2022-05-06 00:16:00 +02:00
parent 9af4aa032b
commit 66add9f8ac
11 changed files with 195 additions and 108 deletions

View File

@ -14,7 +14,6 @@ public class ActionBroadcastReceiver extends BroadcastReceiver {
protected static final String LOG_TAG = "ActionBroadcastReceiver";
public static final String KEY_ACTION_ID = "com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ACTION_ID";
public static final String KEY_ACTION_VIEW_ID = "com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ACTION_VIEW_ID";
public static final String CHROME_MANAGER_ID = "com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.CHROME_MANAGER_ID";
public static final String KEY_URL_TITLE = "android.intent.extra.SUBJECT";
@Override
@ -26,15 +25,10 @@ public class ActionBroadcastReceiver extends BroadcastReceiver {
int id = b.getInt(KEY_ACTION_ID);
String title = b.getString(KEY_URL_TITLE);
String managerId = b.getString(CHROME_MANAGER_ID);
ChromeSafariBrowserManager manager = (ChromeSafariBrowserManager) ChromeSafariBrowserManager.shared.get(managerId);
MethodChannel channel = new MethodChannel(manager.plugin.messenger, ChromeCustomTabsActivity.METHOD_CHANNEL_NAME_PREFIX + viewId);
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("title", title);
obj.put("id", id);
channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj);
ChromeCustomTabsActivity browser = ChromeSafariBrowserManager.browsers.get(viewId);
if (browser != null && browser.channelDelegate != null) {
browser.channelDelegate.onChromeSafariBrowserItemActionPerform(id, url, title);
}
}
}
}

View File

@ -9,8 +9,9 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsCallback;
@ -19,6 +20,7 @@ import androidx.browser.customtabs.CustomTabsService;
import androidx.browser.customtabs.CustomTabsSession;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsActionButton;
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsMenuItem;
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
@ -28,7 +30,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class ChromeCustomTabsActivity extends Activity implements Disposable {
@ -54,6 +55,7 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
@Nullable
public ChromeCustomTabsChannelDelegate channelDelegate;
@CallSuper
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -69,6 +71,8 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
manager = ChromeSafariBrowserManager.shared.get(managerId);
if (manager == null || manager.plugin == null|| manager.plugin.messenger == null) return;
ChromeSafariBrowserManager.browsers.put(id, this);
MethodChannel channel = new MethodChannel(manager.plugin.messenger, METHOD_CHANNEL_NAME_PREFIX + id);
channelDelegate = new ChromeCustomTabsChannelDelegate(this, channel);
@ -230,7 +234,6 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
Bundle extras = new Bundle();
extras.putInt(ActionBroadcastReceiver.KEY_ACTION_ID, actionSourceId);
extras.putString(ActionBroadcastReceiver.KEY_ACTION_VIEW_ID, id);
extras.putString(ActionBroadcastReceiver.CHROME_MANAGER_ID, manager.id);
actionIntent.putExtras(extras);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@ -244,14 +247,21 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
@Override
public void dispose() {
onStop();
onDestroy();
if (channelDelegate != null) {
channelDelegate.dispose();
channelDelegate = null;
}
if (ChromeSafariBrowserManager.browsers.containsKey(id)) {
ChromeSafariBrowserManager.browsers.put(id, null);
}
manager = null;
}
public void close() {
onStop();
onDestroy();
customTabsSession = null;
finish();
if (channelDelegate != null) {

View File

@ -75,6 +75,16 @@ public class ChromeCustomTabsChannelDelegate extends ChannelDelegateImpl {
channel.invokeMethod("onChromeSafariBrowserClosed", obj);
}
public void onChromeSafariBrowserItemActionPerform(int id, String url, String title) {
MethodChannel channel = getChannel();
if (channel == null) return;
Map<String, Object> obj = new HashMap<>();
obj.put("id", id);
obj.put("url", url);
obj.put("title", title);
channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj);
}
@Override
public void dispose() {
super.dispose();

View File

@ -8,9 +8,11 @@ import androidx.annotation.Nullable;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.Util;
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView;
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -27,6 +29,7 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
public InAppWebViewFlutterPlugin plugin;
public String id;
public static final Map<String, ChromeSafariBrowserManager> shared = new HashMap<>();
public static final Map<String, ChromeCustomTabsActivity> browsers = new HashMap<>();
public ChromeSafariBrowserManager(final InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
@ -37,7 +40,7 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
@Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
final String id = (String) call.argument("id");
final String viewId = (String) call.argument("id");
switch (call.method) {
case "open":
@ -46,7 +49,7 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
HashMap<String, Object> settings = (HashMap<String, Object>) call.argument("settings");
HashMap<String, Object> actionButton = (HashMap<String, Object>) call.argument("actionButton");
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
open(plugin.activity, id, url, settings, actionButton, menuItemList, result);
open(plugin.activity, viewId, url, settings, actionButton, menuItemList, result);
} else {
result.success(false);
}
@ -63,7 +66,7 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
}
}
public void open(Activity activity, String id, String url, HashMap<String, Object> settings,
public void open(Activity activity, String viewId, String url, HashMap<String, Object> settings,
HashMap<String, Object> actionButton,
List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
@ -71,7 +74,7 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
Bundle extras = new Bundle();
extras.putString("url", url);
extras.putBoolean("isData", false);
extras.putString("id", id);
extras.putString("id", viewId);
extras.putString("managerId", this.id);
extras.putSerializable("settings", settings);
extras.putSerializable("actionButton", (Serializable) actionButton);
@ -99,6 +102,14 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
@Override
public void dispose() {
super.dispose();
Collection<ChromeCustomTabsActivity> browserList = browsers.values();
for (ChromeCustomTabsActivity browser : browserList) {
if (browser != null) {
browser.close();
browser.dispose();
}
}
browsers.clear();
shared.remove(this.id);
plugin = null;
}

View File

@ -96,7 +96,7 @@ public class HeadlessInAppWebView implements Disposable {
HeadlessInAppWebViewManager.webViews.put(id, null);
}
if (plugin != null && plugin.activity != null) {
ViewGroup contentView = (ViewGroup) plugin.activity.findViewById(android.R.id.content);
ViewGroup contentView = plugin.activity.findViewById(android.R.id.content);
if (contentView != null) {
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
if (mainView != null && flutterWebView != null) {

View File

@ -9,12 +9,12 @@ import Foundation
public class HeadlessWebViewChannelDelegate : ChannelDelegate {
private var headlessWebView: HeadlessInAppWebView?
public init(headlessWebView: HeadlessInAppWebView, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.headlessWebView = headlessWebView
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
@ -46,14 +46,14 @@ public class HeadlessWebViewChannelDelegate : ChannelDelegate {
break
}
}
public func onWebViewCreated() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onWebViewCreated", arguments: arguments)
}
public override func dispose() {
super.dispose()
headlessWebView = nil
}
}
}

View File

@ -0,0 +1,24 @@
//
// InAppBrowserChannelDelegate.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 05/05/22.
//
import Foundation
public class InAppBrowserChannelDelegate : ChannelDelegate {
public override init(channel: FlutterMethodChannel) {
super.init(channel: channel)
}
public func onBrowserCreated() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onBrowserCreated", arguments: arguments)
}
public func onExit() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onExit", arguments: arguments)
}
}

View File

@ -10,7 +10,8 @@ import UIKit
import WebKit
import Foundation
public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelegate, UIScrollViewDelegate, WKUIDelegate, UISearchBarDelegate {
public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelegate, UIScrollViewDelegate, UISearchBarDelegate, Disposable {
static var METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_";
var closeButton: UIBarButtonItem!
var reloadButton: UIBarButtonItem!
@ -24,7 +25,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
var id: String = ""
var windowId: Int64?
var webView: InAppWebView!
var channel: FlutterMethodChannel?
var channelDelegate: InAppBrowserChannelDelegate?
var initialUrlRequest: URLRequest?
var initialFile: String?
var contextMenu: [String: Any]?
@ -40,7 +41,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
var methodCallDelegate: InAppWebViewMethodHandler?
public override func loadView() {
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser_" + id, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
let channel = FlutterMethodChannel(name: InAppBrowserWebViewController.METHOD_CHANNEL_NAME_PREFIX + id, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
channelDelegate = InAppBrowserChannelDelegate(channel: channel)
var userScripts: [UserScript] = []
for intialUserScript in initialUserScripts {
@ -51,19 +53,19 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
if let wId = windowId, let webViewTransport = InAppWebView.windowWebViews[wId] {
webView = webViewTransport.webView
webView.contextMenu = contextMenu
webView.channel = channel!
webView.channel = channel
webView.initialUserScripts = userScripts
} else {
webView = InAppWebView(frame: .zero,
configuration: preWebviewConfiguration,
contextMenu: contextMenu,
channel: channel!,
channel: channel,
userScripts: userScripts)
}
webView.inAppBrowserDelegate = self
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
channel.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
let pullToRefreshSettings = PullToRefreshSettings()
let _ = pullToRefreshSettings.parse(settings: pullToRefreshInitialSettings)
@ -175,7 +177,8 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
webView.loadUrl(urlRequest: initialUrlRequest, allowingReadAccessTo: allowingReadAccessToURL)
}
onBrowserCreated()
channelDelegate?.onBrowserCreated()
}
deinit {
@ -549,9 +552,9 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
}
public func dispose() {
onExit()
channel?.setMethodCallHandler(nil)
channel = nil
channelDelegate?.onExit()
channelDelegate?.dispose()
channelDelegate = nil
webView?.dispose()
webView = nil
view = nil
@ -568,12 +571,4 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
methodCallDelegate?.webView = nil
methodCallDelegate = nil
}
public func onBrowserCreated() {
channel?.invokeMethod("onBrowserCreated", arguments: [])
}
public func onExit() {
channel?.invokeMethod("onExit", arguments: [])
}
}

View File

@ -15,6 +15,7 @@ import SafariServices
public class ChromeSafariBrowserManager: ChannelDelegate {
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_chromesafaribrowser"
static var registrar: FlutterPluginRegistrar?
static var browsers: [String: SafariViewController?] = [:]
init(registrar: FlutterPluginRegistrar) {
super.init(channel: FlutterMethodChannel(name: ChromeSafariBrowserManager.METHOD_CHANNEL_NAME, binaryMessenger: registrar.messenger()))
@ -63,17 +64,14 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
config.entersReaderIfAvailable = safariSettings.entersReaderIfAvailable
config.barCollapsingEnabled = safariSettings.barCollapsingEnabled
safari = SafariViewController(url: absoluteUrl, configuration: config)
safari = SafariViewController(id: id, url: absoluteUrl, configuration: config,
menuItemList: menuItemList, safariSettings: safariSettings)
} else {
// Fallback on earlier versions
safari = SafariViewController(url: absoluteUrl)
safari = SafariViewController(id: id, url: absoluteUrl, entersReaderIfAvailable: safariSettings.entersReaderIfAvailable,
menuItemList: menuItemList, safariSettings: safariSettings)
}
safari.id = id
safari.menuItemList = menuItemList
safari.prepareMethodChannel()
safari.delegate = safari
safari.safariSettings = safariSettings
safari.prepareSafariBrowser()
flutterViewController.present(safari, animated: true) {
@ -89,5 +87,11 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
public override func dispose() {
super.dispose()
ChromeSafariBrowserManager.registrar = nil
let browsers = ChromeSafariBrowserManager.browsers.values
browsers.forEach { (browser: SafariViewController?) in
browser?.close(result: nil)
browser?.dispose()
}
ChromeSafariBrowserManager.browsers.removeAll()
}
}

View File

@ -9,15 +9,34 @@ import Foundation
import SafariServices
@available(iOS 9.0, *)
public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafariViewControllerDelegate {
var channel: FlutterMethodChannel?
var safariSettings: SafariBrowserSettings?
var id: String = ""
public class SafariViewController: SFSafariViewController, SFSafariViewControllerDelegate, Disposable {
static let METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_chromesafaribrowser_"
var channelDelegate: SafariViewControllerChannelDelegate?
var safariSettings: SafariBrowserSettings
var id: String
var menuItemList: [[String: Any]] = []
public static func register(with registrar: FlutterPluginRegistrar) {
@available(iOS 11.0, *)
public init(id: String, url: URL, configuration: SFSafariViewController.Configuration, menuItemList: [[String: Any]] = [], safariSettings: SafariBrowserSettings) {
self.id = id
self.menuItemList = menuItemList
self.safariSettings = safariSettings
super.init(url: url, configuration: configuration)
let channel = FlutterMethodChannel(name: SafariViewController.METHOD_CHANNEL_NAME_PREFIX + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channelDelegate = SafariViewControllerChannelDelegate(safariViewController: self, channel: channel)
self.delegate = self
}
public init(id: String, url: URL, entersReaderIfAvailable: Bool, menuItemList: [[String: Any]] = [], safariSettings: SafariBrowserSettings) {
self.id = id
self.menuItemList = menuItemList
self.safariSettings = safariSettings
super.init(url: url, entersReaderIfAvailable: entersReaderIfAvailable)
let channel = FlutterMethodChannel(name: SafariViewController.METHOD_CHANNEL_NAME_PREFIX + id,
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
self.channelDelegate = SafariViewControllerChannelDelegate(safariViewController: self, channel: channel)
self.delegate = self
}
deinit {
@ -25,41 +44,20 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
dispose()
}
public func prepareMethodChannel() {
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + id, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
SwiftFlutterPlugin.instance!.registrar!.addMethodCallDelegate(self, channel: channel!)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
// let arguments = call.arguments as? NSDictionary
switch call.method {
case "close":
close(result: result)
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public override func viewWillAppear(_ animated: Bool) {
// prepareSafariBrowser()
super.viewWillAppear(animated)
onChromeSafariBrowserOpened()
channelDelegate?.onChromeSafariBrowserOpened()
}
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.onChromeSafariBrowserClosed()
channelDelegate?.onChromeSafariBrowserClosed()
self.dispose()
}
func prepareSafariBrowser() {
guard let safariSettings = safariSettings else {
return
}
if #available(iOS 11.0, *) {
self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: safariSettings.dismissButtonStyle)!
}
@ -82,8 +80,8 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
// wait for the animation
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400), execute: {() -> Void in
if result != nil {
result!(true)
if let result = result {
result(true)
}
})
}
@ -95,7 +93,7 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
public func safariViewController(_ controller: SFSafariViewController,
didCompleteInitialLoad didLoadSuccessfully: Bool) {
if didLoadSuccessfully {
onChromeSafariBrowserCompletedInitialLoad()
channelDelegate?.onChromeSafariBrowserCompletedInitialLoad()
}
else {
print("Cant load successfully the 'SafariViewController'.")
@ -123,22 +121,11 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
// print(URL)
// }
public func onChromeSafariBrowserOpened() {
channel?.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
}
public func onChromeSafariBrowserCompletedInitialLoad() {
channel?.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: [])
}
public func onChromeSafariBrowserClosed() {
channel?.invokeMethod("onChromeSafariBrowserClosed", arguments: [])
}
public func dispose() {
channel?.setMethodCallHandler(nil)
channel = nil
channelDelegate?.dispose()
channelDelegate = nil
delegate = nil
ChromeSafariBrowserManager.browsers[id] = nil
}
}
@ -182,18 +169,7 @@ class CustomUIActivity : UIActivity {
}
override func perform() {
guard let registrar = SwiftFlutterPlugin.instance?.registrar else {
return
}
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId,
binaryMessenger: registrar.messenger())
let arguments: [String: Any?] = [
"url": url.absoluteString,
"title": title,
"id": id,
]
channel.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", arguments: arguments)
let browser = ChromeSafariBrowserManager.browsers[viewId]
browser??.channelDelegate?.onChromeSafariBrowserMenuItemActionPerform(id: id, url: url, title: title)
}
}

View File

@ -0,0 +1,63 @@
//
// SafariViewControllerChannelDelegate.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 05/05/22.
//
import Foundation
public class SafariViewControllerChannelDelegate : ChannelDelegate {
private var safariViewController: SafariViewController?
public init(safariViewController: SafariViewController, channel: FlutterMethodChannel) {
super.init(channel: channel)
self.safariViewController = safariViewController
}
public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
// let arguments = call.arguments as? NSDictionary
switch call.method {
case "close":
if let safariViewController = safariViewController {
safariViewController.close(result: result)
} else {
result(false)
}
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func onChromeSafariBrowserOpened() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onChromeSafariBrowserOpened", arguments: arguments)
}
public func onChromeSafariBrowserCompletedInitialLoad() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: arguments)
}
public func onChromeSafariBrowserClosed() {
let arguments: [String: Any?] = [:]
channel?.invokeMethod("onChromeSafariBrowserClosed", arguments: arguments)
}
public func onChromeSafariBrowserMenuItemActionPerform(id: Int64, url: URL, title: String?) {
let arguments: [String: Any?] = [
"id": id,
"url": url.absoluteString,
"title": title,
]
channel?.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", arguments: arguments)
}
public override func dispose() {
super.dispose()
safariViewController = nil
}
}