diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bcddb06..731f547a 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ - Added support for `onPermissionRequest` event on iOS 15.0+ - Updated `getMetaThemeColor` on iOS 15.0+ +## 5.4.2 + +- Added `setActionButton` method to `ChromeSafariBrowser` class + +## 5.4.1+2 + +- Fixed "Android ServiceWorkerControllerCompat.setServiceWorkerClient(null) makes Webivew Plugin Crashes" [#1151](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1151) + ## 5.4.1+1 - Fixed Android default context menu over custom context menu on API Level 31+ diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java index 3b26a7a7..f032ee77 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/ServiceWorkerManager.java @@ -118,7 +118,9 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { private void setServiceWorkerClient(Boolean isNull) { if (serviceWorkerController != null) { - serviceWorkerController.setServiceWorkerClient(isNull ? null : new ServiceWorkerClientCompat() { + // set ServiceWorkerClient as null makes the app crashes, so just set a dummy ServiceWorkerClientCompat. + // https://github.com/pichillilorenzo/flutter_inappwebview/issues/1151 + serviceWorkerController.setServiceWorkerClient(isNull ? dummyServiceWorkerClientCompat() : new ServiceWorkerClientCompat() { @Nullable @Override public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) { @@ -165,10 +167,20 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler { } } + private ServiceWorkerClientCompat dummyServiceWorkerClientCompat() { + return new ServiceWorkerClientCompat() { + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) { + return null; + } + }; + } + public void dispose() { channel.setMethodCallHandler(null); if (serviceWorkerController != null) { - serviceWorkerController.setServiceWorkerClient(null); + serviceWorkerController.setServiceWorkerClient(dummyServiceWorkerClientCompat()); serviceWorkerController = null; } plugin = null; diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java index f51d857c..1d3bb700 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ActionBroadcastReceiver.java @@ -34,7 +34,7 @@ public class ActionBroadcastReceiver extends BroadcastReceiver { obj.put("url", url); obj.put("title", title); obj.put("id", id); - channel.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", obj); + channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj); } } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java index 534c7a8f..ddb51997 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeCustomTabsActivity.java @@ -3,11 +3,14 @@ package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import androidx.annotation.Nullable; import androidx.browser.customtabs.CustomTabColorSchemeParams; import androidx.browser.customtabs.CustomTabsCallback; import androidx.browser.customtabs.CustomTabsIntent; @@ -15,7 +18,10 @@ import androidx.browser.customtabs.CustomTabsService; import androidx.browser.customtabs.CustomTabsSession; import com.pichillilorenzo.flutter_inappwebview.R; +import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsActionButton; +import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsMenuItem; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,6 +42,10 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. protected boolean onChromeSafariBrowserOpened = false; protected boolean onChromeSafariBrowserCompletedInitialLoad = false; public ChromeSafariBrowserManager manager; + public String initialUrl; + public List menuItems = new ArrayList<>(); + @Nullable + public CustomTabsActionButton actionButton; @Override protected void onCreate(Bundle savedInstanceState) { @@ -54,12 +64,15 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. channel = new MethodChannel(manager.plugin.messenger, "com.pichillilorenzo/flutter_chromesafaribrowser_" + id); channel.setMethodCallHandler(this); - final String url = b.getString("url"); + initialUrl = b.getString("url"); customSettings = new ChromeCustomTabsSettings(); customSettings.parse((HashMap) b.getSerializable("settings")); - - final List> menuItemList = (List>) b.getSerializable("menuItemList"); + actionButton = CustomTabsActionButton.fromMap((Map) b.getSerializable("actionButton")); + List> menuItemList = (List>) b.getSerializable("menuItemList"); + for (Map menuItem : menuItemList) { + menuItems.add(CustomTabsMenuItem.fromMap(menuItem)); + } final ChromeCustomTabsActivity chromeCustomTabsActivity = this; @@ -67,7 +80,7 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. customTabActivityHelper.setConnectionCallback(new CustomTabActivityHelper.ConnectionCallback() { @Override public void onCustomTabsConnected() { - customTabsConnected(url, menuItemList); + customTabsConnected(); } @Override @@ -139,13 +152,13 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. } } - public void customTabsConnected (String url, List> menuItemList) { + public void customTabsConnected() { customTabsSession = customTabActivityHelper.getSession(); - Uri uri = Uri.parse(url); + Uri uri = Uri.parse(initialUrl); customTabActivityHelper.mayLaunchUrl(uri, null, null); builder = new CustomTabsIntent.Builder(customTabsSession); - prepareCustomTabs(menuItemList); + prepareCustomTabs(); CustomTabsIntent customTabsIntent = builder.build(); prepareCustomTabsIntent(customTabsIntent); @@ -153,7 +166,7 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. CustomTabActivityHelper.openCustomTab(this, customTabsIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE); } - private void prepareCustomTabs(List> menuItemList) { + private void prepareCustomTabs() { if (customSettings.addDefaultShareMenuItem != null) { builder.setShareState(customSettings.addDefaultShareMenuItem ? CustomTabsIntent.SHARE_STATE_ON : CustomTabsIntent.SHARE_STATE_OFF); @@ -172,10 +185,21 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel. builder.setUrlBarHidingEnabled(customSettings.enableUrlBarHiding); builder.setInstantAppsEnabled(customSettings.instantAppsEnabled); - for (HashMap menuItem : menuItemList) { - int id = (int) menuItem.get("id"); - String label = (String) menuItem.get("label"); - builder.addMenuItem(label, createPendingIntent(id)); + for (CustomTabsMenuItem menuItem : menuItems) { + builder.addMenuItem(menuItem.getLabel(), + createPendingIntent(menuItem.getId())); + } + + if (actionButton != null) { + byte[] data = actionButton.getIcon(); + BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inMutable = true; + Bitmap bmp = BitmapFactory.decodeByteArray( + data, 0, data.length, bitmapOptions + ); + builder.setActionButton(bmp, actionButton.getDescription(), + createPendingIntent(actionButton.getId()), + actionButton.isShouldTint()); } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java index 574150bb..ea585359 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/ChromeSafariBrowserManager.java @@ -41,15 +41,22 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl switch (call.method) { case "open": - { + if (plugin != null) { String url = (String) call.argument("url"); HashMap settings = (HashMap) call.argument("settings"); + HashMap actionButton = (HashMap) call.argument("actionButton"); List> menuItemList = (List>) call.argument("menuItemList"); - open(plugin.activity, id, url, settings, menuItemList, result); + open(plugin.activity, id, url, settings, actionButton, menuItemList, result); + } else { + result.success(false); } break; case "isAvailable": - result.success(CustomTabActivityHelper.isAvailable(plugin.activity)); + if (plugin != null) { + result.success(CustomTabActivityHelper.isAvailable(plugin.activity)); + } else { + result.success(false); + } break; default: result.notImplemented(); @@ -57,6 +64,7 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl } public void open(Activity activity, String id, String url, HashMap settings, + HashMap actionButton, List> menuItemList, MethodChannel.Result result) { Intent intent = null; @@ -66,6 +74,7 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl extras.putString("id", id); extras.putString("managerId", this.id); extras.putSerializable("settings", settings); + extras.putSerializable("actionButton", (Serializable) actionButton); extras.putSerializable("menuItemList", (Serializable) menuItemList); Boolean isSingleInstance = (Boolean) Util.getOrDefault(settings, "isSingleInstance", false); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java index 72bcf3f8..38b729f2 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/chrome_custom_tabs/TrustedWebActivity.java @@ -18,9 +18,9 @@ public class TrustedWebActivity extends ChromeCustomTabsActivity { public TrustedWebActivityIntentBuilder builder; @Override - public void customTabsConnected (String url, List> menuItemList) { + public void customTabsConnected() { customTabsSession = customTabActivityHelper.getSession(); - Uri uri = Uri.parse(url); + Uri uri = Uri.parse(initialUrl); customTabActivityHelper.mayLaunchUrl(uri, null, null); builder = new TrustedWebActivityIntentBuilder(uri); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsActionButton.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsActionButton.java new file mode 100644 index 00000000..b95b764c --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsActionButton.java @@ -0,0 +1,104 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Arrays; +import java.util.Map; + +public class CustomTabsActionButton { + private int id; + + @NonNull + private byte[] icon; + + @NonNull + private String description; + + private boolean shouldTint; + + public CustomTabsActionButton(int id, @NonNull byte[] icon, @NonNull String description, boolean shouldTint) { + this.id = id; + this.icon = icon; + this.description = description; + this.shouldTint = shouldTint; + } + + @Nullable + public static CustomTabsActionButton fromMap(@Nullable Map map) { + if (map == null) { + return null; + } + int id = (int) map.get("id"); + byte[] icon = (byte[]) map.get("icon"); + String description = (String) map.get("description"); + boolean shouldTint = (boolean) map.get("shouldTint"); + return new CustomTabsActionButton(id, icon, description, shouldTint); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @NonNull + public byte[] getIcon() { + return icon; + } + + public void setIcon(@NonNull byte[] icon) { + this.icon = icon; + } + + @NonNull + public String getDescription() { + return description; + } + + public void setDescription(@NonNull String description) { + this.description = description; + } + + public boolean isShouldTint() { + return shouldTint; + } + + public void setShouldTint(boolean shouldTint) { + this.shouldTint = shouldTint; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CustomTabsActionButton that = (CustomTabsActionButton) o; + + if (id != that.id) return false; + if (shouldTint != that.shouldTint) return false; + if (!Arrays.equals(icon, that.icon)) return false; + return description.equals(that.description); + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + Arrays.hashCode(icon); + result = 31 * result + description.hashCode(); + result = 31 * result + (shouldTint ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "CustomTabsActionButton{" + + "id=" + id + + ", icon=" + Arrays.toString(icon) + + ", description='" + description + '\'' + + ", shouldTint=" + shouldTint + + '}'; + } +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsMenuItem.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsMenuItem.java new file mode 100644 index 00000000..c00b4da0 --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/CustomTabsMenuItem.java @@ -0,0 +1,72 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Arrays; +import java.util.Map; + +public class CustomTabsMenuItem { + private int id; + + @NonNull + private String label; + + public CustomTabsMenuItem(int id, @NonNull String label) { + this.id = id; + this.label = label; + } + + @Nullable + public static CustomTabsMenuItem fromMap(@Nullable Map map) { + if (map == null) { + return null; + } + int id = (int) map.get("id"); + String label = (String) map.get("label"); + return new CustomTabsMenuItem(id, label); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @NonNull + public String getLabel() { + return label; + } + + public void setLabel(@NonNull String label) { + this.label = label; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CustomTabsMenuItem that = (CustomTabsMenuItem) o; + + if (id != that.id) return false; + return label.equals(that.label); + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + label.hashCode(); + return result; + } + + @Override + public String toString() { + return "CustomTabsMenuItem{" + + "id=" + id + + ", label='" + label + '\'' + + '}'; + } +} diff --git a/example/integration_test/webview_flutter_test.dart b/example/integration_test/webview_flutter_test.dart index 7d011eb3..31b6e369 100644 --- a/example/integration_test/webview_flutter_test.dart +++ b/example/integration_test/webview_flutter_test.dart @@ -4148,7 +4148,7 @@ setTimeout(function() { if (Platform.isAndroid) { await pageLoaded.future; expect(await controller.evaluateJavascript(source: "document.body"), - isNull); + isEmpty); } else if (Platform.isIOS) { expect(pageLoaded.future, doesNotComplete); } @@ -5449,7 +5449,7 @@ setTimeout(function() { }); group('Service Worker', () { - testWidgets('AndroidInAppWebViewController', (WidgetTester tester) async { + testWidgets('shouldInterceptRequest', (WidgetTester tester) async { final Completer completer = Completer(); var swAvailable = await AndroidWebViewFeature.isFeatureSupported( @@ -5487,6 +5487,39 @@ setTimeout(function() { expect(completer.future, completes); }, skip: !Platform.isAndroid); + + testWidgets('setServiceWorkerClient to null', (WidgetTester tester) async { + final Completer pageLoaded = Completer(); + + var swAvailable = await AndroidWebViewFeature.isFeatureSupported( + AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE); + var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported( + AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST); + + if (swAvailable && swInterceptAvailable) { + AndroidServiceWorkerController serviceWorkerController = + AndroidServiceWorkerController.instance(); + + await serviceWorkerController.setServiceWorkerClient(null); + } + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: InAppWebView( + key: GlobalKey(), + initialUrlRequest: + URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')), + onLoadStop: (controller, url) { + pageLoaded.complete(url!.toString()); + }, + ), + ), + ); + + final String url = await pageLoaded.future; + expect(url, "https://mdn.github.io/sw-test/"); + }, skip: !Platform.isAndroid); }); group('Cookie Manager', () { @@ -5814,7 +5847,57 @@ setTimeout(function() { expect(chromeSafariBrowser.isOpened(), false); }); + test('add custom menu item', () async { + var chromeSafariBrowser = new MyChromeSafariBrowser(); + chromeSafariBrowser.addMenuItem(ChromeSafariBrowserMenuItem( + id: 2, + label: 'Custom item menu 1', + action: (url, title) { + print('Custom item menu 1 clicked!'); + })); + expect(chromeSafariBrowser.isOpened(), false); + + await chromeSafariBrowser.open( + url: Uri.parse("https://github.com/flutter")); + await chromeSafariBrowser.browserCreated.future; + expect(chromeSafariBrowser.isOpened(), true); + expect(() async { + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); + }, throwsA(isInstanceOf())); + + await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); + await chromeSafariBrowser.close(); + await chromeSafariBrowser.browserClosed.future; + expect(chromeSafariBrowser.isOpened(), false); + }); + group('Android Custom Tabs', () { + test('add custom action button', () async { + var chromeSafariBrowser = new MyChromeSafariBrowser(); + var actionButtonIcon = await rootBundle.load('test_assets/images/flutter-logo.png'); + chromeSafariBrowser.setActionButton(ChromeSafariBrowserActionButton( + id: 1, + description: 'Action Button description', + icon: actionButtonIcon.buffer.asUint8List(), + action: (url, title) { + print('Action Button 1 clicked!'); + })); + expect(chromeSafariBrowser.isOpened(), false); + + await chromeSafariBrowser.open( + url: Uri.parse("https://github.com/flutter")); + await chromeSafariBrowser.browserCreated.future; + expect(chromeSafariBrowser.isOpened(), true); + expect(() async { + await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev")); + }, throwsA(isInstanceOf())); + + await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes); + await chromeSafariBrowser.close(); + await chromeSafariBrowser.browserClosed.future; + expect(chromeSafariBrowser.isOpened(), false); + }); + test('Custom Tabs single instance', () async { var chromeSafariBrowser = new MyChromeSafariBrowser(); expect(chromeSafariBrowser.isOpened(), false); diff --git a/example/lib/chrome_safari_browser_example.screen.dart b/example/lib/chrome_safari_browser_example.screen.dart index b21f569a..ce94fa79 100755 --- a/example/lib/chrome_safari_browser_example.screen.dart +++ b/example/lib/chrome_safari_browser_example.screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'main.dart'; @@ -32,8 +33,20 @@ class _ChromeSafariBrowserExampleScreenState extends State { @override void initState() { + rootBundle.load('assets/images/flutter-logo.png').then((actionButtonIcon) { + widget.browser.setActionButton(ChromeSafariBrowserActionButton( + id: 1, + description: 'Action Button description', + icon: actionButtonIcon.buffer.asUint8List(), + action: (url, title) { + print('Action Button 1 clicked!'); + print(url); + print(title); + })); + }); + widget.browser.addMenuItem(ChromeSafariBrowserMenuItem( - id: 1, + id: 2, label: 'Custom item menu 1', action: (url, title) { print('Custom item menu 1 clicked!'); @@ -41,7 +54,7 @@ class _ChromeSafariBrowserExampleScreenState print(title); })); widget.browser.addMenuItem(ChromeSafariBrowserMenuItem( - id: 2, + id: 3, label: 'Custom item menu 2', action: (url, title) { print('Custom item menu 2 clicked!'); diff --git a/lib/src/chrome_safari_browser/chrome_safari_browser.dart b/lib/src/chrome_safari_browser/chrome_safari_browser.dart index b172b287..b7e1b15f 100755 --- a/lib/src/chrome_safari_browser/chrome_safari_browser.dart +++ b/lib/src/chrome_safari_browser/chrome_safari_browser.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:collection'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/src/util.dart'; @@ -44,6 +45,7 @@ class ChromeSafariBrowser { ///View ID used internally. late final String id; + ChromeSafariBrowserActionButton? _actionButton; Map _menuItems = new HashMap(); bool _isOpened = false; late MethodChannel _channel; @@ -70,12 +72,14 @@ class ChromeSafariBrowser { onClosed(); this._isOpened = false; break; - case "onChromeSafariBrowserMenuItemActionPerform": + case "onChromeSafariBrowserItemActionPerform": String url = call.arguments["url"]; String title = call.arguments["title"]; int id = call.arguments["id"].toInt(); - if (this._menuItems[id] != null) { - this._menuItems[id]!.action(url, title); + if (this._actionButton?.id == id) { + this._actionButton?.action(url, title); + } else if (this._menuItems[id] != null) { + this._menuItems[id]?.action(url, title); } break; default: @@ -101,7 +105,7 @@ class ChromeSafariBrowser { List> menuItemList = []; _menuItems.forEach((key, value) { - menuItemList.add({"id": value.id, "label": value.label}); + menuItemList.add(value.toMap()); }); var initialSettings = settings?.toMap() ?? @@ -112,6 +116,7 @@ class ChromeSafariBrowser { args.putIfAbsent('id', () => id); args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('actionButton', () => _actionButton?.toMap()); args.putIfAbsent('menuItemList', () => menuItemList); await _sharedChannel.invokeMethod('open', args); this._isOpened = true; @@ -123,6 +128,16 @@ class ChromeSafariBrowser { await _channel.invokeMethod("close", args); } + ///Set a custom action button. + /// + ///**NOTE**: Not available in a Trusted Web Activity. + /// + ///**Supported Platforms/Implementations**: + ///- Android ([Official API - CustomTabsIntent.Builder.setActionButton ](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsIntent.Builder#setActionButton(android.graphics.Bitmap,%20java.lang.String,%20android.app.PendingIntent,%20boolean))) + void setActionButton(ChromeSafariBrowserActionButton actionButton) { + this._actionButton = actionButton; + } + ///Adds a [ChromeSafariBrowserMenuItem] to the menu. /// ///**NOTE**: Not available in an Android Trusted Web Activity. @@ -178,11 +193,56 @@ class ChromeSafariBrowser { } } +///Class that represents a custom action button for a [ChromeSafariBrowser] instance. +/// +///**NOTE**: Not available in an Android Trusted Web Activity. +class ChromeSafariBrowserActionButton { + ///The action button id. It should be different from the [ChromeSafariBrowserMenuItem.id]. + int id; + + ///The icon byte data. + Uint8List icon; + + ///The description for the button. To be used for accessibility. + String description; + + ///Whether the action button should be tinted. + bool shouldTint; + + ///Callback function to be invoked when the menu item is clicked + final void Function(String url, String title) action; + + ChromeSafariBrowserActionButton( + {required this.id, + required this.icon, + required this.description, + required this.action, + this.shouldTint = false}); + + Map toMap() { + return { + "id": id, + "icon": icon, + "description": description, + "shouldTint": shouldTint + }; + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} + ///Class that represents a custom menu item for a [ChromeSafariBrowser] instance. /// ///**NOTE**: Not available in an Android Trusted Web Activity. class ChromeSafariBrowserMenuItem { - ///The menu item id + ///The menu item id. It should be different from [ChromeSafariBrowserActionButton.id]. int id; ///The label of the menu item diff --git a/lib/src/types.dart b/lib/src/types.dart index 21d34307..63297d13 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4798,7 +4798,8 @@ class PermissionResourceType { static PermissionResourceType? fromValue(dynamic? value) { if (value != null) { try { - Set valueList = [].toSet(); + Set valueList = + [].toSet(); if (defaultTargetPlatform == TargetPlatform.android) { valueList = PermissionResourceType._androidValues; } else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {