Merge branch 'v6.0.0-beta.9'
This commit is contained in:
commit
52fb4b3263
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,3 +1,22 @@
|
||||||
|
## 6.0.0-beta.9
|
||||||
|
|
||||||
|
- Added `headers`, `otherLikelyURLs`, `referrer` arguments on `ChromeSafariBrowser.open` method for Android
|
||||||
|
- Added `onNavigationEvent`, `onServiceConnected`, `onRelationshipValidationResult` events on `ChromeSafariBrowser` for Android
|
||||||
|
- Added `mayLaunchUrl`, `launchUrl`, `updateActionButton`, `validateRelationship`, `setSecondaryToolbar`, `updateSecondaryToolbar` methods on `ChromeSafariBrowser` for Android
|
||||||
|
- Added `startAnimations`, `exitAnimations`, `navigationBarColor`, `navigationBarDividerColor`, `secondaryToolbarColor`, `alwaysUseBrowserUI` ChromeSafariBrowser settings for Android
|
||||||
|
- Added `ChromeSafariBrowserMenuItem.image` property for iOS
|
||||||
|
- Added `didLoadSuccessfully` optional argument on `ChromeSafariBrowser.onCompletedInitialLoad` event for iOS
|
||||||
|
- Added `onInitialLoadDidRedirect`, `onWillOpenInBrowser` events on `ChromeSafariBrowser` for iOS
|
||||||
|
- Added `activityButton`, `eventAttribution` ChromeSafariBrowser settings for iOS
|
||||||
|
- Added `clearWebsiteData`, `prewarmConnections`, `invalidatePrewarmingToken`, `getMaxToolbarItems` static methods on `ChromeSafariBrowser` for iOS
|
||||||
|
- Added `getVariationsHeader` WebView static method
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
- `ChromeSafariBrowser.onCompletedInitialLoad` event has an optional argument
|
||||||
|
- `ChromeSafariBrowserMenuItem.action` and `ChromeSafariBrowserActionButton.action` can be null
|
||||||
|
- All `ChromeSafariBrowserSettings` properties are optionals
|
||||||
|
|
||||||
## 6.0.0-beta.8
|
## 6.0.0-beta.8
|
||||||
|
|
||||||
- Merged "Exposed "shared" property of HttpServer bind method to support more use-cases." [#1395](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1395) (thanks to [LugonjaAleksandar](https://github.com/LugonjaAleksandar))
|
- Merged "Exposed "shared" property of HttpServer bind method to support more use-cases." [#1395](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1395) (thanks to [LugonjaAleksandar](https://github.com/LugonjaAleksandar))
|
||||||
|
@ -65,7 +84,7 @@
|
||||||
- Added `PullToRefreshController.isEnabled` method
|
- Added `PullToRefreshController.isEnabled` method
|
||||||
- Updated `getMetaThemeColor` on iOS 15.0+
|
- Updated `getMetaThemeColor` on iOS 15.0+
|
||||||
- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes
|
- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes
|
||||||
- Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes
|
- Deprecated `onLoadHttpError` for `onReceivedHttpError`. `onReceivedHttpError` will be called also for subframes
|
||||||
|
|
||||||
### BREAKING CHANGES
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,6 @@ import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
public class InAppWebViewFileProvider extends FileProvider {
|
public class InAppWebViewFileProvider extends FileProvider {
|
||||||
|
|
||||||
// This class intentionally left blank.
|
public static final String fileProviderAuthorityExtension = "flutter_inappwebview.fileprovider";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,14 @@ public class InAppWebViewStatic extends ChannelDelegateImpl {
|
||||||
}
|
}
|
||||||
result.success(true);
|
result.success(true);
|
||||||
break;
|
break;
|
||||||
|
case "getVariationsHeader":
|
||||||
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.GET_VARIATIONS_HEADER)) {
|
||||||
|
result.success(WebViewCompat.getVariationsHeader());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.success(null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
result.notImplemented();
|
result.notImplemented();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,9 @@ import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
|
||||||
|
|
||||||
public class ActionBroadcastReceiver extends BroadcastReceiver {
|
public class ActionBroadcastReceiver extends BroadcastReceiver {
|
||||||
protected static final String LOG_TAG = "ActionBroadcastReceiver";
|
protected static final String LOG_TAG = "ActionBroadcastReceiver";
|
||||||
|
@ -18,16 +16,25 @@ public class ActionBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
int clickedId = intent.getIntExtra(CustomTabsIntent.EXTRA_REMOTEVIEWS_CLICKED_ID, -1);
|
||||||
String url = intent.getDataString();
|
String url = intent.getDataString();
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
Bundle b = intent.getExtras();
|
Bundle b = intent.getExtras();
|
||||||
String viewId = b.getString(KEY_ACTION_VIEW_ID);
|
String viewId = b.getString(KEY_ACTION_VIEW_ID);
|
||||||
int id = b.getInt(KEY_ACTION_ID);
|
|
||||||
String title = b.getString(KEY_URL_TITLE);
|
|
||||||
|
|
||||||
ChromeCustomTabsActivity browser = ChromeSafariBrowserManager.browsers.get(viewId);
|
if (clickedId == -1) {
|
||||||
if (browser != null && browser.channelDelegate != null) {
|
int id = b.getInt(KEY_ACTION_ID);
|
||||||
browser.channelDelegate.onChromeSafariBrowserItemActionPerform(id, url, title);
|
String title = b.getString(KEY_URL_TITLE);
|
||||||
|
|
||||||
|
ChromeCustomTabsActivity browser = ChromeSafariBrowserManager.browsers.get(viewId);
|
||||||
|
if (browser != null && browser.channelDelegate != null) {
|
||||||
|
browser.channelDelegate.onItemActionPerform(id, url, title);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ChromeCustomTabsActivity browser = ChromeSafariBrowserManager.browsers.get(viewId);
|
||||||
|
if (browser != null && browser.channelDelegate != null) {
|
||||||
|
browser.channelDelegate.onSecondaryItemActionPerform(browser.getResources().getResourceName(clickedId), url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,10 @@ import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
||||||
import androidx.browser.customtabs.CustomTabsCallback;
|
import androidx.browser.customtabs.CustomTabsCallback;
|
||||||
|
@ -20,9 +21,10 @@ import androidx.browser.customtabs.CustomTabsService;
|
||||||
import androidx.browser.customtabs.CustomTabsSession;
|
import androidx.browser.customtabs.CustomTabsSession;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
import com.pichillilorenzo.flutter_inappwebview.types.AndroidResource;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsActionButton;
|
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsActionButton;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsMenuItem;
|
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsMenuItem;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsSecondaryToolbar;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -44,15 +46,24 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
@Nullable
|
@Nullable
|
||||||
public CustomTabsSession customTabsSession;
|
public CustomTabsSession customTabsSession;
|
||||||
protected final int CHROME_CUSTOM_TAB_REQUEST_CODE = 100;
|
protected final int CHROME_CUSTOM_TAB_REQUEST_CODE = 100;
|
||||||
protected boolean onChromeSafariBrowserOpened = false;
|
protected boolean onOpened = false;
|
||||||
protected boolean onChromeSafariBrowserCompletedInitialLoad = false;
|
protected boolean onCompletedInitialLoad = false;
|
||||||
@Nullable
|
@Nullable
|
||||||
public ChromeSafariBrowserManager manager;
|
public ChromeSafariBrowserManager manager;
|
||||||
|
@Nullable
|
||||||
public String initialUrl;
|
public String initialUrl;
|
||||||
|
@Nullable
|
||||||
|
public List<String> initialOtherLikelyURLs;
|
||||||
|
@Nullable
|
||||||
|
public Map<String, String> initialHeaders;
|
||||||
|
@Nullable
|
||||||
|
public String initialReferrer;
|
||||||
public List<CustomTabsMenuItem> menuItems = new ArrayList<>();
|
public List<CustomTabsMenuItem> menuItems = new ArrayList<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
public CustomTabsActionButton actionButton;
|
public CustomTabsActionButton actionButton;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
public CustomTabsSecondaryToolbar secondaryToolbar;
|
||||||
|
@Nullable
|
||||||
public ChromeCustomTabsChannelDelegate channelDelegate;
|
public ChromeCustomTabsChannelDelegate channelDelegate;
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
|
@ -69,7 +80,7 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
|
|
||||||
String managerId = b.getString("managerId");
|
String managerId = b.getString("managerId");
|
||||||
manager = ChromeSafariBrowserManager.shared.get(managerId);
|
manager = ChromeSafariBrowserManager.shared.get(managerId);
|
||||||
if (manager == null || manager.plugin == null|| manager.plugin.messenger == null) return;
|
if (manager == null || manager.plugin == null || manager.plugin.messenger == null) return;
|
||||||
|
|
||||||
ChromeSafariBrowserManager.browsers.put(id, this);
|
ChromeSafariBrowserManager.browsers.put(id, this);
|
||||||
|
|
||||||
|
@ -77,10 +88,14 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
channelDelegate = new ChromeCustomTabsChannelDelegate(this, channel);
|
channelDelegate = new ChromeCustomTabsChannelDelegate(this, channel);
|
||||||
|
|
||||||
initialUrl = b.getString("url");
|
initialUrl = b.getString("url");
|
||||||
|
initialHeaders = (Map<String, String>) b.getSerializable("headers");
|
||||||
|
initialReferrer = b.getString("referrer");
|
||||||
|
initialOtherLikelyURLs = b.getStringArrayList("otherLikelyURLs");
|
||||||
|
|
||||||
customSettings = new ChromeCustomTabsSettings();
|
customSettings = new ChromeCustomTabsSettings();
|
||||||
customSettings.parse((HashMap<String, Object>) b.getSerializable("settings"));
|
customSettings.parse((HashMap<String, Object>) b.getSerializable("settings"));
|
||||||
actionButton = CustomTabsActionButton.fromMap((Map<String, Object>) b.getSerializable("actionButton"));
|
actionButton = CustomTabsActionButton.fromMap((Map<String, Object>) b.getSerializable("actionButton"));
|
||||||
|
secondaryToolbar = CustomTabsSecondaryToolbar.fromMap((Map<String, Object>) b.getSerializable("secondaryToolbar"));
|
||||||
List<Map<String, Object>> menuItemList = (List<Map<String, Object>>) b.getSerializable("menuItemList");
|
List<Map<String, Object>> menuItemList = (List<Map<String, Object>>) b.getSerializable("menuItemList");
|
||||||
for (Map<String, Object> menuItem : menuItemList) {
|
for (Map<String, Object> menuItem : menuItemList) {
|
||||||
menuItems.add(CustomTabsMenuItem.fromMap(menuItem));
|
menuItems.add(CustomTabsMenuItem.fromMap(menuItem));
|
||||||
|
@ -92,6 +107,9 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
@Override
|
@Override
|
||||||
public void onCustomTabsConnected() {
|
public void onCustomTabsConnected() {
|
||||||
customTabsConnected();
|
customTabsConnected();
|
||||||
|
if (channelDelegate != null) {
|
||||||
|
channelDelegate.onServiceConnected();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,59 +122,85 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
customTabActivityHelper.setCustomTabsCallback(new CustomTabsCallback() {
|
customTabActivityHelper.setCustomTabsCallback(new CustomTabsCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onNavigationEvent(int navigationEvent, Bundle extras) {
|
public void onNavigationEvent(int navigationEvent, Bundle extras) {
|
||||||
if (navigationEvent == TAB_SHOWN && !onChromeSafariBrowserOpened) {
|
if (navigationEvent == TAB_SHOWN && !onOpened) {
|
||||||
onChromeSafariBrowserOpened = true;
|
onOpened = true;
|
||||||
if (channelDelegate != null) {
|
if (channelDelegate != null) {
|
||||||
channelDelegate.onChromeSafariBrowserOpened();
|
channelDelegate.onOpened();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navigationEvent == NAVIGATION_FINISHED && !onChromeSafariBrowserCompletedInitialLoad) {
|
if (navigationEvent == NAVIGATION_FINISHED && !onCompletedInitialLoad) {
|
||||||
onChromeSafariBrowserCompletedInitialLoad = true;
|
onCompletedInitialLoad = true;
|
||||||
if (channelDelegate != null) {
|
if (channelDelegate != null) {
|
||||||
channelDelegate.onChromeSafariBrowserCompletedInitialLoad();
|
channelDelegate.onCompletedInitialLoad();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (channelDelegate != null) {
|
||||||
|
channelDelegate.onNavigationEvent(navigationEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void extraCallback(String callbackName, Bundle args) {
|
public void extraCallback(@NonNull String callbackName, Bundle args) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageChannelReady(Bundle extras) {
|
public void onMessageChannelReady(Bundle extras) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPostMessage(String message, Bundle extras) {
|
public void onPostMessage(@NonNull String message, Bundle extras) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRelationshipValidationResult(@CustomTabsService.Relation int relation, Uri requestedOrigin,
|
public void onRelationshipValidationResult(@CustomTabsService.Relation int relation,
|
||||||
|
@NonNull Uri requestedOrigin,
|
||||||
boolean result, Bundle extras) {
|
boolean result, Bundle extras) {
|
||||||
|
if (channelDelegate != null) {
|
||||||
|
channelDelegate.onRelationshipValidationResult(relation, requestedOrigin, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void customTabsConnected() {
|
public void launchUrl(@NonNull String url,
|
||||||
customTabsSession = customTabActivityHelper.getSession();
|
@Nullable Map<String, String> headers,
|
||||||
Uri uri = Uri.parse(initialUrl);
|
@Nullable String referrer,
|
||||||
customTabActivityHelper.mayLaunchUrl(uri, null, null);
|
@Nullable List<String> otherLikelyURLs) {
|
||||||
|
mayLaunchUrl(url, otherLikelyURLs);
|
||||||
builder = new CustomTabsIntent.Builder(customTabsSession);
|
builder = new CustomTabsIntent.Builder(customTabsSession);
|
||||||
prepareCustomTabs();
|
prepareCustomTabs();
|
||||||
|
|
||||||
CustomTabsIntent customTabsIntent = builder.build();
|
CustomTabsIntent customTabsIntent = builder.build();
|
||||||
prepareCustomTabsIntent(customTabsIntent);
|
prepareCustomTabsIntent(customTabsIntent);
|
||||||
|
|
||||||
CustomTabActivityHelper.openCustomTab(this, customTabsIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE);
|
CustomTabActivityHelper.openCustomTab(this, customTabsIntent, Uri.parse(url), headers,
|
||||||
|
referrer != null ? Uri.parse(referrer) : null, CHROME_CUSTOM_TAB_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mayLaunchUrl(@Nullable String url, @Nullable List<String> otherLikelyURLs) {
|
||||||
|
Uri uri = url != null ? Uri.parse(url) : null;
|
||||||
|
|
||||||
|
List<Bundle> bundleOtherLikelyURLs = new ArrayList<>();
|
||||||
|
if (otherLikelyURLs != null) {
|
||||||
|
Bundle bundleOtherLikelyURL = new Bundle();
|
||||||
|
for (String otherLikelyURL : otherLikelyURLs) {
|
||||||
|
bundleOtherLikelyURL.putString(CustomTabsService.KEY_URL, otherLikelyURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return customTabActivityHelper.mayLaunchUrl(uri, null, bundleOtherLikelyURLs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void customTabsConnected() {
|
||||||
|
customTabsSession = customTabActivityHelper.getSession();
|
||||||
|
if (initialUrl != null) {
|
||||||
|
launchUrl(initialUrl, initialHeaders, initialReferrer, initialOtherLikelyURLs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareCustomTabs() {
|
private void prepareCustomTabs() {
|
||||||
|
if (builder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (customSettings.addDefaultShareMenuItem != null) {
|
if (customSettings.addDefaultShareMenuItem != null) {
|
||||||
builder.setShareState(customSettings.addDefaultShareMenuItem ?
|
builder.setShareState(customSettings.addDefaultShareMenuItem ?
|
||||||
CustomTabsIntent.SHARE_STATE_ON : CustomTabsIntent.SHARE_STATE_OFF);
|
CustomTabsIntent.SHARE_STATE_ON : CustomTabsIntent.SHARE_STATE_OFF);
|
||||||
|
@ -164,16 +208,34 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
builder.setShareState(customSettings.shareState);
|
builder.setShareState(customSettings.shareState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder();
|
||||||
if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) {
|
if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) {
|
||||||
CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder();
|
defaultColorSchemeBuilder.setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor));
|
||||||
builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder
|
|
||||||
.setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor))
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
if (customSettings.navigationBarColor != null && !customSettings.navigationBarColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setNavigationBarColor(Color.parseColor(customSettings.navigationBarColor));
|
||||||
|
}
|
||||||
|
if (customSettings.navigationBarDividerColor != null && !customSettings.navigationBarDividerColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setNavigationBarDividerColor(Color.parseColor(customSettings.navigationBarDividerColor));
|
||||||
|
}
|
||||||
|
if (customSettings.secondaryToolbarColor != null && !customSettings.secondaryToolbarColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setSecondaryToolbarColor(Color.parseColor(customSettings.secondaryToolbarColor));
|
||||||
|
}
|
||||||
|
builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder.build());
|
||||||
|
|
||||||
builder.setShowTitle(customSettings.showTitle);
|
builder.setShowTitle(customSettings.showTitle);
|
||||||
builder.setUrlBarHidingEnabled(customSettings.enableUrlBarHiding);
|
builder.setUrlBarHidingEnabled(customSettings.enableUrlBarHiding);
|
||||||
builder.setInstantAppsEnabled(customSettings.instantAppsEnabled);
|
builder.setInstantAppsEnabled(customSettings.instantAppsEnabled);
|
||||||
|
if (customSettings.startAnimations.size() == 2) {
|
||||||
|
builder.setStartAnimations(this,
|
||||||
|
customSettings.startAnimations.get(0).getIdentifier(this),
|
||||||
|
customSettings.startAnimations.get(1).getIdentifier(this));
|
||||||
|
}
|
||||||
|
if (customSettings.exitAnimations.size() == 2) {
|
||||||
|
builder.setExitAnimations(this,
|
||||||
|
customSettings.exitAnimations.get(0).getIdentifier(this),
|
||||||
|
customSettings.exitAnimations.get(1).getIdentifier(this));
|
||||||
|
}
|
||||||
|
|
||||||
for (CustomTabsMenuItem menuItem : menuItems) {
|
for (CustomTabsMenuItem menuItem : menuItems) {
|
||||||
builder.addMenuItem(menuItem.getLabel(),
|
builder.addMenuItem(menuItem.getLabel(),
|
||||||
|
@ -191,6 +253,33 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
createPendingIntent(actionButton.getId()),
|
createPendingIntent(actionButton.getId()),
|
||||||
actionButton.isShouldTint());
|
actionButton.isShouldTint());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (secondaryToolbar != null) {
|
||||||
|
AndroidResource layout = secondaryToolbar.getLayout();
|
||||||
|
RemoteViews remoteViews = new RemoteViews(layout.getDefPackage(), layout.getIdentifier(this));
|
||||||
|
int[] clickableIDs = new int[secondaryToolbar.getClickableIDs().size()];
|
||||||
|
for (int i = 0, length = secondaryToolbar.getClickableIDs().size(); i < length; i++) {
|
||||||
|
AndroidResource clickableID = secondaryToolbar.getClickableIDs().get(i);
|
||||||
|
clickableIDs[i] = clickableID.getIdentifier(this);
|
||||||
|
}
|
||||||
|
builder.setSecondaryToolbarViews(remoteViews, clickableIDs, getSecondaryToolbarOnClickPendingIntent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PendingIntent getSecondaryToolbarOnClickPendingIntent() {
|
||||||
|
Intent broadcastIntent = new Intent(this, ActionBroadcastReceiver.class);
|
||||||
|
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putString(ActionBroadcastReceiver.KEY_ACTION_VIEW_ID, id);
|
||||||
|
broadcastIntent.putExtras(extras);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
this, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||||
|
} else {
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
this, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareCustomTabsIntent(CustomTabsIntent customTabsIntent) {
|
private void prepareCustomTabsIntent(CustomTabsIntent customTabsIntent) {
|
||||||
|
@ -201,6 +290,38 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
|
|
||||||
if (customSettings.keepAliveEnabled)
|
if (customSettings.keepAliveEnabled)
|
||||||
CustomTabsHelper.addKeepAliveExtra(this, customTabsIntent.intent);
|
CustomTabsHelper.addKeepAliveExtra(this, customTabsIntent.intent);
|
||||||
|
|
||||||
|
if (customSettings.alwaysUseBrowserUI)
|
||||||
|
CustomTabsIntent.setAlwaysUseBrowserUI(customTabsIntent.intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateActionButton(@NonNull byte[] icon, @NonNull String description) {
|
||||||
|
if (customTabsSession == null || actionButton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
|
||||||
|
bitmapOptions.inMutable = true;
|
||||||
|
Bitmap bmp = BitmapFactory.decodeByteArray(
|
||||||
|
icon, 0, icon.length, bitmapOptions
|
||||||
|
);
|
||||||
|
customTabsSession.setActionButton(bmp, description);
|
||||||
|
actionButton.setIcon(icon);
|
||||||
|
actionButton.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSecondaryToolbar(CustomTabsSecondaryToolbar secondaryToolbar) {
|
||||||
|
if (customTabsSession == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AndroidResource layout = secondaryToolbar.getLayout();
|
||||||
|
RemoteViews remoteViews = new RemoteViews(layout.getDefPackage(), layout.getIdentifier(this));
|
||||||
|
int[] clickableIDs = new int[secondaryToolbar.getClickableIDs().size()];
|
||||||
|
for (int i = 0, length = secondaryToolbar.getClickableIDs().size(); i < length; i++) {
|
||||||
|
AndroidResource clickableID = secondaryToolbar.getClickableIDs().get(i);
|
||||||
|
clickableIDs[i] = clickableID.getIdentifier(this);
|
||||||
|
}
|
||||||
|
customTabsSession.setSecondaryToolbarViews(remoteViews, clickableIDs, getSecondaryToolbarOnClickPendingIntent());
|
||||||
|
this.secondaryToolbar = secondaryToolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -265,7 +386,7 @@ public class ChromeCustomTabsActivity extends Activity implements Disposable {
|
||||||
customTabsSession = null;
|
customTabsSession = null;
|
||||||
finish();
|
finish();
|
||||||
if (channelDelegate != null) {
|
if (channelDelegate != null) {
|
||||||
channelDelegate.onChromeSafariBrowserClosed();
|
channelDelegate.onClosed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,18 @@ package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsActionButton;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.CustomTabsSecondaryToolbar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
@ -28,6 +31,59 @@ public class ChromeCustomTabsChannelDelegate extends ChannelDelegateImpl {
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(@NonNull final MethodCall call, @NonNull final MethodChannel.Result result) {
|
public void onMethodCall(@NonNull final MethodCall call, @NonNull final MethodChannel.Result result) {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
|
case "launchUrl":
|
||||||
|
if (chromeCustomTabsActivity != null) {
|
||||||
|
String url = (String) call.argument("url");
|
||||||
|
if (url != null) {
|
||||||
|
Map<String, String> headers = (Map<String, String>) call.argument("headers");
|
||||||
|
String referrer = (String) call.argument("referrer");
|
||||||
|
List<String> otherLikelyURLs = (List<String>) call.argument("otherLikelyURLs");
|
||||||
|
chromeCustomTabsActivity.launchUrl(url, headers, referrer, otherLikelyURLs);
|
||||||
|
result.success(true);
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "mayLaunchUrl":
|
||||||
|
if (chromeCustomTabsActivity != null) {
|
||||||
|
String url = (String) call.argument("url");
|
||||||
|
List<String> otherLikelyURLs = (List<String>) call.argument("otherLikelyURLs");
|
||||||
|
result.success(chromeCustomTabsActivity.mayLaunchUrl(url, otherLikelyURLs));
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "updateActionButton":
|
||||||
|
if (chromeCustomTabsActivity != null) {
|
||||||
|
byte[] icon = (byte[]) call.argument("icon");
|
||||||
|
String description = (String) call.argument("description");
|
||||||
|
chromeCustomTabsActivity.updateActionButton(icon, description);
|
||||||
|
result.success(true);
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "validateRelationship":
|
||||||
|
if (chromeCustomTabsActivity != null && chromeCustomTabsActivity.customTabsSession != null) {
|
||||||
|
Integer relation = (Integer) call.argument("relation");
|
||||||
|
String origin = (String) call.argument("origin");
|
||||||
|
result.success(chromeCustomTabsActivity.customTabsSession.validateRelationship(relation, Uri.parse(origin), null));
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "updateSecondaryToolbar":
|
||||||
|
if (chromeCustomTabsActivity != null) {
|
||||||
|
CustomTabsSecondaryToolbar secondaryToolbar = CustomTabsSecondaryToolbar.fromMap((Map<String, Object>) call.argument("secondaryToolbar"));
|
||||||
|
chromeCustomTabsActivity.updateSecondaryToolbar(secondaryToolbar);
|
||||||
|
result.success(true);
|
||||||
|
} else {
|
||||||
|
result.success(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "close":
|
case "close":
|
||||||
if (chromeCustomTabsActivity != null) {
|
if (chromeCustomTabsActivity != null) {
|
||||||
chromeCustomTabsActivity.onStop();
|
chromeCustomTabsActivity.onStop();
|
||||||
|
@ -54,35 +110,69 @@ public class ChromeCustomTabsChannelDelegate extends ChannelDelegateImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onChromeSafariBrowserOpened() {
|
public void onServiceConnected() {
|
||||||
MethodChannel channel = getChannel();
|
MethodChannel channel = getChannel();
|
||||||
if (channel == null) return;
|
if (channel == null) return;
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
channel.invokeMethod("onChromeSafariBrowserOpened", obj);
|
channel.invokeMethod("onServiceConnected", obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onChromeSafariBrowserCompletedInitialLoad() {
|
public void onOpened() {
|
||||||
MethodChannel channel = getChannel();
|
MethodChannel channel = getChannel();
|
||||||
if (channel == null) return;
|
if (channel == null) return;
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
channel.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", obj);
|
channel.invokeMethod("onOpened", obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onChromeSafariBrowserClosed() {
|
public void onCompletedInitialLoad() {
|
||||||
MethodChannel channel = getChannel();
|
MethodChannel channel = getChannel();
|
||||||
if (channel == null) return;
|
if (channel == null) return;
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
channel.invokeMethod("onChromeSafariBrowserClosed", obj);
|
channel.invokeMethod("onCompletedInitialLoad", obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onChromeSafariBrowserItemActionPerform(int id, String url, String title) {
|
public void onNavigationEvent(int navigationEvent) {;
|
||||||
|
MethodChannel channel = getChannel();
|
||||||
|
if (channel == null) return;
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("navigationEvent", navigationEvent);
|
||||||
|
channel.invokeMethod("onNavigationEvent", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClosed() {
|
||||||
|
MethodChannel channel = getChannel();
|
||||||
|
if (channel == null) return;
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
channel.invokeMethod("onClosed", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onItemActionPerform(int id, String url, String title) {
|
||||||
MethodChannel channel = getChannel();
|
MethodChannel channel = getChannel();
|
||||||
if (channel == null) return;
|
if (channel == null) return;
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
obj.put("id", id);
|
obj.put("id", id);
|
||||||
obj.put("url", url);
|
obj.put("url", url);
|
||||||
obj.put("title", title);
|
obj.put("title", title);
|
||||||
channel.invokeMethod("onChromeSafariBrowserItemActionPerform", obj);
|
channel.invokeMethod("onItemActionPerform", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSecondaryItemActionPerform(String name, String url) {
|
||||||
|
MethodChannel channel = getChannel();
|
||||||
|
if (channel == null) return;
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("name", name);
|
||||||
|
obj.put("url", url);
|
||||||
|
channel.invokeMethod("onSecondaryItemActionPerform", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin, boolean result) {
|
||||||
|
MethodChannel channel = getChannel();
|
||||||
|
if (channel == null) return;
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("relation", relation);
|
||||||
|
obj.put("requestedOrigin", requestedOrigin.toString());
|
||||||
|
obj.put("result", result);
|
||||||
|
channel.invokeMethod("onRelationshipValidationResult", obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.browser.trusted.ScreenOrientation;
|
||||||
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
|
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.AndroidResource;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -25,6 +26,12 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
public Boolean showTitle = true;
|
public Boolean showTitle = true;
|
||||||
@Nullable
|
@Nullable
|
||||||
public String toolbarBackgroundColor;
|
public String toolbarBackgroundColor;
|
||||||
|
@Nullable
|
||||||
|
public String navigationBarColor;
|
||||||
|
@Nullable
|
||||||
|
public String navigationBarDividerColor;
|
||||||
|
@Nullable
|
||||||
|
public String secondaryToolbarColor;
|
||||||
public Boolean enableUrlBarHiding = false;
|
public Boolean enableUrlBarHiding = false;
|
||||||
public Boolean instantAppsEnabled = false;
|
public Boolean instantAppsEnabled = false;
|
||||||
public String packageName;
|
public String packageName;
|
||||||
|
@ -35,6 +42,9 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
public List<String> additionalTrustedOrigins = new ArrayList<>();
|
public List<String> additionalTrustedOrigins = new ArrayList<>();
|
||||||
public TrustedWebActivityDisplayMode displayMode = null;
|
public TrustedWebActivityDisplayMode displayMode = null;
|
||||||
public Integer screenOrientation = ScreenOrientation.DEFAULT;
|
public Integer screenOrientation = ScreenOrientation.DEFAULT;
|
||||||
|
public List<AndroidResource> startAnimations = new ArrayList<>();
|
||||||
|
public List<AndroidResource> exitAnimations = new ArrayList<>();
|
||||||
|
public Boolean alwaysUseBrowserUI = false;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,6 +69,15 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
case "toolbarBackgroundColor":
|
case "toolbarBackgroundColor":
|
||||||
toolbarBackgroundColor = (String) value;
|
toolbarBackgroundColor = (String) value;
|
||||||
break;
|
break;
|
||||||
|
case "navigationBarColor":
|
||||||
|
navigationBarColor = (String) value;
|
||||||
|
break;
|
||||||
|
case "navigationBarDividerColor":
|
||||||
|
navigationBarDividerColor = (String) value;
|
||||||
|
break;
|
||||||
|
case "secondaryToolbarColor":
|
||||||
|
secondaryToolbarColor = (String) value;
|
||||||
|
break;
|
||||||
case "enableUrlBarHiding":
|
case "enableUrlBarHiding":
|
||||||
enableUrlBarHiding = (Boolean) value;
|
enableUrlBarHiding = (Boolean) value;
|
||||||
break;
|
break;
|
||||||
|
@ -92,14 +111,37 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
boolean isSticky = (boolean) displayModeMap.get("isSticky");
|
boolean isSticky = (boolean) displayModeMap.get("isSticky");
|
||||||
int layoutInDisplayCutoutMode = (int) displayModeMap.get("displayCutoutMode");
|
int layoutInDisplayCutoutMode = (int) displayModeMap.get("displayCutoutMode");
|
||||||
displayMode = new TrustedWebActivityDisplayMode.ImmersiveMode(isSticky, layoutInDisplayCutoutMode);
|
displayMode = new TrustedWebActivityDisplayMode.ImmersiveMode(isSticky, layoutInDisplayCutoutMode);
|
||||||
|
break;
|
||||||
case "DEFAULT_MODE":
|
case "DEFAULT_MODE":
|
||||||
displayMode = new TrustedWebActivityDisplayMode.DefaultMode();
|
displayMode = new TrustedWebActivityDisplayMode.DefaultMode();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "screenOrientation":
|
case "screenOrientation":
|
||||||
screenOrientation = (Integer) value;
|
screenOrientation = (Integer) value;
|
||||||
break;
|
break;
|
||||||
|
case "startAnimations":
|
||||||
|
List<Map<String, Object>> startAnimationsList = (List<Map<String, Object>>) value;
|
||||||
|
for (Map<String, Object> startAnimation : startAnimationsList) {
|
||||||
|
AndroidResource androidResource = AndroidResource.fromMap(startAnimation);
|
||||||
|
if (androidResource != null) {
|
||||||
|
startAnimations.add(AndroidResource.fromMap(startAnimation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "exitAnimations":
|
||||||
|
List<Map<String, Object>> exitAnimationsList = (List<Map<String, Object>>) value;
|
||||||
|
for (Map<String, Object> exitAnimation : exitAnimationsList) {
|
||||||
|
AndroidResource androidResource = AndroidResource.fromMap(exitAnimation);
|
||||||
|
if (androidResource != null) {
|
||||||
|
exitAnimations.add(AndroidResource.fromMap(exitAnimation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "alwaysUseBrowserUI":
|
||||||
|
alwaysUseBrowserUI = (Boolean) value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +155,9 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
options.put("addDefaultShareMenuItem", addDefaultShareMenuItem);
|
options.put("addDefaultShareMenuItem", addDefaultShareMenuItem);
|
||||||
options.put("showTitle", showTitle);
|
options.put("showTitle", showTitle);
|
||||||
options.put("toolbarBackgroundColor", toolbarBackgroundColor);
|
options.put("toolbarBackgroundColor", toolbarBackgroundColor);
|
||||||
|
options.put("navigationBarColor", navigationBarColor);
|
||||||
|
options.put("navigationBarDividerColor", navigationBarDividerColor);
|
||||||
|
options.put("secondaryToolbarColor", secondaryToolbarColor);
|
||||||
options.put("enableUrlBarHiding", enableUrlBarHiding);
|
options.put("enableUrlBarHiding", enableUrlBarHiding);
|
||||||
options.put("instantAppsEnabled", instantAppsEnabled);
|
options.put("instantAppsEnabled", instantAppsEnabled);
|
||||||
options.put("packageName", packageName);
|
options.put("packageName", packageName);
|
||||||
|
@ -122,6 +167,7 @@ public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActiv
|
||||||
options.put("isTrustedWebActivity", isTrustedWebActivity);
|
options.put("isTrustedWebActivity", isTrustedWebActivity);
|
||||||
options.put("additionalTrustedOrigins", additionalTrustedOrigins);
|
options.put("additionalTrustedOrigins", additionalTrustedOrigins);
|
||||||
options.put("screenOrientation", screenOrientation);
|
options.put("screenOrientation", screenOrientation);
|
||||||
|
options.put("alwaysUseBrowserUI", alwaysUseBrowserUI);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||||
|
@ -12,6 +13,7 @@ import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.Headless
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -46,10 +48,14 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
|
||||||
case "open":
|
case "open":
|
||||||
if (plugin != null && plugin.activity != null) {
|
if (plugin != null && plugin.activity != null) {
|
||||||
String url = (String) call.argument("url");
|
String url = (String) call.argument("url");
|
||||||
|
HashMap<String, Object> headers = (HashMap<String, Object>) call.argument("headers");
|
||||||
|
String referrer = (String) call.argument("referrer");
|
||||||
|
ArrayList<String> otherLikelyURLs = (ArrayList<String>) call.argument("otherLikelyURLs");
|
||||||
HashMap<String, Object> settings = (HashMap<String, Object>) call.argument("settings");
|
HashMap<String, Object> settings = (HashMap<String, Object>) call.argument("settings");
|
||||||
HashMap<String, Object> actionButton = (HashMap<String, Object>) call.argument("actionButton");
|
HashMap<String, Object> actionButton = (HashMap<String, Object>) call.argument("actionButton");
|
||||||
|
HashMap<String, Object> secondaryToolbar = (HashMap<String, Object>) call.argument("secondaryToolbar");
|
||||||
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
|
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
|
||||||
open(plugin.activity, viewId, url, settings, actionButton, menuItemList, result);
|
open(plugin.activity, viewId, url, headers, referrer, otherLikelyURLs, settings, actionButton, secondaryToolbar, menuItemList, result);
|
||||||
} else {
|
} else {
|
||||||
result.success(false);
|
result.success(false);
|
||||||
}
|
}
|
||||||
|
@ -61,23 +67,31 @@ public class ChromeSafariBrowserManager extends ChannelDelegateImpl {
|
||||||
result.success(false);
|
result.success(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "getMaxToolbarItems":
|
||||||
|
result.success(CustomTabsIntent.getMaxToolbarItems());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
result.notImplemented();
|
result.notImplemented();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(Activity activity, String viewId, String url, HashMap<String, Object> settings,
|
public void open(Activity activity, String viewId, @Nullable String url, @Nullable HashMap<String, Object> headers,
|
||||||
HashMap<String, Object> actionButton,
|
@Nullable String referrer, @Nullable ArrayList<String> otherLikelyURLs,
|
||||||
|
HashMap<String, Object> settings, HashMap<String, Object> actionButton,
|
||||||
|
HashMap<String, Object> secondaryToolbar,
|
||||||
List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
|
List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
|
||||||
|
|
||||||
Intent intent = null;
|
Intent intent = null;
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putString("url", url);
|
extras.putString("url", url);
|
||||||
extras.putBoolean("isData", false);
|
|
||||||
extras.putString("id", viewId);
|
extras.putString("id", viewId);
|
||||||
extras.putString("managerId", this.id);
|
extras.putString("managerId", this.id);
|
||||||
|
extras.putSerializable("headers", headers);
|
||||||
|
extras.putString("referrer", referrer);
|
||||||
|
extras.putSerializable("otherLikelyURLs", otherLikelyURLs);
|
||||||
extras.putSerializable("settings", settings);
|
extras.putSerializable("settings", settings);
|
||||||
extras.putSerializable("actionButton", (Serializable) actionButton);
|
extras.putSerializable("actionButton", (Serializable) actionButton);
|
||||||
|
extras.putSerializable("secondaryToolbar", (Serializable) secondaryToolbar);
|
||||||
extras.putSerializable("menuItemList", (Serializable) menuItemList);
|
extras.putSerializable("menuItemList", (Serializable) menuItemList);
|
||||||
|
|
||||||
Boolean isSingleInstance = Util.<Boolean>getOrDefault(settings, "isSingleInstance", false);
|
Boolean isSingleInstance = Util.<Boolean>getOrDefault(settings, "isSingleInstance", false);
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
|
package com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Browser;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.browser.customtabs.CustomTabsCallback;
|
import androidx.browser.customtabs.CustomTabsCallback;
|
||||||
import androidx.browser.customtabs.CustomTabsClient;
|
import androidx.browser.customtabs.CustomTabsClient;
|
||||||
import androidx.browser.customtabs.CustomTabsIntent;
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
|
@ -13,6 +15,7 @@ import androidx.browser.customtabs.CustomTabsSession;
|
||||||
import androidx.browser.trusted.TrustedWebActivityIntent;
|
import androidx.browser.trusted.TrustedWebActivityIntent;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a helper class to manage the connection to the Custom Tabs Service.
|
* This is a helper class to manage the connection to the Custom Tabs Service.
|
||||||
|
@ -25,27 +28,52 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||||
private CustomTabsCallback mCustomTabsCallback;
|
private CustomTabsCallback mCustomTabsCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
|
* Opens the URL on a Custom Tab if possible.
|
||||||
*
|
*
|
||||||
* @param activity The host activity.
|
* @param activity The host activity.
|
||||||
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
|
* @param intent a intent to be used if Custom Tabs is available.
|
||||||
* @param uri the Uri to be opened.
|
* @param uri the Uri to be opened.
|
||||||
*/
|
*/
|
||||||
public static void openCustomTab(Activity activity,
|
public static void openCustomTab(Activity activity,
|
||||||
CustomTabsIntent customTabsIntent,
|
Intent intent,
|
||||||
Uri uri,
|
Uri uri,
|
||||||
|
@Nullable Map<String, String> headers,
|
||||||
|
@Nullable Uri referrer,
|
||||||
int requestCode) {
|
int requestCode) {
|
||||||
customTabsIntent.intent.setData(uri);
|
intent.setData(uri);
|
||||||
activity.startActivityForResult(customTabsIntent.intent, requestCode);
|
if (headers != null) {
|
||||||
|
Bundle bundleHeaders = new Bundle();
|
||||||
|
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||||
|
bundleHeaders.putString(header.getKey(), header.getValue());
|
||||||
|
}
|
||||||
|
intent.putExtra(Browser.EXTRA_HEADERS, bundleHeaders);
|
||||||
|
}
|
||||||
|
if (referrer != null) {
|
||||||
|
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
|
||||||
|
}
|
||||||
|
activity.startActivityForResult(intent, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openCustomTab(Activity activity,
|
public static void openCustomTab(Activity activity,
|
||||||
TrustedWebActivityIntent trustedWebActivityIntent,
|
CustomTabsIntent customTabsIntent,
|
||||||
Uri uri,
|
Uri uri,
|
||||||
|
@Nullable Map<String, String> headers,
|
||||||
|
@Nullable Uri referrer,
|
||||||
int requestCode) {
|
int requestCode) {
|
||||||
trustedWebActivityIntent.getIntent().setData(uri);
|
CustomTabActivityHelper.openCustomTab(activity, customTabsIntent.intent, uri,
|
||||||
activity.startActivityForResult(trustedWebActivityIntent.getIntent(), requestCode);
|
headers, referrer, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void openTrustedWebActivity(Activity activity,
|
||||||
|
TrustedWebActivityIntent trustedWebActivityIntent,
|
||||||
|
Uri uri,
|
||||||
|
@Nullable Map<String, String> headers,
|
||||||
|
@Nullable Uri referrer,
|
||||||
|
int requestCode) {
|
||||||
|
CustomTabActivityHelper.openCustomTab(activity, trustedWebActivityIntent.getIntent(), uri,
|
||||||
|
headers, referrer, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean isAvailable(Activity activity) {
|
public static boolean isAvailable(Activity activity) {
|
||||||
return CustomTabsHelper.getPackageNameToUse(activity) != null;
|
return CustomTabsHelper.getPackageNameToUse(activity) != null;
|
||||||
|
@ -68,6 +96,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||||
*
|
*
|
||||||
* @return a CustomTabsSession.
|
* @return a CustomTabsSession.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public CustomTabsSession getSession() {
|
public CustomTabsSession getSession() {
|
||||||
if (mClient == null) {
|
if (mClient == null) {
|
||||||
mCustomTabsSession = null;
|
mCustomTabsSession = null;
|
||||||
|
|
|
@ -4,12 +4,15 @@ import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import androidx.browser.trusted.TrustedWebActivityIntent;
|
import androidx.browser.trusted.TrustedWebActivityIntent;
|
||||||
import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
|
import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class TrustedWebActivity extends ChromeCustomTabsActivity {
|
public class TrustedWebActivity extends ChromeCustomTabsActivity {
|
||||||
|
|
||||||
|
@ -18,27 +21,49 @@ public class TrustedWebActivity extends ChromeCustomTabsActivity {
|
||||||
public TrustedWebActivityIntentBuilder builder;
|
public TrustedWebActivityIntentBuilder builder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customTabsConnected() {
|
public void launchUrl(@NonNull String url,
|
||||||
customTabsSession = customTabActivityHelper.getSession();
|
@Nullable Map<String, String> headers,
|
||||||
Uri uri = Uri.parse(initialUrl);
|
@Nullable String referrer,
|
||||||
customTabActivityHelper.mayLaunchUrl(uri, null, null);
|
@Nullable List<String> otherLikelyURLs) {
|
||||||
|
if (customTabsSession == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(url);
|
||||||
|
|
||||||
|
mayLaunchUrl(url, otherLikelyURLs);
|
||||||
builder = new TrustedWebActivityIntentBuilder(uri);
|
builder = new TrustedWebActivityIntentBuilder(uri);
|
||||||
prepareCustomTabs();
|
prepareCustomTabs();
|
||||||
|
|
||||||
TrustedWebActivityIntent trustedWebActivityIntent = builder.build(customTabsSession);
|
TrustedWebActivityIntent trustedWebActivityIntent = builder.build(customTabsSession);
|
||||||
prepareCustomTabsIntent(trustedWebActivityIntent);
|
prepareCustomTabsIntent(trustedWebActivityIntent);
|
||||||
|
|
||||||
CustomTabActivityHelper.openCustomTab(this, trustedWebActivityIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE);
|
CustomTabActivityHelper.openTrustedWebActivity(this, trustedWebActivityIntent, uri, headers,
|
||||||
|
referrer != null ? Uri.parse(referrer) : null, CHROME_CUSTOM_TAB_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customTabsConnected() {
|
||||||
|
customTabsSession = customTabActivityHelper.getSession();
|
||||||
|
if (initialUrl != null) {
|
||||||
|
launchUrl(initialUrl, initialHeaders, initialReferrer, initialOtherLikelyURLs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareCustomTabs() {
|
private void prepareCustomTabs() {
|
||||||
|
CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder();
|
||||||
if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) {
|
if (customSettings.toolbarBackgroundColor != null && !customSettings.toolbarBackgroundColor.isEmpty()) {
|
||||||
CustomTabColorSchemeParams.Builder defaultColorSchemeBuilder = new CustomTabColorSchemeParams.Builder();
|
defaultColorSchemeBuilder.setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor));
|
||||||
builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder
|
|
||||||
.setToolbarColor(Color.parseColor(customSettings.toolbarBackgroundColor))
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
if (customSettings.navigationBarColor != null && !customSettings.navigationBarColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setNavigationBarColor(Color.parseColor(customSettings.navigationBarColor));
|
||||||
|
}
|
||||||
|
if (customSettings.navigationBarDividerColor != null && !customSettings.navigationBarDividerColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setNavigationBarDividerColor(Color.parseColor(customSettings.navigationBarDividerColor));
|
||||||
|
}
|
||||||
|
if (customSettings.secondaryToolbarColor != null && !customSettings.secondaryToolbarColor.isEmpty()) {
|
||||||
|
defaultColorSchemeBuilder.setSecondaryToolbarColor(Color.parseColor(customSettings.secondaryToolbarColor));
|
||||||
|
}
|
||||||
|
builder.setDefaultColorSchemeParams(defaultColorSchemeBuilder.build());
|
||||||
|
|
||||||
if (customSettings.additionalTrustedOrigins != null && !customSettings.additionalTrustedOrigins.isEmpty()) {
|
if (customSettings.additionalTrustedOrigins != null && !customSettings.additionalTrustedOrigins.isEmpty()) {
|
||||||
builder.setAdditionalTrustedOrigins(customSettings.additionalTrustedOrigins);
|
builder.setAdditionalTrustedOrigins(customSettings.additionalTrustedOrigins);
|
||||||
|
@ -47,7 +72,7 @@ public class TrustedWebActivity extends ChromeCustomTabsActivity {
|
||||||
if (customSettings.displayMode != null) {
|
if (customSettings.displayMode != null) {
|
||||||
builder.setDisplayMode(customSettings.displayMode);
|
builder.setDisplayMode(customSettings.displayMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setScreenOrientation(customSettings.screenOrientation);
|
builder.setScreenOrientation(customSettings.screenOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,5 +85,8 @@ public class TrustedWebActivity extends ChromeCustomTabsActivity {
|
||||||
|
|
||||||
if (customSettings.keepAliveEnabled)
|
if (customSettings.keepAliveEnabled)
|
||||||
CustomTabsHelper.addKeepAliveExtra(this, intent);
|
CustomTabsHelper.addKeepAliveExtra(this, intent);
|
||||||
|
|
||||||
|
if (customSettings.alwaysUseBrowserUI)
|
||||||
|
CustomTabsIntent.setAlwaysUseBrowserUI(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AndroidResource {
|
||||||
|
@NonNull
|
||||||
|
private String name;
|
||||||
|
@Nullable
|
||||||
|
private String defType;
|
||||||
|
@Nullable
|
||||||
|
private String defPackage;
|
||||||
|
|
||||||
|
public AndroidResource(@NonNull String name, @Nullable String defType, @Nullable String defPackage) {
|
||||||
|
this.name = name;
|
||||||
|
this.defType = defType;
|
||||||
|
this.defPackage = defPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static AndroidResource fromMap(@Nullable Map<String, Object> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String name = (String) map.get("name");
|
||||||
|
String defType = (String) map.get("defType");
|
||||||
|
String defPackage = (String) map.get("defPackage");
|
||||||
|
return new AndroidResource(name, defType, defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> urlRequestMap = new HashMap<>();
|
||||||
|
urlRequestMap.put("name", name);
|
||||||
|
urlRequestMap.put("defType", defType);
|
||||||
|
urlRequestMap.put("defPackage", defPackage);
|
||||||
|
return urlRequestMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(@NonNull String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getDefType() {
|
||||||
|
return defType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefType(@Nullable String defType) {
|
||||||
|
this.defType = defType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getDefPackage() {
|
||||||
|
return defPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefPackage(@Nullable String defPackage) {
|
||||||
|
this.defPackage = defPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIdentifier(@NonNull Context ctx) {
|
||||||
|
return ctx.getResources().getIdentifier(name, defType, defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
AndroidResource that = (AndroidResource) o;
|
||||||
|
|
||||||
|
if (!name.equals(that.name)) return false;
|
||||||
|
if (defType != null ? !defType.equals(that.defType) : that.defType != null) return false;
|
||||||
|
return defPackage != null ? defPackage.equals(that.defPackage) : that.defPackage == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = name.hashCode();
|
||||||
|
result = 31 * result + (defType != null ? defType.hashCode() : 0);
|
||||||
|
result = 31 * result + (defPackage != null ? defPackage.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AndroidResource{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", type='" + defType + '\'' +
|
||||||
|
", defPackage='" + defPackage + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CustomTabsSecondaryToolbar {
|
||||||
|
@NonNull
|
||||||
|
private AndroidResource layout;
|
||||||
|
@NonNull
|
||||||
|
private List<AndroidResource> clickableIDs = new ArrayList<>();
|
||||||
|
|
||||||
|
public CustomTabsSecondaryToolbar(@NonNull AndroidResource layout, @NonNull List<AndroidResource> clickableIDs) {
|
||||||
|
this.layout = layout;
|
||||||
|
this.clickableIDs = clickableIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static CustomTabsSecondaryToolbar fromMap(@Nullable Map<String, Object> map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AndroidResource layout = AndroidResource.fromMap((Map<String, Object>) map.get("layout"));
|
||||||
|
List<AndroidResource> clickableIDs = new ArrayList<>();
|
||||||
|
List<Map<String, Object>> clickableIDList = (List<Map<String, Object>>) map.get("clickableIDs");
|
||||||
|
if (clickableIDList != null) {
|
||||||
|
for (Map<String, Object> clickableIDMap : clickableIDList) {
|
||||||
|
AndroidResource clickableID = AndroidResource.fromMap((Map<String, Object>) clickableIDMap.get("id"));
|
||||||
|
if (clickableID != null) {
|
||||||
|
clickableIDs.add(clickableID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CustomTabsSecondaryToolbar(layout, clickableIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public AndroidResource getLayout() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLayout(@NonNull AndroidResource layout) {
|
||||||
|
this.layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<AndroidResource> getClickableIDs() {
|
||||||
|
return clickableIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClickableIDs(@NonNull List<AndroidResource> clickableIDs) {
|
||||||
|
this.clickableIDs = clickableIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
CustomTabsSecondaryToolbar that = (CustomTabsSecondaryToolbar) o;
|
||||||
|
|
||||||
|
if (!layout.equals(that.layout)) return false;
|
||||||
|
return clickableIDs.equals(that.clickableIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = layout.hashCode();
|
||||||
|
result = 31 * result + clickableIDs.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CustomTabsSecondaryToolbar{" +
|
||||||
|
"layout=" + layout +
|
||||||
|
", clickableIDs=" + clickableIDs +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFileProvider;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.CreateWindowAction;
|
import com.pichillilorenzo.flutter_inappwebview.types.CreateWindowAction;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.ActivityResultListener;
|
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.ActivityResultListener;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
|
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate;
|
||||||
|
@ -73,8 +74,6 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
public static Map<Integer, Message> windowWebViewMessages = new HashMap<>();
|
public static Map<Integer, Message> windowWebViewMessages = new HashMap<>();
|
||||||
private static int windowAutoincrementId = 0;
|
private static int windowAutoincrementId = 0;
|
||||||
|
|
||||||
private static final String fileProviderAuthorityExtension = "flutter_inappwebview.fileprovider";
|
|
||||||
|
|
||||||
private static final int PICKER = 1;
|
private static final int PICKER = 1;
|
||||||
private static final int PICKER_LEGACY = 3;
|
private static final int PICKER_LEGACY = 3;
|
||||||
final String DEFAULT_MIME_TYPES = "*/*";
|
final String DEFAULT_MIME_TYPES = "*/*";
|
||||||
|
@ -1148,8 +1147,11 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// for versions 6.0+ (23) we use the FileProvider to avoid runtime permissions
|
// for versions 6.0+ (23) we use the FileProvider to avoid runtime permissions
|
||||||
String packageName = activity.getApplicationContext().getPackageName();
|
String fileProviderAuthority = activity.getApplicationContext().getPackageName() + "." +
|
||||||
return FileProvider.getUriForFile(activity.getApplicationContext(), packageName + "." + fileProviderAuthorityExtension, capturedFile);
|
InAppWebViewFileProvider.fileProviderAuthorityExtension;
|
||||||
|
return FileProvider.getUriForFile(activity.getApplicationContext(),
|
||||||
|
fileProviderAuthority,
|
||||||
|
capturedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Button 1" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Button 2" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="Button 3" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,39 +0,0 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import '../constants.dart';
|
|
||||||
import '../util.dart';
|
|
||||||
|
|
||||||
void customActionButton() {
|
|
||||||
final shouldSkip = kIsWeb
|
|
||||||
? true
|
|
||||||
: ![
|
|
||||||
TargetPlatform.android,
|
|
||||||
].contains(defaultTargetPlatform);
|
|
||||||
|
|
||||||
test('add custom action button', () async {
|
|
||||||
var chromeSafariBrowser = 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) {}));
|
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
|
||||||
|
|
||||||
await chromeSafariBrowser.open(url: TEST_URL_1);
|
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
|
||||||
expect(() async {
|
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
|
||||||
await chromeSafariBrowser.close();
|
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
|
||||||
}, skip: shouldSkip);
|
|
||||||
}
|
|
|
@ -14,11 +14,14 @@ void customMenuItem() {
|
||||||
test('add custom menu item', () async {
|
test('add custom menu item', () async {
|
||||||
var chromeSafariBrowser = MyChromeSafariBrowser();
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
chromeSafariBrowser.addMenuItem(ChromeSafariBrowserMenuItem(
|
chromeSafariBrowser.addMenuItem(ChromeSafariBrowserMenuItem(
|
||||||
id: 2, label: 'Custom item menu 1', action: (url, title) {}));
|
id: 2,
|
||||||
|
label: 'Custom item menu 1',
|
||||||
|
image: UIImage(systemName: "pencil"),
|
||||||
|
onClick: (url, title) {}));
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
await chromeSafariBrowser.open(url: TEST_URL_1);
|
await chromeSafariBrowser.open(url: TEST_URL_1);
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.opened.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
@ -26,7 +29,7 @@ void customMenuItem() {
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.closed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
}, skip: shouldSkip);
|
}, skip: shouldSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
@ -13,14 +14,15 @@ void customTabs() {
|
||||||
].contains(defaultTargetPlatform);
|
].contains(defaultTargetPlatform);
|
||||||
|
|
||||||
group('Custom Tabs', () {
|
group('Custom Tabs', () {
|
||||||
test('single instance', () async {
|
test('custom referrer', () async {
|
||||||
var chromeSafariBrowser = MyChromeSafariBrowser();
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(
|
||||||
url: TEST_URL_1,
|
url: TEST_URL_1,
|
||||||
|
referrer: Uri.parse("android-app://custom-referrer"),
|
||||||
settings: ChromeSafariBrowserSettings(isSingleInstance: true));
|
settings: ChromeSafariBrowserSettings(isSingleInstance: true));
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await expectLater(chromeSafariBrowser.opened.future, completes);
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
@ -28,8 +30,151 @@ void customTabs() {
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await expectLater(chromeSafariBrowser.closed.future, completes);
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('single instance', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(
|
||||||
|
url: TEST_URL_1,
|
||||||
|
settings: ChromeSafariBrowserSettings(isSingleInstance: true));
|
||||||
|
await expectLater(chromeSafariBrowser.opened.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
expect(() async {
|
||||||
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await expectLater(chromeSafariBrowser.closed.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('add custom action button and update icon', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
var actionButtonIcon =
|
||||||
|
await rootBundle.load('test_assets/images/flutter-logo.png');
|
||||||
|
var actionButtonIcon2 =
|
||||||
|
await rootBundle.load('test_assets/images/flutter-logo.jpg');
|
||||||
|
chromeSafariBrowser.setActionButton(ChromeSafariBrowserActionButton(
|
||||||
|
id: 1,
|
||||||
|
description: 'Action Button description',
|
||||||
|
icon: actionButtonIcon.buffer.asUint8List(),
|
||||||
|
onClick: (url, title) {}));
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(url: TEST_URL_1);
|
||||||
|
await chromeSafariBrowser.opened.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
expect(() async {
|
||||||
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
await chromeSafariBrowser.updateActionButton(
|
||||||
|
icon: actionButtonIcon2.buffer.asUint8List(),
|
||||||
|
description: 'New Action Button description');
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await chromeSafariBrowser.closed.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
}, skip: shouldSkip);
|
||||||
|
|
||||||
|
test('mayLaunchUrl and launchUrl', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open();
|
||||||
|
await expectLater(chromeSafariBrowser.serviceConnected.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
expect(
|
||||||
|
await chromeSafariBrowser.mayLaunchUrl(
|
||||||
|
url: TEST_URL_1, otherLikelyURLs: [TEST_CROSS_PLATFORM_URL_1]),
|
||||||
|
true);
|
||||||
|
await chromeSafariBrowser.launchUrl(
|
||||||
|
url: TEST_URL_1,
|
||||||
|
headers: {'accept-language': 'it-IT'},
|
||||||
|
otherLikelyURLs: [TEST_CROSS_PLATFORM_URL_1]);
|
||||||
|
await expectLater(chromeSafariBrowser.opened.future, completes);
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await expectLater(chromeSafariBrowser.closed.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('onNavigationEvent', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(url: TEST_URL_1);
|
||||||
|
await expectLater(chromeSafariBrowser.opened.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
expect(await chromeSafariBrowser.navigationEvent.future, isNotNull);
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await expectLater(chromeSafariBrowser.closed.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('add and update secondary toolbar', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
chromeSafariBrowser.setSecondaryToolbar(
|
||||||
|
ChromeSafariBrowserSecondaryToolbar(
|
||||||
|
layout: AndroidResource.layout(
|
||||||
|
name: "remote_view",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
clickableIDs: [
|
||||||
|
ChromeSafariBrowserSecondaryToolbarClickableID(
|
||||||
|
id: AndroidResource.id(
|
||||||
|
name: "button1",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
onClick: (Uri? url) {
|
||||||
|
print("Button 1 with $url");
|
||||||
|
}),
|
||||||
|
ChromeSafariBrowserSecondaryToolbarClickableID(
|
||||||
|
id: AndroidResource.id(
|
||||||
|
name: "button2",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
onClick: (Uri? url) {
|
||||||
|
print("Button 2 with $url");
|
||||||
|
}),
|
||||||
|
]));
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(url: TEST_URL_1);
|
||||||
|
await chromeSafariBrowser.opened.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
await chromeSafariBrowser.updateSecondaryToolbar(
|
||||||
|
ChromeSafariBrowserSecondaryToolbar(
|
||||||
|
layout: AndroidResource.layout(
|
||||||
|
name: "remote_view_2",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
clickableIDs: [
|
||||||
|
ChromeSafariBrowserSecondaryToolbarClickableID(
|
||||||
|
id: AndroidResource.id(
|
||||||
|
name: "button3",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
onClick: (Uri? url) {
|
||||||
|
print("Button 3 with $url");
|
||||||
|
}),
|
||||||
|
]));
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await chromeSafariBrowser.closed.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getMaxToolbarItems', () async {
|
||||||
|
expect(await ChromeSafariBrowser.getMaxToolbarItems(),
|
||||||
|
greaterThanOrEqualTo(0));
|
||||||
|
});
|
||||||
}, skip: shouldSkip);
|
}, skip: shouldSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'custom_action_button.dart';
|
|
||||||
import 'custom_menu_item.dart';
|
import 'custom_menu_item.dart';
|
||||||
import 'custom_tabs.dart';
|
import 'custom_tabs.dart';
|
||||||
import 'open_and_close.dart';
|
import 'open_and_close.dart';
|
||||||
import 'trusted_web_activity.dart';
|
import 'trusted_web_activity.dart';
|
||||||
|
import 'sf_safari_view_controller.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final shouldSkip =
|
final shouldSkip =
|
||||||
|
@ -14,8 +14,8 @@ void main() {
|
||||||
group('ChromeSafariBrowser', () {
|
group('ChromeSafariBrowser', () {
|
||||||
openAndClose();
|
openAndClose();
|
||||||
customMenuItem();
|
customMenuItem();
|
||||||
customActionButton();
|
|
||||||
customTabs();
|
customTabs();
|
||||||
trustedWebActivity();
|
trustedWebActivity();
|
||||||
|
sfSafariViewController();
|
||||||
}, skip: shouldSkip);
|
}, skip: shouldSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,38 @@ void openAndClose() {
|
||||||
var chromeSafariBrowser = MyChromeSafariBrowser();
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
await chromeSafariBrowser.open(url: TEST_URL_1);
|
await chromeSafariBrowser.open(
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
url: TEST_URL_1,
|
||||||
|
settings: ChromeSafariBrowserSettings(
|
||||||
|
shareState: CustomTabsShareState.SHARE_STATE_OFF,
|
||||||
|
startAnimations: [
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "slide_in_left", defPackage: "android"),
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "slide_out_right", defPackage: "android")
|
||||||
|
],
|
||||||
|
exitAnimations: [
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "abc_slide_in_top",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "abc_slide_out_top",
|
||||||
|
defPackage: "com.pichillilorenzo.flutter_inappwebviewexample")
|
||||||
|
],
|
||||||
|
keepAliveEnabled: true,
|
||||||
|
dismissButtonStyle: DismissButtonStyle.CLOSE,
|
||||||
|
presentationStyle: ModalPresentationStyle.OVER_FULL_SCREEN,
|
||||||
|
eventAttribution: UIEventAttribution(
|
||||||
|
sourceIdentifier: 4,
|
||||||
|
destinationURL: Uri.parse("https://shop.example/test.html"),
|
||||||
|
sourceDescription: "Banner ad for Test.",
|
||||||
|
purchaser: "Shop Example, Inc."),
|
||||||
|
activityButton: ActivityButton(
|
||||||
|
templateImage: UIImage(systemName: "sun.max"),
|
||||||
|
extensionIdentifier:
|
||||||
|
"com.pichillilorenzo.flutter-inappwebview-6-Example.test")));
|
||||||
|
await chromeSafariBrowser.opened.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
@ -24,7 +54,7 @@ void openAndClose() {
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.closed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
}, skip: shouldSkip);
|
}, skip: shouldSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../constants.dart';
|
||||||
|
import '../util.dart';
|
||||||
|
|
||||||
|
void sfSafariViewController() {
|
||||||
|
final shouldSkip = kIsWeb
|
||||||
|
? true
|
||||||
|
: ![
|
||||||
|
TargetPlatform.iOS,
|
||||||
|
].contains(defaultTargetPlatform);
|
||||||
|
|
||||||
|
group('SF Safari View Controller', () {
|
||||||
|
test('onCompletedInitialLoad did load successfully', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(url: TEST_URL_1);
|
||||||
|
await expectLater(chromeSafariBrowser.opened.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
expect(() async {
|
||||||
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
|
expect(await chromeSafariBrowser.firstPageLoaded.future, true);
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await expectLater(chromeSafariBrowser.closed.future, completes);
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: this test takes a lot of time to complete. Tested on iOS 16.0.
|
||||||
|
// test('clearWebsiteData', () async {
|
||||||
|
// await expectLater(ChromeSafariBrowser.clearWebsiteData(), completes);
|
||||||
|
// });
|
||||||
|
|
||||||
|
test('create and invalidate Prewarming Token', () async {
|
||||||
|
final prewarmingToken =
|
||||||
|
await ChromeSafariBrowser.prewarmConnections([TEST_URL_1]);
|
||||||
|
expect(prewarmingToken, isNotNull);
|
||||||
|
await expectLater(
|
||||||
|
ChromeSafariBrowser.invalidatePrewarmingToken(prewarmingToken!),
|
||||||
|
completes);
|
||||||
|
});
|
||||||
|
}, skip: shouldSkip);
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ void trustedWebActivity() {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(
|
||||||
url: TEST_URL_1,
|
url: TEST_URL_1,
|
||||||
settings: ChromeSafariBrowserSettings(isTrustedWebActivity: true));
|
settings: ChromeSafariBrowserSettings(isTrustedWebActivity: true));
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.opened.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
@ -28,7 +28,7 @@ void trustedWebActivity() {
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.closed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ void trustedWebActivity() {
|
||||||
url: TEST_URL_1,
|
url: TEST_URL_1,
|
||||||
settings: ChromeSafariBrowserSettings(
|
settings: ChromeSafariBrowserSettings(
|
||||||
isTrustedWebActivity: true, isSingleInstance: true));
|
isTrustedWebActivity: true, isSingleInstance: true));
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.opened.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
await chromeSafariBrowser.open(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
@ -48,7 +48,30 @@ void trustedWebActivity() {
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.closed.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate relationship', () async {
|
||||||
|
var chromeSafariBrowser = MyChromeSafariBrowser();
|
||||||
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
|
await chromeSafariBrowser.open(
|
||||||
|
settings: ChromeSafariBrowserSettings(isTrustedWebActivity: true));
|
||||||
|
await chromeSafariBrowser.serviceConnected.future;
|
||||||
|
expect(
|
||||||
|
await chromeSafariBrowser.validateRelationship(
|
||||||
|
relation: CustomTabsRelationType.USE_AS_ORIGIN,
|
||||||
|
origin: TEST_CROSS_PLATFORM_URL_1),
|
||||||
|
true);
|
||||||
|
expect(
|
||||||
|
await chromeSafariBrowser.relationshipValidationResult.future, true);
|
||||||
|
await chromeSafariBrowser.launchUrl(url: TEST_CROSS_PLATFORM_URL_1);
|
||||||
|
await chromeSafariBrowser.opened.future;
|
||||||
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
await chromeSafariBrowser.close();
|
||||||
|
await chromeSafariBrowser.closed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
});
|
});
|
||||||
}, skip: shouldSkip);
|
}, skip: shouldSkip);
|
||||||
|
|
|
@ -54,22 +54,44 @@ class MyInAppBrowser extends InAppBrowser {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyChromeSafariBrowser extends ChromeSafariBrowser {
|
class MyChromeSafariBrowser extends ChromeSafariBrowser {
|
||||||
final Completer<void> browserCreated = Completer<void>();
|
final Completer<void> serviceConnected = Completer<void>();
|
||||||
final Completer<void> firstPageLoaded = Completer<void>();
|
final Completer<void> opened = Completer<void>();
|
||||||
final Completer<void> browserClosed = Completer<void>();
|
final Completer<bool?> firstPageLoaded = Completer<bool?>();
|
||||||
|
final Completer<void> closed = Completer<void>();
|
||||||
|
final Completer<CustomTabsNavigationEventType?> navigationEvent =
|
||||||
|
Completer<CustomTabsNavigationEventType?>();
|
||||||
|
final Completer<bool> relationshipValidationResult = Completer<bool>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onOpened() {
|
void onServiceConnected() {
|
||||||
browserCreated.complete();
|
serviceConnected.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCompletedInitialLoad() {
|
void onOpened() {
|
||||||
firstPageLoaded.complete();
|
opened.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onCompletedInitialLoad(didLoadSuccessfully) {
|
||||||
|
firstPageLoaded.complete(didLoadSuccessfully);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onNavigationEvent(CustomTabsNavigationEventType? type) {
|
||||||
|
if (!navigationEvent.isCompleted) {
|
||||||
|
navigationEvent.complete(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRelationshipValidationResult(
|
||||||
|
CustomTabsRelationType? relation, Uri? requestedOrigin, bool result) {
|
||||||
|
relationshipValidationResult.complete(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClosed() {
|
void onClosed() {
|
||||||
browserClosed.complete();
|
closed.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
25A517508F43E58C47090625 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
25A517508F43E58C47090625 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
6143FF37290959650014A1FC /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6143FF36290959650014A1FC /* UniformTypeIdentifiers.framework */; };
|
||||||
|
6143FF3A290959650014A1FC /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6143FF39290959650014A1FC /* Media.xcassets */; };
|
||||||
|
6143FF3C290959650014A1FC /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6143FF3B290959650014A1FC /* ActionViewController.swift */; };
|
||||||
|
6143FF3F290959650014A1FC /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6143FF3D290959650014A1FC /* MainInterface.storyboard */; };
|
||||||
|
6143FF43290959650014A1FC /* test.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6143FF35290959650014A1FC /* test.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; };
|
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
@ -18,6 +23,16 @@
|
||||||
EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
6143FF41290959650014A1FC /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 6143FF34290959650014A1FC;
|
||||||
|
remoteInfo = test;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
6174FE1725CEB74E00A5020C /* Embed App Extensions */ = {
|
6174FE1725CEB74E00A5020C /* Embed App Extensions */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
@ -25,6 +40,7 @@
|
||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 13;
|
dstSubfolderSpec = 13;
|
||||||
files = (
|
files = (
|
||||||
|
6143FF43290959650014A1FC /* test.appex in Embed App Extensions */,
|
||||||
);
|
);
|
||||||
name = "Embed App Extensions";
|
name = "Embed App Extensions";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -35,6 +51,12 @@
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
6143FF35290959650014A1FC /* test.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = test.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
6143FF36290959650014A1FC /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
|
||||||
|
6143FF39290959650014A1FC /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
|
||||||
|
6143FF3B290959650014A1FC /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
6143FF3E290959650014A1FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
|
6143FF40290959650014A1FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||||
61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
@ -53,6 +75,14 @@
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
6143FF32290959650014A1FC /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6143FF37290959650014A1FC /* UniformTypeIdentifiers.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -72,10 +102,22 @@
|
||||||
61FF730123634DD10069C557 /* flutter_downloader.framework */,
|
61FF730123634DD10069C557 /* flutter_downloader.framework */,
|
||||||
61FF72FF23634CA10069C557 /* libsqlite3.tbd */,
|
61FF72FF23634CA10069C557 /* libsqlite3.tbd */,
|
||||||
B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */,
|
B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */,
|
||||||
|
6143FF36290959650014A1FC /* UniformTypeIdentifiers.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
6143FF38290959650014A1FC /* test */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
6143FF39290959650014A1FC /* Media.xcassets */,
|
||||||
|
6143FF3B290959650014A1FC /* ActionViewController.swift */,
|
||||||
|
6143FF3D290959650014A1FC /* MainInterface.storyboard */,
|
||||||
|
6143FF40290959650014A1FC /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = test;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
647DC95AB5350DB6D2264FFE /* Pods */ = {
|
647DC95AB5350DB6D2264FFE /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -101,6 +143,7 @@
|
||||||
children = (
|
children = (
|
||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
6143FF38290959650014A1FC /* test */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
647DC95AB5350DB6D2264FFE /* Pods */,
|
647DC95AB5350DB6D2264FFE /* Pods */,
|
||||||
50BAF1DF19E018DDF2B149B9 /* Frameworks */,
|
50BAF1DF19E018DDF2B149B9 /* Frameworks */,
|
||||||
|
@ -111,6 +154,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
6143FF35290959650014A1FC /* test.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -141,6 +185,23 @@
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
6143FF34290959650014A1FC /* test */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 6143FF46290959660014A1FC /* Build configuration list for PBXNativeTarget "test" */;
|
||||||
|
buildPhases = (
|
||||||
|
6143FF31290959650014A1FC /* Sources */,
|
||||||
|
6143FF32290959650014A1FC /* Frameworks */,
|
||||||
|
6143FF33290959650014A1FC /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = test;
|
||||||
|
productName = test;
|
||||||
|
productReference = 6143FF35290959650014A1FC /* test.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
@ -157,6 +218,7 @@
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
6143FF42290959650014A1FC /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
|
@ -169,10 +231,15 @@
|
||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1240;
|
LastSwiftUpdateCheck = 1400;
|
||||||
LastUpgradeCheck = 1300;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "The Chromium Authors";
|
ORGANIZATIONNAME = "The Chromium Authors";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
6143FF34290959650014A1FC = {
|
||||||
|
CreatedOnToolsVersion = 14.0;
|
||||||
|
DevelopmentTeam = PFP8UV45Y6;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
|
};
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
DevelopmentTeam = PFP8UV45Y6;
|
DevelopmentTeam = PFP8UV45Y6;
|
||||||
|
@ -200,11 +267,21 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
97C146ED1CF9000F007C117D /* Runner */,
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
6143FF34290959650014A1FC /* test */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
6143FF33290959650014A1FC /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6143FF3A290959650014A1FC /* Media.xcassets in Resources */,
|
||||||
|
6143FF3F290959650014A1FC /* MainInterface.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -296,6 +373,14 @@
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
6143FF31290959650014A1FC /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6143FF3C290959650014A1FC /* ActionViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -307,7 +392,23 @@
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
6143FF42290959650014A1FC /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 6143FF34290959650014A1FC /* test */;
|
||||||
|
targetProxy = 6143FF41290959650014A1FC /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
6143FF3D290959650014A1FC /* MainInterface.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
6143FF3E290959650014A1FC /* Base */,
|
||||||
|
);
|
||||||
|
name = MainInterface.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -327,6 +428,72 @@
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
6143FF44290959660014A1FC /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = PFP8UV45Y6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = test/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = test;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 The Chromium Authors. All rights reserved.";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-6-Example.test";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
6143FF45290959660014A1FC /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = PFP8UV45Y6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = test/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = test;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 The Chromium Authors. All rights reserved.";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-6-Example.test";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -442,6 +609,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
@ -459,7 +627,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-Example";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-6-Example";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -473,6 +641,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
@ -490,7 +659,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-Example";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.pichillilorenzo.flutter-inappwebview-6-Example";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -502,6 +671,15 @@
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
6143FF46290959660014A1FC /* Build configuration list for PBXNativeTarget "test" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
6143FF44290959660014A1FC /* Debug */,
|
||||||
|
6143FF45290959660014A1FC /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// ActionViewController.swift
|
||||||
|
// test
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 26/10/22.
|
||||||
|
// Copyright © 2022 The Chromium Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MobileCoreServices
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
class ActionViewController: UIViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// Get the item[s] we're handling from the extension context.
|
||||||
|
|
||||||
|
// For example, look for an image and place it into an image view.
|
||||||
|
// Replace this with something appropriate for the type[s] your extension supports.
|
||||||
|
var imageFound = false
|
||||||
|
for item in self.extensionContext!.inputItems as! [NSExtensionItem] {
|
||||||
|
for provider in item.attachments! {
|
||||||
|
if provider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
|
||||||
|
// This is an image. We'll load it, then place it in our image view.
|
||||||
|
weak var weakImageView = self.imageView
|
||||||
|
provider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil, completionHandler: { (imageURL, error) in
|
||||||
|
OperationQueue.main.addOperation {
|
||||||
|
if let strongImageView = weakImageView {
|
||||||
|
if let imageURL = imageURL as? URL {
|
||||||
|
strongImageView.image = UIImage(data: try! Data(contentsOf: imageURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
imageFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageFound) {
|
||||||
|
// We only handle one image, so stop looking for more.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func done() {
|
||||||
|
// Return any edited content to the host app.
|
||||||
|
// This template doesn't do anything, so we just echo the passed in items.
|
||||||
|
self.extensionContext!.completeRequest(returningItems: self.extensionContext!.inputItems, completionHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ObA-dk-sSI">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Image-->
|
||||||
|
<scene sceneID="7MM-of-jgj">
|
||||||
|
<objects>
|
||||||
|
<viewController title="Image" id="ObA-dk-sSI" customClass="ActionViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="zMn-AG-sqS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="528"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<navigationBar contentMode="scaleToFill" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="NOA-Dm-cuz">
|
||||||
|
<items>
|
||||||
|
<navigationItem id="3HJ-uW-3hn">
|
||||||
|
<barButtonItem key="leftBarButtonItem" title="Done" style="done" id="WYi-yp-eM6">
|
||||||
|
<connections>
|
||||||
|
<action selector="done" destination="ObA-dk-sSI" id="Qdu-qn-U6V"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
|
</items>
|
||||||
|
</navigationBar>
|
||||||
|
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="9ga-4F-77Z"/>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="VVe-Uw-JpX" firstAttribute="trailing" secondItem="NOA-Dm-cuz" secondAttribute="trailing" id="A05-Pj-hrr"/>
|
||||||
|
<constraint firstItem="9ga-4F-77Z" firstAttribute="top" secondItem="NOA-Dm-cuz" secondAttribute="bottom" id="Fps-3D-QQW"/>
|
||||||
|
<constraint firstItem="NOA-Dm-cuz" firstAttribute="leading" secondItem="VVe-Uw-JpX" secondAttribute="leading" id="HxO-8t-aoh"/>
|
||||||
|
<constraint firstItem="VVe-Uw-JpX" firstAttribute="trailing" secondItem="9ga-4F-77Z" secondAttribute="trailing" id="Ozw-Hg-0yh"/>
|
||||||
|
<constraint firstItem="9ga-4F-77Z" firstAttribute="leading" secondItem="VVe-Uw-JpX" secondAttribute="leading" id="XH5-ld-ONA"/>
|
||||||
|
<constraint firstItem="VVe-Uw-JpX" firstAttribute="bottom" secondItem="9ga-4F-77Z" secondAttribute="bottom" id="eQg-nn-Zy4"/>
|
||||||
|
<constraint firstItem="NOA-Dm-cuz" firstAttribute="top" secondItem="VVe-Uw-JpX" secondAttribute="top" id="we0-1t-bgp"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="VVe-Uw-JpX"/>
|
||||||
|
</view>
|
||||||
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
|
<size key="freeformSize" width="320" height="528"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="9ga-4F-77Z" id="5y6-5w-9QO"/>
|
||||||
|
<outlet property="view" destination="zMn-AG-sqS" id="Qma-de-2ek"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="X47-rx-isc" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<string>TRUEPREDICATE</string>
|
||||||
|
<key>NSExtensionServiceAllowsFinderPreviewItem</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionServiceAllowsTouchBarItem</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionServiceFinderPreviewIconName</key>
|
||||||
|
<string>NSActionTemplate</string>
|
||||||
|
<key>NSExtensionServiceTouchBarBezelColorName</key>
|
||||||
|
<string>TouchBarBezel</string>
|
||||||
|
<key>NSExtensionServiceTouchBarIconName</key>
|
||||||
|
<string>NSActionTemplate</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.ui-services</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"color" : {
|
||||||
|
"reference" : "systemPurpleColor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCompletedInitialLoad() {
|
void onCompletedInitialLoad(didLoadSuccessfully) {
|
||||||
print("ChromeSafari browser initial load completed");
|
print("ChromeSafari browser initial load completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ class _ChromeSafariBrowserExampleScreenState
|
||||||
id: 1,
|
id: 1,
|
||||||
description: 'Action Button description',
|
description: 'Action Button description',
|
||||||
icon: actionButtonIcon.buffer.asUint8List(),
|
icon: actionButtonIcon.buffer.asUint8List(),
|
||||||
action: (url, title) {
|
onClick: (url, title) {
|
||||||
print('Action Button 1 clicked!');
|
print('Action Button 1 clicked!');
|
||||||
print(url);
|
print(url);
|
||||||
print(title);
|
print(title);
|
||||||
|
@ -48,7 +48,8 @@ class _ChromeSafariBrowserExampleScreenState
|
||||||
widget.browser.addMenuItem(ChromeSafariBrowserMenuItem(
|
widget.browser.addMenuItem(ChromeSafariBrowserMenuItem(
|
||||||
id: 2,
|
id: 2,
|
||||||
label: 'Custom item menu 1',
|
label: 'Custom item menu 1',
|
||||||
action: (url, title) {
|
image: UIImage(systemName: "sun.max"),
|
||||||
|
onClick: (url, title) {
|
||||||
print('Custom item menu 1 clicked!');
|
print('Custom item menu 1 clicked!');
|
||||||
print(url);
|
print(url);
|
||||||
print(title);
|
print(title);
|
||||||
|
@ -56,7 +57,8 @@ class _ChromeSafariBrowserExampleScreenState
|
||||||
widget.browser.addMenuItem(ChromeSafariBrowserMenuItem(
|
widget.browser.addMenuItem(ChromeSafariBrowserMenuItem(
|
||||||
id: 3,
|
id: 3,
|
||||||
label: 'Custom item menu 2',
|
label: 'Custom item menu 2',
|
||||||
action: (url, title) {
|
image: UIImage(systemName: "pencil"),
|
||||||
|
onClick: (url, title) {
|
||||||
print('Custom item menu 2 clicked!');
|
print('Custom item menu 2 clicked!');
|
||||||
print(url);
|
print(url);
|
||||||
print(title);
|
print(title);
|
||||||
|
@ -82,6 +84,22 @@ class _ChromeSafariBrowserExampleScreenState
|
||||||
isSingleInstance: false,
|
isSingleInstance: false,
|
||||||
isTrustedWebActivity: false,
|
isTrustedWebActivity: false,
|
||||||
keepAliveEnabled: true,
|
keepAliveEnabled: true,
|
||||||
|
startAnimations: [
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "slide_in_left", defPackage: "android"),
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "slide_out_right", defPackage: "android")
|
||||||
|
],
|
||||||
|
exitAnimations: [
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "abc_slide_in_top",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample"),
|
||||||
|
AndroidResource.anim(
|
||||||
|
name: "abc_slide_out_top",
|
||||||
|
defPackage:
|
||||||
|
"com.pichillilorenzo.flutter_inappwebviewexample")
|
||||||
|
],
|
||||||
dismissButtonStyle: DismissButtonStyle.CLOSE,
|
dismissButtonStyle: DismissButtonStyle.CLOSE,
|
||||||
presentationStyle:
|
presentationStyle:
|
||||||
ModalPresentationStyle.OVER_FULL_SCREEN));
|
ModalPresentationStyle.OVER_FULL_SCREEN));
|
||||||
|
|
|
@ -16,6 +16,8 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
|
||||||
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_chromesafaribrowser"
|
static let METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_chromesafaribrowser"
|
||||||
static var registrar: FlutterPluginRegistrar?
|
static var registrar: FlutterPluginRegistrar?
|
||||||
static var browsers: [String: SafariViewController?] = [:]
|
static var browsers: [String: SafariViewController?] = [:]
|
||||||
|
@available(iOS 15.0, *)
|
||||||
|
static var prewarmingTokens: [String: SFSafariViewController.PrewarmingToken?] = [:]
|
||||||
|
|
||||||
init(registrar: FlutterPluginRegistrar) {
|
init(registrar: FlutterPluginRegistrar) {
|
||||||
super.init(channel: FlutterMethodChannel(name: ChromeSafariBrowserManager.METHOD_CHANNEL_NAME, binaryMessenger: registrar.messenger()))
|
super.init(channel: FlutterMethodChannel(name: ChromeSafariBrowserManager.METHOD_CHANNEL_NAME, binaryMessenger: registrar.messenger()))
|
||||||
|
@ -40,6 +42,44 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
|
||||||
result(false)
|
result(false)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case "clearWebsiteData":
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
SFSafariViewController.DataStore.default.clearWebsiteData {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
|
case "prewarmConnections":
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
let stringURLs = arguments!["URLs"] as! [String]
|
||||||
|
var URLs: [URL] = []
|
||||||
|
for stringURL in stringURLs {
|
||||||
|
if let url = URL(string: stringURL) {
|
||||||
|
URLs.append(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let prewarmingToken = SFSafariViewController.prewarmConnections(to: URLs)
|
||||||
|
let prewarmingTokenId = NSUUID().uuidString
|
||||||
|
ChromeSafariBrowserManager.prewarmingTokens[prewarmingTokenId] = prewarmingToken
|
||||||
|
result([
|
||||||
|
"id": prewarmingTokenId
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
result(nil)
|
||||||
|
}
|
||||||
|
case "invalidatePrewarmingToken":
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
let prewarmingToken = arguments!["prewarmingToken"] as! [String:Any?]
|
||||||
|
if let prewarmingTokenId = prewarmingToken["id"] as? String,
|
||||||
|
let prewarmingToken = ChromeSafariBrowserManager.prewarmingTokens[prewarmingTokenId] {
|
||||||
|
prewarmingToken?.invalidate()
|
||||||
|
ChromeSafariBrowserManager.prewarmingTokens[prewarmingTokenId] = nil
|
||||||
|
}
|
||||||
|
result(true)
|
||||||
|
} else {
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
break
|
break
|
||||||
|
@ -61,9 +101,6 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
|
||||||
|
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
let config = SFSafariViewController.Configuration()
|
let config = SFSafariViewController.Configuration()
|
||||||
config.entersReaderIfAvailable = safariSettings.entersReaderIfAvailable
|
|
||||||
config.barCollapsingEnabled = safariSettings.barCollapsingEnabled
|
|
||||||
|
|
||||||
safari = SafariViewController(id: id, url: absoluteUrl, configuration: config,
|
safari = SafariViewController(id: id, url: absoluteUrl, configuration: config,
|
||||||
menuItemList: menuItemList, safariSettings: safariSettings)
|
menuItemList: menuItemList, safariSettings: safariSettings)
|
||||||
} else {
|
} else {
|
||||||
|
@ -95,6 +132,12 @@ public class ChromeSafariBrowserManager: ChannelDelegate {
|
||||||
browser?.dispose()
|
browser?.dispose()
|
||||||
}
|
}
|
||||||
ChromeSafariBrowserManager.browsers.removeAll()
|
ChromeSafariBrowserManager.browsers.removeAll()
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
ChromeSafariBrowserManager.prewarmingTokens.values.forEach { (prewarmingToken: SFSafariViewController.PrewarmingToken?) in
|
||||||
|
prewarmingToken?.invalidate()
|
||||||
|
}
|
||||||
|
ChromeSafariBrowserManager.prewarmingTokens.removeAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
|
@ -48,6 +48,6 @@ class CustomUIActivity : UIActivity {
|
||||||
|
|
||||||
override func perform() {
|
override func perform() {
|
||||||
let browser = ChromeSafariBrowserManager.browsers[viewId]
|
let browser = ChromeSafariBrowserManager.browsers[viewId]
|
||||||
browser??.channelDelegate?.onChromeSafariBrowserMenuItemActionPerform(id: id, url: url, title: title)
|
browser??.channelDelegate?.onItemActionPerform(id: id, url: url, title: title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,21 @@ public class SafariBrowserSettings: ISettings<SafariViewController> {
|
||||||
var preferredBarTintColor: String?
|
var preferredBarTintColor: String?
|
||||||
var preferredControlTintColor: String?
|
var preferredControlTintColor: String?
|
||||||
var presentationStyle = 0 //fullscreen
|
var presentationStyle = 0 //fullscreen
|
||||||
var transitionStyle = 0 //crossDissolve
|
var transitionStyle = 0 //coverVertical
|
||||||
|
var activityButton: [String:Any?]?
|
||||||
|
var eventAttribution: [String:Any?]?
|
||||||
|
|
||||||
override init(){
|
override init(){
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func parse(settings: [String: Any?]) -> SafariBrowserSettings {
|
||||||
|
let _ = super.parse(settings: settings)
|
||||||
|
activityButton = settings["activityButton"] as? [String : Any?]
|
||||||
|
eventAttribution = settings["eventAttribution"] as? [String : Any?]
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
override func getRealSettings(obj: SafariViewController?) -> [String: Any?] {
|
override func getRealSettings(obj: SafariViewController?) -> [String: Any?] {
|
||||||
var realOptions: [String: Any?] = toMap()
|
var realOptions: [String: Any?] = toMap()
|
||||||
if let safariViewController = obj {
|
if let safariViewController = obj {
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class SafariViewController: SFSafariViewController, SFSafariViewControlle
|
||||||
self.id = id
|
self.id = id
|
||||||
self.menuItemList = menuItemList
|
self.menuItemList = menuItemList
|
||||||
self.safariSettings = safariSettings
|
self.safariSettings = safariSettings
|
||||||
|
SafariViewController.prepareConfig(configuration: configuration, safariSettings: safariSettings)
|
||||||
super.init(url: url, configuration: configuration)
|
super.init(url: url, configuration: configuration)
|
||||||
let channel = FlutterMethodChannel(name: SafariViewController.METHOD_CHANNEL_NAME_PREFIX + id,
|
let channel = FlutterMethodChannel(name: SafariViewController.METHOD_CHANNEL_NAME_PREFIX + id,
|
||||||
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
||||||
|
@ -39,19 +40,17 @@ public class SafariViewController: SFSafariViewController, SFSafariViewControlle
|
||||||
self.delegate = self
|
self.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewWillAppear(_ animated: Bool) {
|
public static func prepareConfig(configuration: SFSafariViewController.Configuration, safariSettings: SafariBrowserSettings) {
|
||||||
// prepareSafariBrowser()
|
configuration.entersReaderIfAvailable = safariSettings.entersReaderIfAvailable
|
||||||
super.viewWillAppear(animated)
|
configuration.barCollapsingEnabled = safariSettings.barCollapsingEnabled
|
||||||
channelDelegate?.onChromeSafariBrowserOpened()
|
if #available(iOS 15.0, *), let activityButtonMap = safariSettings.activityButton {
|
||||||
|
configuration.activityButton = .fromMap(map: activityButtonMap)
|
||||||
|
}
|
||||||
|
if #available(iOS 15.2, *), let eventAttributionMap = safariSettings.eventAttribution {
|
||||||
|
configuration.eventAttribution = .fromMap(map: eventAttributionMap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewDidDisappear(_ animated: Bool) {
|
|
||||||
super.viewDidDisappear(animated)
|
|
||||||
channelDelegate?.onChromeSafariBrowserClosed()
|
|
||||||
self.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func prepareSafariBrowser() {
|
func prepareSafariBrowser() {
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: safariSettings.dismissButtonStyle)!
|
self.dismissButtonStyle = SFSafariViewController.DismissButtonStyle(rawValue: safariSettings.dismissButtonStyle)!
|
||||||
|
@ -70,6 +69,18 @@ public class SafariViewController: SFSafariViewController, SFSafariViewControlle
|
||||||
self.modalTransitionStyle = UIModalTransitionStyle(rawValue: safariSettings.transitionStyle)!
|
self.modalTransitionStyle = UIModalTransitionStyle(rawValue: safariSettings.transitionStyle)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func viewWillAppear(_ animated: Bool) {
|
||||||
|
// prepareSafariBrowser()
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
channelDelegate?.onOpened()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
channelDelegate?.onClosed()
|
||||||
|
self.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
func close(result: FlutterResult?) {
|
func close(result: FlutterResult?) {
|
||||||
dismiss(animated: true)
|
dismiss(animated: true)
|
||||||
|
|
||||||
|
@ -87,34 +98,25 @@ public class SafariViewController: SFSafariViewController, SFSafariViewControlle
|
||||||
|
|
||||||
public func safariViewController(_ controller: SFSafariViewController,
|
public func safariViewController(_ controller: SFSafariViewController,
|
||||||
didCompleteInitialLoad didLoadSuccessfully: Bool) {
|
didCompleteInitialLoad didLoadSuccessfully: Bool) {
|
||||||
if didLoadSuccessfully {
|
channelDelegate?.onCompletedInitialLoad(didLoadSuccessfully: didLoadSuccessfully)
|
||||||
channelDelegate?.onChromeSafariBrowserCompletedInitialLoad()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
print("Cant load successfully the 'SafariViewController'.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: URL, title: String?) -> [UIActivity] {
|
public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: URL, title: String?) -> [UIActivity] {
|
||||||
var uiActivities: [UIActivity] = []
|
var uiActivities: [UIActivity] = []
|
||||||
menuItemList.forEach { (menuItem) in
|
menuItemList.forEach { (menuItem) in
|
||||||
let activity = CustomUIActivity(viewId: id, id: menuItem["id"] as! Int64, url: URL, title: title, label: menuItem["label"] as? String, type: nil, image: nil)
|
let activity = CustomUIActivity(viewId: id, id: menuItem["id"] as! Int64, url: URL, title: title, label: menuItem["label"] as? String, type: nil, image: .fromMap(map: menuItem["image"] as? [String:Any?]))
|
||||||
uiActivities.append(activity)
|
uiActivities.append(activity)
|
||||||
}
|
}
|
||||||
return uiActivities
|
return uiActivities
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// public func safariViewController(_ controller: SFSafariViewController, excludedActivityTypesFor URL: URL, title: String?) -> [UIActivity.ActivityType] {
|
public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo url: URL) {
|
||||||
// print("excludedActivityTypesFor")
|
channelDelegate?.onInitialLoadDidRedirect(url: url)
|
||||||
// print(URL)
|
}
|
||||||
// print(title)
|
|
||||||
// return []
|
public func safariViewControllerWillOpenInBrowser(_ controller: SFSafariViewController) {
|
||||||
// }
|
channelDelegate?.onWillOpenInBrowser()
|
||||||
//
|
}
|
||||||
// public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
|
|
||||||
// print("initialLoadDidRedirectTo")
|
|
||||||
// print(URL)
|
|
||||||
// }
|
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
channelDelegate?.dispose()
|
channelDelegate?.dispose()
|
||||||
|
|
|
@ -31,28 +31,42 @@ public class SafariViewControllerChannelDelegate : ChannelDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserOpened() {
|
public func onOpened() {
|
||||||
let arguments: [String: Any?] = [:]
|
let arguments: [String: Any?] = [:]
|
||||||
channel?.invokeMethod("onChromeSafariBrowserOpened", arguments: arguments)
|
channel?.invokeMethod("onOpened", arguments: arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserCompletedInitialLoad() {
|
public func onCompletedInitialLoad(didLoadSuccessfully: Bool) {
|
||||||
let arguments: [String: Any?] = [:]
|
let arguments: [String: Any?] = [
|
||||||
channel?.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: arguments)
|
"didLoadSuccessfully": didLoadSuccessfully
|
||||||
|
]
|
||||||
|
channel?.invokeMethod("onCompletedInitialLoad", arguments: arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserClosed() {
|
public func onInitialLoadDidRedirect(url: URL) {
|
||||||
let arguments: [String: Any?] = [:]
|
let arguments: [String: Any?] = [
|
||||||
channel?.invokeMethod("onChromeSafariBrowserClosed", arguments: arguments)
|
"url": url.absoluteString
|
||||||
|
]
|
||||||
|
channel?.invokeMethod("onInitialLoadDidRedirect", arguments: arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserMenuItemActionPerform(id: Int64, url: URL, title: String?) {
|
public func onWillOpenInBrowser() {
|
||||||
|
let arguments: [String: Any?] = [:]
|
||||||
|
channel?.invokeMethod("onWillOpenInBrowser", arguments: arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onClosed() {
|
||||||
|
let arguments: [String: Any?] = [:]
|
||||||
|
channel?.invokeMethod("onClosed", arguments: arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onItemActionPerform(id: Int64, url: URL, title: String?) {
|
||||||
let arguments: [String: Any?] = [
|
let arguments: [String: Any?] = [
|
||||||
"id": id,
|
"id": id,
|
||||||
"url": url.absoluteString,
|
"url": url.absoluteString,
|
||||||
"title": title,
|
"title": title,
|
||||||
]
|
]
|
||||||
channel?.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", arguments: arguments)
|
channel?.invokeMethod("onItemActionPerform", arguments: arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func dispose() {
|
public override func dispose() {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// ActivityButton.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 26/10/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SafariServices
|
||||||
|
|
||||||
|
@available(iOS 15.0, *)
|
||||||
|
extension SFSafariViewController.ActivityButton {
|
||||||
|
public static func fromMap(map: [String:Any?]?) -> SFSafariViewController.ActivityButton? {
|
||||||
|
guard let map = map else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if let templateImageMap = map["templateImage"] as? [String:Any?],
|
||||||
|
let templateImage = UIImage.fromMap(map: templateImageMap),
|
||||||
|
let extensionIdentifier = map["extensionIdentifier"] as? String {
|
||||||
|
return .init(templateImage: templateImage, extensionIdentifier: extensionIdentifier)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// UIEventAttribution.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 26/10/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@available(iOS 14.5, *)
|
||||||
|
extension UIEventAttribution {
|
||||||
|
public static func fromMap(map: [String:Any?]?) -> UIEventAttribution? {
|
||||||
|
guard let map = map else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if let sourceIdentifier = map["sourceIdentifier"] as? UInt8,
|
||||||
|
let destinationURLString = map["destinationURL"] as? String,
|
||||||
|
let destinationURL = URL(string: destinationURLString),
|
||||||
|
let sourceDescription = map["sourceDescription"] as? String,
|
||||||
|
let purchaser = map["purchaser"] as? String {
|
||||||
|
return UIEventAttribution(sourceIdentifier: sourceIdentifier,
|
||||||
|
destinationURL: destinationURL,
|
||||||
|
sourceDescription: sourceDescription,
|
||||||
|
purchaser: purchaser)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// UIImage.swift
|
||||||
|
// flutter_inappwebview
|
||||||
|
//
|
||||||
|
// Created by Lorenzo Pichilli on 26/10/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Flutter
|
||||||
|
|
||||||
|
extension UIImage {
|
||||||
|
public static func fromMap(map: [String:Any?]?) -> UIImage? {
|
||||||
|
guard let map = map else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if let name = map["name"] as? String {
|
||||||
|
return UIImage(named: name)
|
||||||
|
}
|
||||||
|
if #available(iOS 13.0, *), let systemName = map["systemName"] as? String {
|
||||||
|
return UIImage(systemName: systemName)
|
||||||
|
}
|
||||||
|
if let data = map["data"] as? FlutterStandardTypedData {
|
||||||
|
return UIImage(data: data.data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -217,6 +217,10 @@ class WebViewFeature_ {
|
||||||
const WebViewFeature_._internal(
|
const WebViewFeature_._internal(
|
||||||
"ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY");
|
"ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY");
|
||||||
|
|
||||||
|
///This feature covers [InAppWebViewController.getVariationsHeader].
|
||||||
|
static const GET_VARIATIONS_HEADER =
|
||||||
|
const WebViewFeature_._internal("GET_VARIATIONS_HEADER");
|
||||||
|
|
||||||
///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
|
///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
|
||||||
///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device,
|
///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device,
|
||||||
///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`.
|
///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`.
|
||||||
|
|
|
@ -217,6 +217,10 @@ class WebViewFeature {
|
||||||
WebViewFeature._internal('ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY',
|
WebViewFeature._internal('ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY',
|
||||||
'ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY');
|
'ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY');
|
||||||
|
|
||||||
|
///This feature covers [InAppWebViewController.getVariationsHeader].
|
||||||
|
static const GET_VARIATIONS_HEADER = WebViewFeature._internal(
|
||||||
|
'GET_VARIATIONS_HEADER', 'GET_VARIATIONS_HEADER');
|
||||||
|
|
||||||
///Set of all values of [WebViewFeature].
|
///Set of all values of [WebViewFeature].
|
||||||
static final Set<WebViewFeature> values = [
|
static final Set<WebViewFeature> values = [
|
||||||
WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL,
|
WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL,
|
||||||
|
@ -266,6 +270,7 @@ class WebViewFeature {
|
||||||
WebViewFeature.ALGORITHMIC_DARKENING,
|
WebViewFeature.ALGORITHMIC_DARKENING,
|
||||||
WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
|
WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
|
||||||
WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
|
WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
|
||||||
|
WebViewFeature.GET_VARIATIONS_HEADER,
|
||||||
].toSet();
|
].toSet();
|
||||||
|
|
||||||
///Gets a possible [WebViewFeature] instance from [String] value.
|
///Gets a possible [WebViewFeature] instance from [String] value.
|
||||||
|
|
|
@ -4,6 +4,11 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import '../types/android_resource.dart';
|
||||||
|
import '../types/custom_tabs_navigation_event_type.dart';
|
||||||
|
import '../types/custom_tabs_relation_type.dart';
|
||||||
|
import '../types/prewarming_token.dart';
|
||||||
|
import '../types/ui_image.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
import '../debug_logging_settings.dart';
|
import '../debug_logging_settings.dart';
|
||||||
|
|
||||||
|
@ -41,7 +46,7 @@ class ChromeSafariBrowserNotOpenedException implements Exception {
|
||||||
///(you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
|
///(you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android native WebView
|
///- Android
|
||||||
///- iOS
|
///- iOS
|
||||||
class ChromeSafariBrowser {
|
class ChromeSafariBrowser {
|
||||||
///Debug settings.
|
///Debug settings.
|
||||||
|
@ -52,6 +57,7 @@ class ChromeSafariBrowser {
|
||||||
|
|
||||||
ChromeSafariBrowserActionButton? _actionButton;
|
ChromeSafariBrowserActionButton? _actionButton;
|
||||||
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
|
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
|
||||||
|
ChromeSafariBrowserSecondaryToolbar? _secondaryToolbar;
|
||||||
bool _isOpened = false;
|
bool _isOpened = false;
|
||||||
late MethodChannel _channel;
|
late MethodChannel _channel;
|
||||||
static const MethodChannel _sharedChannel =
|
static const MethodChannel _sharedChannel =
|
||||||
|
@ -85,24 +91,87 @@ class ChromeSafariBrowser {
|
||||||
_debugLog(call.method, call.arguments);
|
_debugLog(call.method, call.arguments);
|
||||||
|
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "onChromeSafariBrowserOpened":
|
case "onServiceConnected":
|
||||||
|
onServiceConnected();
|
||||||
|
break;
|
||||||
|
case "onOpened":
|
||||||
onOpened();
|
onOpened();
|
||||||
break;
|
break;
|
||||||
case "onChromeSafariBrowserCompletedInitialLoad":
|
case "onCompletedInitialLoad":
|
||||||
onCompletedInitialLoad();
|
final bool? didLoadSuccessfully = call.arguments["didLoadSuccessfully"];
|
||||||
|
onCompletedInitialLoad(didLoadSuccessfully);
|
||||||
break;
|
break;
|
||||||
case "onChromeSafariBrowserClosed":
|
case "onInitialLoadDidRedirect":
|
||||||
|
final String? url = call.arguments["url"];
|
||||||
|
final Uri? uri = url != null ? Uri.tryParse(url) : null;
|
||||||
|
onInitialLoadDidRedirect(uri);
|
||||||
|
break;
|
||||||
|
case "onNavigationEvent":
|
||||||
|
final navigationEvent = CustomTabsNavigationEventType.fromNativeValue(
|
||||||
|
call.arguments["navigationEvent"]);
|
||||||
|
onNavigationEvent(navigationEvent);
|
||||||
|
break;
|
||||||
|
case "onRelationshipValidationResult":
|
||||||
|
final relation =
|
||||||
|
CustomTabsRelationType.fromNativeValue(call.arguments["relation"]);
|
||||||
|
final requestedOrigin = call.arguments["requestedOrigin"] != null
|
||||||
|
? Uri.tryParse(call.arguments["requestedOrigin"])
|
||||||
|
: null;
|
||||||
|
final bool result = call.arguments["result"];
|
||||||
|
onRelationshipValidationResult(relation, requestedOrigin, result);
|
||||||
|
break;
|
||||||
|
case "onWillOpenInBrowser":
|
||||||
|
onWillOpenInBrowser();
|
||||||
|
break;
|
||||||
|
case "onClosed":
|
||||||
onClosed();
|
onClosed();
|
||||||
this._isOpened = false;
|
this._isOpened = false;
|
||||||
break;
|
break;
|
||||||
case "onChromeSafariBrowserItemActionPerform":
|
case "onItemActionPerform":
|
||||||
String url = call.arguments["url"];
|
String url = call.arguments["url"];
|
||||||
String title = call.arguments["title"];
|
String title = call.arguments["title"];
|
||||||
int id = call.arguments["id"].toInt();
|
int id = call.arguments["id"].toInt();
|
||||||
if (this._actionButton?.id == id) {
|
if (this._actionButton?.id == id) {
|
||||||
this._actionButton?.action(url, title);
|
if (this._actionButton?.action != null) {
|
||||||
|
this._actionButton?.action!(url, title);
|
||||||
|
}
|
||||||
|
if (this._actionButton?.onClick != null) {
|
||||||
|
this._actionButton?.onClick!(Uri.tryParse(url), title);
|
||||||
|
}
|
||||||
} else if (this._menuItems[id] != null) {
|
} else if (this._menuItems[id] != null) {
|
||||||
this._menuItems[id]?.action(url, title);
|
if (this._menuItems[id]?.action != null) {
|
||||||
|
this._menuItems[id]?.action!(url, title);
|
||||||
|
}
|
||||||
|
if (this._menuItems[id]?.onClick != null) {
|
||||||
|
this._menuItems[id]?.onClick!(Uri.tryParse(url), title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "onSecondaryItemActionPerform":
|
||||||
|
final clickableIDs = this._secondaryToolbar?.clickableIDs;
|
||||||
|
if (clickableIDs != null) {
|
||||||
|
Uri? url = call.arguments["url"] != null
|
||||||
|
? Uri.tryParse(call.arguments["url"])
|
||||||
|
: null;
|
||||||
|
String name = call.arguments["name"];
|
||||||
|
for (final clickable in clickableIDs) {
|
||||||
|
var clickableFullname = clickable.id.name;
|
||||||
|
if (clickable.id.defType != null &&
|
||||||
|
!clickableFullname.contains("/")) {
|
||||||
|
clickableFullname = "${clickable.id.defType}/$clickableFullname";
|
||||||
|
}
|
||||||
|
if (clickable.id.defPackage != null &&
|
||||||
|
!clickableFullname.contains(":")) {
|
||||||
|
clickableFullname =
|
||||||
|
"${clickable.id.defPackage}:$clickableFullname";
|
||||||
|
}
|
||||||
|
if (clickableFullname == name) {
|
||||||
|
if (clickable.onClick != null) {
|
||||||
|
clickable.onClick!(url);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -112,25 +181,43 @@ class ChromeSafariBrowser {
|
||||||
|
|
||||||
///Opens the [ChromeSafariBrowser] instance with an [url].
|
///Opens the [ChromeSafariBrowser] instance with an [url].
|
||||||
///
|
///
|
||||||
///[url]: The [url] to load. On iOS, the [url] must use the `http` or `https` scheme.
|
///[url] - The [url] to load. On iOS, the [url] is required and must use the `http` or `https` scheme.
|
||||||
///
|
///
|
||||||
///[options]: Options for the [ChromeSafariBrowser].
|
///[headers] (Supported only on Android) - [whitelisted](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) cross-origin request headers.
|
||||||
|
///It is possible to attach non-whitelisted headers to cross-origin requests, when the server and client are related using a
|
||||||
|
///[digital asset link](https://developers.google.com/digital-asset-links/v1/getting-started).
|
||||||
///
|
///
|
||||||
///[settings]: Settings for the [ChromeSafariBrowser].
|
///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order. Supported only on Android.
|
||||||
|
///
|
||||||
|
///[referrer] - referrer header. Supported only on Android.
|
||||||
|
///
|
||||||
|
///[options] - Deprecated. Use `settings` instead.
|
||||||
|
///
|
||||||
|
///[settings] - Settings for the [ChromeSafariBrowser].
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
Future<void> open(
|
Future<void> open(
|
||||||
{required Uri url,
|
{Uri? url,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
List<Uri>? otherLikelyURLs,
|
||||||
|
Uri? referrer,
|
||||||
@Deprecated('Use settings instead')
|
@Deprecated('Use settings instead')
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
ChromeSafariBrowserClassOptions? options,
|
ChromeSafariBrowserClassOptions? options,
|
||||||
ChromeSafariBrowserSettings? settings}) async {
|
ChromeSafariBrowserSettings? settings}) async {
|
||||||
assert(url.toString().isNotEmpty);
|
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS) {
|
||||||
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
|
assert(url != null, 'The specified URL must not be null on iOS.');
|
||||||
if (!kIsWeb &&
|
assert(['http', 'https'].contains(url!.scheme),
|
||||||
(defaultTargetPlatform == TargetPlatform.iOS ||
|
|
||||||
defaultTargetPlatform == TargetPlatform.macOS)) {
|
|
||||||
assert(['http', 'https'].contains(url.scheme),
|
|
||||||
'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.');
|
'The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported on iOS.');
|
||||||
}
|
}
|
||||||
|
if (url != null) {
|
||||||
|
assert(url.toString().isNotEmpty, 'The specified URL must not be empty.');
|
||||||
|
}
|
||||||
|
this.throwIsAlreadyOpened(message: url != null ? 'Cannot open $url!' : '');
|
||||||
|
|
||||||
|
this._isOpened = true;
|
||||||
|
|
||||||
List<Map<String, dynamic>> menuItemList = [];
|
List<Map<String, dynamic>> menuItemList = [];
|
||||||
_menuItems.forEach((key, value) {
|
_menuItems.forEach((key, value) {
|
||||||
|
@ -143,15 +230,96 @@ class ChromeSafariBrowser {
|
||||||
|
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
args.putIfAbsent('id', () => id);
|
args.putIfAbsent('id', () => id);
|
||||||
args.putIfAbsent('url', () => url.toString());
|
args.putIfAbsent('url', () => url?.toString());
|
||||||
|
args.putIfAbsent('headers', () => headers);
|
||||||
|
args.putIfAbsent('otherLikelyURLs',
|
||||||
|
() => otherLikelyURLs?.map((e) => e.toString()).toList());
|
||||||
|
args.putIfAbsent('referrer', () => referrer?.toString());
|
||||||
args.putIfAbsent('settings', () => initialSettings);
|
args.putIfAbsent('settings', () => initialSettings);
|
||||||
args.putIfAbsent('actionButton', () => _actionButton?.toMap());
|
args.putIfAbsent('actionButton', () => _actionButton?.toMap());
|
||||||
|
args.putIfAbsent('secondaryToolbar', () => _secondaryToolbar?.toMap());
|
||||||
args.putIfAbsent('menuItemList', () => menuItemList);
|
args.putIfAbsent('menuItemList', () => menuItemList);
|
||||||
await _sharedChannel.invokeMethod('open', args);
|
await _sharedChannel.invokeMethod('open', args);
|
||||||
this._isOpened = true;
|
}
|
||||||
|
|
||||||
|
///Tells the browser to launch with [url].
|
||||||
|
///
|
||||||
|
///[url] - initial url.
|
||||||
|
///
|
||||||
|
///[headers] (Supported only on Android) - [whitelisted](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) cross-origin request headers.
|
||||||
|
///It is possible to attach non-whitelisted headers to cross-origin requests, when the server and client are related using a
|
||||||
|
///[digital asset link](https://developers.google.com/digital-asset-links/v1/getting-started).
|
||||||
|
///
|
||||||
|
///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order.
|
||||||
|
///
|
||||||
|
///[referrer] - referrer header. Supported only on Android.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Future<void> launchUrl({
|
||||||
|
required Uri url,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
List<Uri>? otherLikelyURLs,
|
||||||
|
Uri? referrer,
|
||||||
|
}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('url', () => url.toString());
|
||||||
|
args.putIfAbsent('headers', () => headers);
|
||||||
|
args.putIfAbsent('otherLikelyURLs',
|
||||||
|
() => otherLikelyURLs?.map((e) => e.toString()).toList());
|
||||||
|
args.putIfAbsent('referrer', () => referrer?.toString());
|
||||||
|
await _channel.invokeMethod("launchUrl", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Tells the browser of a likely future navigation to a URL.
|
||||||
|
///The most likely URL has to be specified first.
|
||||||
|
///Optionally, a list of other likely URLs can be provided.
|
||||||
|
///They are treated as less likely than the first one, and have to be sorted in decreasing priority order.
|
||||||
|
///These additional URLs may be ignored. All previous calls to this method will be deprioritized.
|
||||||
|
///
|
||||||
|
///[url] - Most likely URL, may be null if otherLikelyBundles is provided.
|
||||||
|
///
|
||||||
|
///[otherLikelyURLs] - Other likely destinations, sorted in decreasing likelihood order.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsSession.mayLaunchUrl](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#mayLaunchUrl(android.net.Uri,android.os.Bundle,java.util.List%3Candroid.os.Bundle%3E)))
|
||||||
|
Future<bool> mayLaunchUrl({Uri? url, List<Uri>? otherLikelyURLs}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('url', () => url?.toString());
|
||||||
|
args.putIfAbsent('otherLikelyURLs',
|
||||||
|
() => otherLikelyURLs?.map((e) => e.toString()).toList());
|
||||||
|
return await _channel.invokeMethod("mayLaunchUrl", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Requests to validate a relationship between the application and an origin.
|
||||||
|
///
|
||||||
|
///See [here](https://developers.google.com/digital-asset-links/v1/getting-started) for documentation about Digital Asset Links.
|
||||||
|
///This methods requests the browser to verify a relation with the calling application, to grant the associated rights.
|
||||||
|
///
|
||||||
|
///If this method returns `true`, the validation result will be provided through [onRelationshipValidationResult].
|
||||||
|
///Otherwise the request didn't succeed.
|
||||||
|
///
|
||||||
|
///[relation] – Relation to check, must be one of the [CustomTabsRelationType] constants.
|
||||||
|
///
|
||||||
|
///[origin] – Origin.
|
||||||
|
///
|
||||||
|
///[extras] – Reserved for future use.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsSession.validateRelationship](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#validateRelationship(int,android.net.Uri,android.os.Bundle)))
|
||||||
|
Future<bool> validateRelationship(
|
||||||
|
{required CustomTabsRelationType relation, required Uri origin}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('relation', () => relation.toNativeValue());
|
||||||
|
args.putIfAbsent('origin', () => origin.toString());
|
||||||
|
return await _channel.invokeMethod("validateRelationship", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
///Closes the [ChromeSafariBrowser] instance.
|
///Closes the [ChromeSafariBrowser] instance.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
await _channel.invokeMethod("close", args);
|
await _channel.invokeMethod("close", args);
|
||||||
|
@ -162,14 +330,59 @@ class ChromeSafariBrowser {
|
||||||
///**NOTE**: Not available in a Trusted Web Activity.
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**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)))
|
///- 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) {
|
void setActionButton(ChromeSafariBrowserActionButton actionButton) {
|
||||||
this._actionButton = actionButton;
|
this._actionButton = actionButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Updates the [ChromeSafariBrowserActionButton.icon] and [ChromeSafariBrowserActionButton.description].
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsSession.setActionButton](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#setActionButton(android.graphics.Bitmap,java.lang.String)))
|
||||||
|
Future<void> updateActionButton(
|
||||||
|
{required Uint8List icon, required String description}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('icon', () => icon);
|
||||||
|
args.putIfAbsent('description', () => description);
|
||||||
|
await _channel.invokeMethod("updateActionButton", args);
|
||||||
|
_actionButton?.icon = icon;
|
||||||
|
_actionButton?.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Sets the remote views displayed in the secondary toolbar in a custom tab.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsIntent.Builder.setSecondaryToolbarViews](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsIntent.Builder#setSecondaryToolbarViews(android.widget.RemoteViews,int[],android.app.PendingIntent)))
|
||||||
|
void setSecondaryToolbar(
|
||||||
|
ChromeSafariBrowserSecondaryToolbar secondaryToolbar) {
|
||||||
|
this._secondaryToolbar = secondaryToolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Sets or updates (if already present) the Remote Views of the secondary toolbar in an existing custom tab session.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsSession.setSecondaryToolbarViews](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#setSecondaryToolbarViews(android.widget.RemoteViews,int[],android.app.PendingIntent)))
|
||||||
|
Future<void> updateSecondaryToolbar(
|
||||||
|
ChromeSafariBrowserSecondaryToolbar secondaryToolbar) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('secondaryToolbar', () => secondaryToolbar.toMap());
|
||||||
|
await _channel.invokeMethod("updateSecondaryToolbar", args);
|
||||||
|
this._secondaryToolbar = secondaryToolbar;
|
||||||
|
}
|
||||||
|
|
||||||
///Adds a [ChromeSafariBrowserMenuItem] to the menu.
|
///Adds a [ChromeSafariBrowserMenuItem] to the menu.
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in an Android Trusted Web Activity.
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
void addMenuItem(ChromeSafariBrowserMenuItem menuItem) {
|
void addMenuItem(ChromeSafariBrowserMenuItem menuItem) {
|
||||||
this._menuItems[menuItem.id] = menuItem;
|
this._menuItems[menuItem.id] = menuItem;
|
||||||
}
|
}
|
||||||
|
@ -177,6 +390,10 @@ class ChromeSafariBrowser {
|
||||||
///Adds a list of [ChromeSafariBrowserMenuItem] to the menu.
|
///Adds a list of [ChromeSafariBrowserMenuItem] to the menu.
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in an Android Trusted Web Activity.
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
void addMenuItems(List<ChromeSafariBrowserMenuItem> menuItems) {
|
void addMenuItems(List<ChromeSafariBrowserMenuItem> menuItems) {
|
||||||
menuItems.forEach((menuItem) {
|
menuItems.forEach((menuItem) {
|
||||||
this._menuItems[menuItem.id] = menuItem;
|
this._menuItems[menuItem.id] = menuItem;
|
||||||
|
@ -186,21 +403,145 @@ class ChromeSafariBrowser {
|
||||||
///On Android, returns `true` if Chrome Custom Tabs is available.
|
///On Android, returns `true` if Chrome Custom Tabs is available.
|
||||||
///On iOS, returns `true` if SFSafariViewController is available.
|
///On iOS, returns `true` if SFSafariViewController is available.
|
||||||
///Otherwise returns `false`.
|
///Otherwise returns `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
static Future<bool> isAvailable() async {
|
static Future<bool> isAvailable() async {
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
return await _sharedChannel.invokeMethod("isAvailable", args);
|
return await _sharedChannel.invokeMethod("isAvailable", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
///Event fires when the [ChromeSafariBrowser] is opened.
|
///The maximum number of allowed secondary toolbar items.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
static Future<int> getMaxToolbarItems() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
return await _sharedChannel.invokeMethod("getMaxToolbarItems", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Clear associated website data accrued from browsing activity within your app.
|
||||||
|
///This includes all local storage, cached resources, and cookies.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available on iOS 16.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS ([Official API - SFSafariViewController.DataStore.clearWebsiteData](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/datastore/3981117-clearwebsitedata))
|
||||||
|
static Future<void> clearWebsiteData() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
await _sharedChannel.invokeMethod("clearWebsiteData", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Prewarms a connection to each URL. SFSafariViewController will automatically use a
|
||||||
|
///prewarmed connection if possible when loading its initial URL.
|
||||||
|
///
|
||||||
|
///Returns a token object that corresponds to the requested URLs. You must keep a strong
|
||||||
|
///reference to this token as long as you expect the prewarmed connections to remain open. If the same
|
||||||
|
///server is requested in multiple calls to this method, all of the corresponding tokens must be
|
||||||
|
///invalidated or released to end the prewarmed connection to that server.
|
||||||
|
///
|
||||||
|
///This method uses a best-effort approach to prewarming connections, but may delay
|
||||||
|
///or drop requests based on the volume of requests made by your app. Use this method when you expect
|
||||||
|
///to present the browser soon. Many HTTP servers time out connections after a few minutes.
|
||||||
|
///After a timeout, prewarming delivers less performance benefit.
|
||||||
|
///
|
||||||
|
///[URLs] - the URLs of servers that the browser should prewarm connections to.
|
||||||
|
///Only supports URLs with `http://` or `https://` schemes.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available on iOS 15.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS ([Official API - SFSafariViewController.prewarmConnections](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/3752133-prewarmconnections))
|
||||||
|
static Future<PrewarmingToken?> prewarmConnections(List<Uri> URLs) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('URLs', () => URLs.map((e) => e.toString()).toList());
|
||||||
|
Map<String, dynamic>? result =
|
||||||
|
(await _sharedChannel.invokeMethod("prewarmConnections", args))
|
||||||
|
?.cast<String, dynamic>();
|
||||||
|
return PrewarmingToken.fromMap(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Ends all prewarmed connections associated with the token, except for connections that are also kept alive by other tokens.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available on iOS 15.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS ([Official API - SFSafariViewController.prewarmConnections](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/3752133-prewarmconnections))
|
||||||
|
static Future<void> invalidatePrewarmingToken(
|
||||||
|
PrewarmingToken prewarmingToken) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('prewarmingToken', () => prewarmingToken.toMap());
|
||||||
|
await _sharedChannel.invokeMethod("invalidatePrewarmingToken", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Event fired when the when connecting from Android Custom Tabs Service.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
void onServiceConnected() {}
|
||||||
|
|
||||||
|
///Event fired when the [ChromeSafariBrowser] is opened.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
void onOpened() {}
|
void onOpened() {}
|
||||||
|
|
||||||
///Event fires when the initial URL load is complete.
|
///Event fired when the initial URL load is complete.
|
||||||
void onCompletedInitialLoad() {}
|
///
|
||||||
|
///[didLoadSuccessfully] - `true` if loading completed successfully; otherwise, `false`. Supported only on iOS.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/1621215-safariviewcontroller))
|
||||||
|
void onCompletedInitialLoad(bool? didLoadSuccessfully) {}
|
||||||
|
|
||||||
///Event fires when the [ChromeSafariBrowser] is closed.
|
///Event fired when the initial URL load is complete.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/2923545-safariviewcontroller))
|
||||||
|
void onInitialLoadDidRedirect(Uri? url) {}
|
||||||
|
|
||||||
|
///Event fired when a navigation event happens.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsCallback.onNavigationEvent](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onNavigationEvent(int,android.os.Bundle)))
|
||||||
|
void onNavigationEvent(CustomTabsNavigationEventType? navigationEvent) {}
|
||||||
|
|
||||||
|
///Event fired when a relationship validation result is available.
|
||||||
|
///
|
||||||
|
///[relation] - Relation for which the result is available. Value previously passed to [validateRelationship].
|
||||||
|
///
|
||||||
|
///[requestedOrigin] - Origin requested. Value previously passed to [validateRelationship].
|
||||||
|
///
|
||||||
|
///[result] - Whether the relation was validated.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android ([Official API - CustomTabsCallback.onRelationshipValidationResult](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsCallback#onRelationshipValidationResult(int,android.net.Uri,boolean,android.os.Bundle)))
|
||||||
|
void onRelationshipValidationResult(
|
||||||
|
CustomTabsRelationType? relation, Uri? requestedOrigin, bool result) {}
|
||||||
|
|
||||||
|
///Event fired when the user opens the current page in the default browser by tapping the toolbar button.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available on iOS 14.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS ([Official API - SFSafariViewControllerDelegate.safariViewControllerWillOpenInBrowser](https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/3650426-safariviewcontrollerwillopeninbr))
|
||||||
|
void onWillOpenInBrowser() {}
|
||||||
|
|
||||||
|
///Event fired when the [ChromeSafariBrowser] is closed.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
void onClosed() {}
|
void onClosed() {}
|
||||||
|
|
||||||
///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`.
|
///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
bool isOpened() {
|
bool isOpened() {
|
||||||
return this._isOpened;
|
return this._isOpened;
|
||||||
}
|
}
|
||||||
|
@ -225,6 +566,9 @@ class ChromeSafariBrowser {
|
||||||
///Class that represents a custom action button for a [ChromeSafariBrowser] instance.
|
///Class that represents a custom action button for a [ChromeSafariBrowser] instance.
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in an Android Trusted Web Activity.
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
class ChromeSafariBrowserActionButton {
|
class ChromeSafariBrowserActionButton {
|
||||||
///The action button id. It should be different from the [ChromeSafariBrowserMenuItem.id].
|
///The action button id. It should be different from the [ChromeSafariBrowserMenuItem.id].
|
||||||
int id;
|
int id;
|
||||||
|
@ -238,14 +582,19 @@ class ChromeSafariBrowserActionButton {
|
||||||
///Whether the action button should be tinted.
|
///Whether the action button should be tinted.
|
||||||
bool shouldTint;
|
bool shouldTint;
|
||||||
|
|
||||||
///Callback function to be invoked when the menu item is clicked
|
///Use onClick instead.
|
||||||
final void Function(String url, String title) action;
|
@Deprecated("Use onClick instead")
|
||||||
|
void Function(String url, String title)? action;
|
||||||
|
|
||||||
|
///Callback function to be invoked when the action button is clicked
|
||||||
|
void Function(Uri? url, String title)? onClick;
|
||||||
|
|
||||||
ChromeSafariBrowserActionButton(
|
ChromeSafariBrowserActionButton(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.action,
|
@Deprecated("Use onClick instead") this.action,
|
||||||
|
this.onClick,
|
||||||
this.shouldTint = false});
|
this.shouldTint = false});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
|
@ -270,21 +619,106 @@ class ChromeSafariBrowserActionButton {
|
||||||
///Class that represents a custom menu item for a [ChromeSafariBrowser] instance.
|
///Class that represents a custom menu item for a [ChromeSafariBrowser] instance.
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in an Android Trusted Web Activity.
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
///- iOS
|
||||||
class ChromeSafariBrowserMenuItem {
|
class ChromeSafariBrowserMenuItem {
|
||||||
///The menu item id. It should be different from [ChromeSafariBrowserActionButton.id].
|
///The menu item id. It should be different from [ChromeSafariBrowserActionButton.id].
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
///The label of the menu item
|
///The label of the menu item.
|
||||||
String label;
|
String label;
|
||||||
|
|
||||||
|
///Item image.
|
||||||
|
UIImage? image;
|
||||||
|
|
||||||
|
///Use onClick instead.
|
||||||
|
@Deprecated("Use onClick instead")
|
||||||
|
void Function(String url, String title)? action;
|
||||||
|
|
||||||
///Callback function to be invoked when the menu item is clicked
|
///Callback function to be invoked when the menu item is clicked
|
||||||
final void Function(String url, String title) action;
|
void Function(Uri? url, String title)? onClick;
|
||||||
|
|
||||||
ChromeSafariBrowserMenuItem(
|
ChromeSafariBrowserMenuItem(
|
||||||
{required this.id, required this.label, required this.action});
|
{required this.id,
|
||||||
|
required this.label,
|
||||||
|
this.image,
|
||||||
|
@Deprecated("Use onClick instead") this.action,
|
||||||
|
this.onClick});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {"id": id, "label": label};
|
return {"id": id, "label": label, "image": image?.toMap()};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Class that represents the [RemoteViews](https://developer.android.com/reference/android/widget/RemoteViews.html)
|
||||||
|
///that will be shown on the secondary toolbar of a custom tab.
|
||||||
|
///
|
||||||
|
///This class describes a view hierarchy that can be displayed in another process.
|
||||||
|
///The hierarchy is inflated from an Android layout resource file.
|
||||||
|
///
|
||||||
|
///RemoteViews has limited to support to Android layouts.
|
||||||
|
///Check the [RemoteViews Official API](https://developer.android.com/reference/android/widget/RemoteViews.html) for more details.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
class ChromeSafariBrowserSecondaryToolbar {
|
||||||
|
///The android layout resource.
|
||||||
|
AndroidResource layout;
|
||||||
|
|
||||||
|
///The IDs of clickable views. The `onClick` event of these views will be handled by custom tabs.
|
||||||
|
List<ChromeSafariBrowserSecondaryToolbarClickableID> clickableIDs;
|
||||||
|
|
||||||
|
ChromeSafariBrowserSecondaryToolbar(
|
||||||
|
{required this.layout, this.clickableIDs = const []});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"layout": layout.toMap(),
|
||||||
|
"clickableIDs": clickableIDs.map((e) => e.toMap()).toList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Class that represents a clickable ID item of the secondary toolbar for a [ChromeSafariBrowser] instance.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in an Android Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
class ChromeSafariBrowserSecondaryToolbarClickableID {
|
||||||
|
///The android id resource
|
||||||
|
AndroidResource id;
|
||||||
|
|
||||||
|
///Callback function to be invoked when the item is clicked
|
||||||
|
void Function(Uri? url)? onClick;
|
||||||
|
|
||||||
|
ChromeSafariBrowserSecondaryToolbarClickableID(
|
||||||
|
{required this.id, this.onClick});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {"id": id.toMap()};
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|
|
@ -1,11 +1,37 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
import '../types/activity_button.dart';
|
||||||
|
import '../types/android_resource.dart';
|
||||||
|
import '../types/custom_tabs_share_state.dart';
|
||||||
|
import '../types/dismiss_button_style.dart';
|
||||||
|
import '../types/main.dart';
|
||||||
|
import '../types/modal_presentation_style.dart';
|
||||||
|
import '../types/modal_transition_style.dart';
|
||||||
|
import '../types/trusted_web_activity_display_mode.dart';
|
||||||
|
import '../types/trusted_web_activity_screen_orientation.dart';
|
||||||
|
import '../types/ui_event_attribution.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
import 'android/chrome_custom_tabs_options.dart';
|
import 'android/chrome_custom_tabs_options.dart';
|
||||||
import 'apple/safari_options.dart';
|
import 'apple/safari_options.dart';
|
||||||
import '../types/main.dart';
|
|
||||||
|
part 'chrome_safari_browser_settings.g.dart';
|
||||||
|
|
||||||
|
TrustedWebActivityDisplayMode? _deserializeDisplayMode(
|
||||||
|
Map<String, dynamic>? displayMode) {
|
||||||
|
if (displayMode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (displayMode["type"]) {
|
||||||
|
case "IMMERSIVE_MODE":
|
||||||
|
return TrustedWebActivityImmersiveDisplayMode.fromMap(displayMode);
|
||||||
|
case "DEFAULT_MODE":
|
||||||
|
default:
|
||||||
|
return TrustedWebActivityDefaultDisplayMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ChromeSafariBrowserOptions {
|
class ChromeSafariBrowserOptions {
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
|
@ -31,14 +57,15 @@ class ChromeSafariBrowserOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
///Class that represents the settings that can be used for an [ChromeSafariBrowser] window.
|
///Class that represents the settings that can be used for an [ChromeSafariBrowser] window.
|
||||||
class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
@ExchangeableObject(copyMethod: true)
|
||||||
|
class ChromeSafariBrowserSettings_ implements ChromeSafariBrowserOptions {
|
||||||
///The share state that should be applied to the custom tab. The default value is [CustomTabsShareState.SHARE_STATE_DEFAULT].
|
///The share state that should be applied to the custom tab. The default value is [CustomTabsShareState.SHARE_STATE_DEFAULT].
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in a Trusted Web Activity.
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
CustomTabsShareState shareState;
|
CustomTabsShareState_? shareState;
|
||||||
|
|
||||||
///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
|
///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
|
||||||
///
|
///
|
||||||
|
@ -46,7 +73,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool showTitle;
|
bool? showTitle;
|
||||||
|
|
||||||
///Set the custom background color of the toolbar.
|
///Set the custom background color of the toolbar.
|
||||||
///
|
///
|
||||||
|
@ -54,13 +81,31 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///- Android
|
///- Android
|
||||||
Color? toolbarBackgroundColor;
|
Color? toolbarBackgroundColor;
|
||||||
|
|
||||||
|
///Sets the navigation bar color. Has no effect on Android API versions below L.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? navigationBarColor;
|
||||||
|
|
||||||
|
///Sets the navigation bar divider color. Has no effect on Android API versions below P.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? navigationBarDividerColor;
|
||||||
|
|
||||||
|
///Sets the color of the secondary toolbar.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? secondaryToolbarColor;
|
||||||
|
|
||||||
///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
|
///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**NOTE**: Not available in a Trusted Web Activity.
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool enableUrlBarHiding;
|
bool? enableUrlBarHiding;
|
||||||
|
|
||||||
///Set to `true` to enable Instant Apps. The default value is `false`.
|
///Set to `true` to enable Instant Apps. The default value is `false`.
|
||||||
///
|
///
|
||||||
|
@ -68,7 +113,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool instantAppsEnabled;
|
bool? instantAppsEnabled;
|
||||||
|
|
||||||
///Set an explicit application package name that limits
|
///Set an explicit application package name that limits
|
||||||
///the components this Intent will resolve to. If left to the default
|
///the components this Intent will resolve to. If left to the default
|
||||||
|
@ -84,25 +129,25 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool keepAliveEnabled;
|
bool? keepAliveEnabled;
|
||||||
|
|
||||||
///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`.
|
///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool isSingleInstance;
|
bool? isSingleInstance;
|
||||||
|
|
||||||
///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`.
|
///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool noHistory;
|
bool? noHistory;
|
||||||
|
|
||||||
///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`.
|
///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
bool isTrustedWebActivity;
|
bool? isTrustedWebActivity;
|
||||||
|
|
||||||
///Sets a list of additional trusted origins that the user may navigate or be redirected to from the starting uri.
|
///Sets a list of additional trusted origins that the user may navigate or be redirected to from the starting uri.
|
||||||
///
|
///
|
||||||
|
@ -110,7 +155,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
List<String> additionalTrustedOrigins;
|
List<String>? additionalTrustedOrigins;
|
||||||
|
|
||||||
///Sets a display mode of a Trusted Web Activity.
|
///Sets a display mode of a Trusted Web Activity.
|
||||||
///
|
///
|
||||||
|
@ -118,7 +163,8 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
TrustedWebActivityDisplayMode? displayMode;
|
@ExchangeableObjectProperty(deserializer: _deserializeDisplayMode)
|
||||||
|
TrustedWebActivityDisplayMode_? displayMode;
|
||||||
|
|
||||||
///Sets a screen orientation. This can be used e.g. to enable the locking of an orientation lock type.
|
///Sets a screen orientation. This can be used e.g. to enable the locking of an orientation lock type.
|
||||||
///
|
///
|
||||||
|
@ -126,19 +172,44 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android
|
///- Android
|
||||||
TrustedWebActivityScreenOrientation screenOrientation;
|
TrustedWebActivityScreenOrientation_? screenOrientation;
|
||||||
|
|
||||||
|
///Sets the start animations.
|
||||||
|
///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the browser
|
||||||
|
///and the second one represents the "exit" animation for the application.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
List<AndroidResource_>? startAnimations;
|
||||||
|
|
||||||
|
///Sets the exit animations.
|
||||||
|
///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the application
|
||||||
|
///and the second one represents the "exit" animation for the browser.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
List<AndroidResource_>? exitAnimations;
|
||||||
|
|
||||||
|
///Adds the necessary flags and extras to signal any browser supporting custom tabs to use the browser UI
|
||||||
|
///at all times and avoid showing custom tab like UI.
|
||||||
|
///Calling this with an intent will override any custom tabs related customizations.
|
||||||
|
///The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? alwaysUseBrowserUI;
|
||||||
|
|
||||||
///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
|
///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS
|
///- iOS
|
||||||
bool entersReaderIfAvailable;
|
bool? entersReaderIfAvailable;
|
||||||
|
|
||||||
///Set to `true` to enable bar collapsing. The default value is `false`.
|
///Set to `true` to enable bar collapsing. The default value is `false`.
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS
|
///- iOS
|
||||||
bool barCollapsingEnabled;
|
bool? barCollapsingEnabled;
|
||||||
|
|
||||||
///Set the custom style for the dismiss button. The default value is [DismissButtonStyle.DONE].
|
///Set the custom style for the dismiss button. The default value is [DismissButtonStyle.DONE].
|
||||||
///
|
///
|
||||||
|
@ -146,7 +217,7 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS
|
///- iOS
|
||||||
DismissButtonStyle dismissButtonStyle;
|
DismissButtonStyle_? dismissButtonStyle;
|
||||||
|
|
||||||
///Set the custom background color of the navigation bar and the toolbar.
|
///Set the custom background color of the navigation bar and the toolbar.
|
||||||
///
|
///
|
||||||
|
@ -168,18 +239,40 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS
|
///- iOS
|
||||||
ModalPresentationStyle presentationStyle;
|
ModalPresentationStyle_? presentationStyle;
|
||||||
|
|
||||||
///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL].
|
///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL].
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- iOS
|
///- iOS
|
||||||
ModalTransitionStyle transitionStyle;
|
ModalTransitionStyle_? transitionStyle;
|
||||||
|
|
||||||
ChromeSafariBrowserSettings(
|
///An additional button to be shown in `SFSafariViewController`'s toolbar.
|
||||||
{this.shareState = CustomTabsShareState.SHARE_STATE_DEFAULT,
|
///This allows the user to access powerful functionality from your extension without needing to first show the `UIActivityViewController`.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 15.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
ActivityButton_? activityButton;
|
||||||
|
|
||||||
|
///An event attribution associated with a click that caused this `SFSafariViewController` to be opened.
|
||||||
|
///This attribute is ignored if the `SFSafariViewController` url has a scheme of 'http'.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 15.2+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
UIEventAttribution_? eventAttribution;
|
||||||
|
|
||||||
|
@ExchangeableObjectConstructor()
|
||||||
|
ChromeSafariBrowserSettings_(
|
||||||
|
{this.shareState = CustomTabsShareState_.SHARE_STATE_DEFAULT,
|
||||||
this.showTitle = true,
|
this.showTitle = true,
|
||||||
this.toolbarBackgroundColor,
|
this.toolbarBackgroundColor,
|
||||||
|
this.navigationBarColor,
|
||||||
|
this.navigationBarDividerColor,
|
||||||
|
this.secondaryToolbarColor,
|
||||||
this.enableUrlBarHiding = false,
|
this.enableUrlBarHiding = false,
|
||||||
this.instantAppsEnabled = false,
|
this.instantAppsEnabled = false,
|
||||||
this.packageName,
|
this.packageName,
|
||||||
|
@ -189,99 +282,45 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
this.isTrustedWebActivity = false,
|
this.isTrustedWebActivity = false,
|
||||||
this.additionalTrustedOrigins = const [],
|
this.additionalTrustedOrigins = const [],
|
||||||
this.displayMode,
|
this.displayMode,
|
||||||
this.screenOrientation = TrustedWebActivityScreenOrientation.DEFAULT,
|
this.screenOrientation = TrustedWebActivityScreenOrientation_.DEFAULT,
|
||||||
|
this.startAnimations,
|
||||||
|
this.exitAnimations,
|
||||||
|
this.alwaysUseBrowserUI = false,
|
||||||
this.entersReaderIfAvailable = false,
|
this.entersReaderIfAvailable = false,
|
||||||
this.barCollapsingEnabled = false,
|
this.barCollapsingEnabled = false,
|
||||||
this.dismissButtonStyle = DismissButtonStyle.DONE,
|
this.dismissButtonStyle = DismissButtonStyle_.DONE,
|
||||||
this.preferredBarTintColor,
|
this.preferredBarTintColor,
|
||||||
this.preferredControlTintColor,
|
this.preferredControlTintColor,
|
||||||
this.presentationStyle = ModalPresentationStyle.FULL_SCREEN,
|
this.presentationStyle = ModalPresentationStyle_.FULL_SCREEN,
|
||||||
this.transitionStyle = ModalTransitionStyle.COVER_VERTICAL});
|
this.transitionStyle = ModalTransitionStyle_.COVER_VERTICAL,
|
||||||
|
this.activityButton,
|
||||||
@override
|
this.eventAttribution}) {
|
||||||
Map<String, dynamic> toMap() {
|
if (startAnimations != null) {
|
||||||
return {
|
assert(startAnimations!.length == 2,
|
||||||
"shareState": shareState.toNativeValue(),
|
"start animations must be have 2 android resources");
|
||||||
"showTitle": showTitle,
|
|
||||||
"toolbarBackgroundColor": toolbarBackgroundColor?.toHex(),
|
|
||||||
"enableUrlBarHiding": enableUrlBarHiding,
|
|
||||||
"instantAppsEnabled": instantAppsEnabled,
|
|
||||||
"packageName": packageName,
|
|
||||||
"keepAliveEnabled": keepAliveEnabled,
|
|
||||||
"isSingleInstance": isSingleInstance,
|
|
||||||
"noHistory": noHistory,
|
|
||||||
"isTrustedWebActivity": isTrustedWebActivity,
|
|
||||||
"additionalTrustedOrigins": additionalTrustedOrigins,
|
|
||||||
"displayMode": displayMode?.toMap(),
|
|
||||||
"screenOrientation": screenOrientation.toNativeValue(),
|
|
||||||
"entersReaderIfAvailable": entersReaderIfAvailable,
|
|
||||||
"barCollapsingEnabled": barCollapsingEnabled,
|
|
||||||
"dismissButtonStyle": dismissButtonStyle.toNativeValue(),
|
|
||||||
"preferredBarTintColor": preferredBarTintColor?.toHex(),
|
|
||||||
"preferredControlTintColor": preferredControlTintColor?.toHex(),
|
|
||||||
"presentationStyle": presentationStyle.toNativeValue(),
|
|
||||||
"transitionStyle": transitionStyle.toNativeValue()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChromeSafariBrowserSettings fromMap(Map<String, dynamic> map) {
|
|
||||||
ChromeSafariBrowserSettings settings = new ChromeSafariBrowserSettings();
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
|
||||||
settings.shareState = map["shareState"];
|
|
||||||
settings.showTitle = map["showTitle"];
|
|
||||||
settings.toolbarBackgroundColor =
|
|
||||||
UtilColor.fromHex(map["toolbarBackgroundColor"]);
|
|
||||||
settings.enableUrlBarHiding = map["enableUrlBarHiding"];
|
|
||||||
settings.instantAppsEnabled = map["instantAppsEnabled"];
|
|
||||||
settings.packageName = map["packageName"];
|
|
||||||
settings.keepAliveEnabled = map["keepAliveEnabled"];
|
|
||||||
settings.isSingleInstance = map["isSingleInstance"];
|
|
||||||
settings.noHistory = map["noHistory"];
|
|
||||||
settings.isTrustedWebActivity = map["isTrustedWebActivity"];
|
|
||||||
settings.additionalTrustedOrigins = map["additionalTrustedOrigins"];
|
|
||||||
switch (map["displayMode"]["type"]) {
|
|
||||||
case "IMMERSIVE_MODE":
|
|
||||||
settings.displayMode = TrustedWebActivityImmersiveDisplayMode.fromMap(
|
|
||||||
map["displayMode"]);
|
|
||||||
break;
|
|
||||||
case "DEFAULT_MODE":
|
|
||||||
default:
|
|
||||||
settings.displayMode = TrustedWebActivityDefaultDisplayMode();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
settings.screenOrientation = map["screenOrientation"];
|
|
||||||
}
|
}
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
if (exitAnimations != null) {
|
||||||
defaultTargetPlatform == TargetPlatform.macOS) {
|
assert(exitAnimations!.length == 2,
|
||||||
settings.entersReaderIfAvailable = map["entersReaderIfAvailable"];
|
"exit animations must be have 2 android resources");
|
||||||
settings.barCollapsingEnabled = map["barCollapsingEnabled"];
|
|
||||||
settings.dismissButtonStyle =
|
|
||||||
DismissButtonStyle.fromNativeValue(map["dismissButtonStyle"])!;
|
|
||||||
settings.preferredBarTintColor =
|
|
||||||
UtilColor.fromHex(map["preferredBarTintColor"]);
|
|
||||||
settings.preferredControlTintColor =
|
|
||||||
UtilColor.fromHex(map["preferredControlTintColor"]);
|
|
||||||
settings.presentationStyle =
|
|
||||||
ModalPresentationStyle.fromNativeValue(map["presentationStyle"])!;
|
|
||||||
settings.transitionStyle =
|
|
||||||
ModalTransitionStyle.fromNativeValue(map["transitionStyle"])!;
|
|
||||||
}
|
}
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ExchangeableObjectMethod(ignore: true)
|
||||||
|
ChromeSafariBrowserSettings_ copy() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@ExchangeableObjectMethod(ignore: true)
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return this.toMap();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
@ExchangeableObjectMethod(ignore: true)
|
||||||
return toMap().toString();
|
Map<String, dynamic> toMap() {
|
||||||
}
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
|
||||||
ChromeSafariBrowserSettings copy() {
|
|
||||||
return ChromeSafariBrowserSettings.fromMap(this.toMap());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,368 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'chrome_safari_browser_settings.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents the settings that can be used for an [ChromeSafariBrowser] window.
|
||||||
|
class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
|
///The share state that should be applied to the custom tab. The default value is [CustomTabsShareState.SHARE_STATE_DEFAULT].
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
CustomTabsShareState? shareState;
|
||||||
|
|
||||||
|
///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? showTitle;
|
||||||
|
|
||||||
|
///Set the custom background color of the toolbar.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? toolbarBackgroundColor;
|
||||||
|
|
||||||
|
///Sets the navigation bar color. Has no effect on Android API versions below L.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? navigationBarColor;
|
||||||
|
|
||||||
|
///Sets the navigation bar divider color. Has no effect on Android API versions below P.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? navigationBarDividerColor;
|
||||||
|
|
||||||
|
///Sets the color of the secondary toolbar.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
Color? secondaryToolbarColor;
|
||||||
|
|
||||||
|
///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? enableUrlBarHiding;
|
||||||
|
|
||||||
|
///Set to `true` to enable Instant Apps. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**NOTE**: Not available in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? instantAppsEnabled;
|
||||||
|
|
||||||
|
///Set an explicit application package name that limits
|
||||||
|
///the components this Intent will resolve to. If left to the default
|
||||||
|
///value of null, all components in all applications will considered.
|
||||||
|
///If non-null, the Intent can only match the components in the given
|
||||||
|
///application package.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
String? packageName;
|
||||||
|
|
||||||
|
///Set to `true` to enable Keep Alive. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? keepAliveEnabled;
|
||||||
|
|
||||||
|
///Set to `true` to launch the Android activity in `singleInstance` mode. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? isSingleInstance;
|
||||||
|
|
||||||
|
///Set to `true` to launch the Android intent with the flag `FLAG_ACTIVITY_NO_HISTORY`. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? noHistory;
|
||||||
|
|
||||||
|
///Set to `true` to launch the Custom Tab as a Trusted Web Activity. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? isTrustedWebActivity;
|
||||||
|
|
||||||
|
///Sets a list of additional trusted origins that the user may navigate or be redirected to from the starting uri.
|
||||||
|
///
|
||||||
|
///**NOTE**: Available only in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
List<String>? additionalTrustedOrigins;
|
||||||
|
|
||||||
|
///Sets a display mode of a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**NOTE**: Available only in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
TrustedWebActivityDisplayMode? displayMode;
|
||||||
|
|
||||||
|
///Sets a screen orientation. This can be used e.g. to enable the locking of an orientation lock type.
|
||||||
|
///
|
||||||
|
///**NOTE**: Available only in a Trusted Web Activity.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
TrustedWebActivityScreenOrientation? screenOrientation;
|
||||||
|
|
||||||
|
///Sets the start animations.
|
||||||
|
///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the browser
|
||||||
|
///and the second one represents the "exit" animation for the application.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
List<AndroidResource>? startAnimations;
|
||||||
|
|
||||||
|
///Sets the exit animations.
|
||||||
|
///It must contain 2 [AndroidResource], where the first one represents the "enter" animation for the application
|
||||||
|
///and the second one represents the "exit" animation for the browser.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
List<AndroidResource>? exitAnimations;
|
||||||
|
|
||||||
|
///Adds the necessary flags and extras to signal any browser supporting custom tabs to use the browser UI
|
||||||
|
///at all times and avoid showing custom tab like UI.
|
||||||
|
///Calling this with an intent will override any custom tabs related customizations.
|
||||||
|
///The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android
|
||||||
|
bool? alwaysUseBrowserUI;
|
||||||
|
|
||||||
|
///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
bool? entersReaderIfAvailable;
|
||||||
|
|
||||||
|
///Set to `true` to enable bar collapsing. The default value is `false`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
bool? barCollapsingEnabled;
|
||||||
|
|
||||||
|
///Set the custom style for the dismiss button. The default value is [DismissButtonStyle.DONE].
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 11.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
DismissButtonStyle? dismissButtonStyle;
|
||||||
|
|
||||||
|
///Set the custom background color of the navigation bar and the toolbar.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 10.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
Color? preferredBarTintColor;
|
||||||
|
|
||||||
|
///Set the custom color of the control buttons on the navigation bar and the toolbar.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 10.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
Color? preferredControlTintColor;
|
||||||
|
|
||||||
|
///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN].
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
ModalPresentationStyle? presentationStyle;
|
||||||
|
|
||||||
|
///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL].
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
ModalTransitionStyle? transitionStyle;
|
||||||
|
|
||||||
|
///An additional button to be shown in `SFSafariViewController`'s toolbar.
|
||||||
|
///This allows the user to access powerful functionality from your extension without needing to first show the `UIActivityViewController`.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 15.0+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
ActivityButton? activityButton;
|
||||||
|
|
||||||
|
///An event attribution associated with a click that caused this `SFSafariViewController` to be opened.
|
||||||
|
///This attribute is ignored if the `SFSafariViewController` url has a scheme of 'http'.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 15.2+.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
UIEventAttribution? eventAttribution;
|
||||||
|
ChromeSafariBrowserSettings(
|
||||||
|
{this.shareState = CustomTabsShareState.SHARE_STATE_DEFAULT,
|
||||||
|
this.showTitle = true,
|
||||||
|
this.toolbarBackgroundColor,
|
||||||
|
this.navigationBarColor,
|
||||||
|
this.navigationBarDividerColor,
|
||||||
|
this.secondaryToolbarColor,
|
||||||
|
this.enableUrlBarHiding = false,
|
||||||
|
this.instantAppsEnabled = false,
|
||||||
|
this.packageName,
|
||||||
|
this.keepAliveEnabled = false,
|
||||||
|
this.isSingleInstance = false,
|
||||||
|
this.noHistory = false,
|
||||||
|
this.isTrustedWebActivity = false,
|
||||||
|
this.additionalTrustedOrigins = const [],
|
||||||
|
this.displayMode,
|
||||||
|
this.screenOrientation = TrustedWebActivityScreenOrientation.DEFAULT,
|
||||||
|
this.startAnimations,
|
||||||
|
this.exitAnimations,
|
||||||
|
this.alwaysUseBrowserUI = false,
|
||||||
|
this.entersReaderIfAvailable = false,
|
||||||
|
this.barCollapsingEnabled = false,
|
||||||
|
this.dismissButtonStyle = DismissButtonStyle.DONE,
|
||||||
|
this.preferredBarTintColor,
|
||||||
|
this.preferredControlTintColor,
|
||||||
|
this.presentationStyle = ModalPresentationStyle.FULL_SCREEN,
|
||||||
|
this.transitionStyle = ModalTransitionStyle.COVER_VERTICAL,
|
||||||
|
this.activityButton,
|
||||||
|
this.eventAttribution}) {
|
||||||
|
if (startAnimations != null) {
|
||||||
|
assert(startAnimations!.length == 2,
|
||||||
|
"start animations must be have 2 android resources");
|
||||||
|
}
|
||||||
|
if (exitAnimations != null) {
|
||||||
|
assert(exitAnimations!.length == 2,
|
||||||
|
"exit animations must be have 2 android resources");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Gets a possible [ChromeSafariBrowserSettings] instance from a [Map] value.
|
||||||
|
static ChromeSafariBrowserSettings? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = ChromeSafariBrowserSettings(
|
||||||
|
toolbarBackgroundColor: map['toolbarBackgroundColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['toolbarBackgroundColor'])
|
||||||
|
: null,
|
||||||
|
navigationBarColor: map['navigationBarColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['navigationBarColor'])
|
||||||
|
: null,
|
||||||
|
navigationBarDividerColor: map['navigationBarDividerColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['navigationBarDividerColor'])
|
||||||
|
: null,
|
||||||
|
secondaryToolbarColor: map['secondaryToolbarColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['secondaryToolbarColor'])
|
||||||
|
: null,
|
||||||
|
packageName: map['packageName'],
|
||||||
|
displayMode: _deserializeDisplayMode(map['displayMode']),
|
||||||
|
startAnimations: map['startAnimations'] != null
|
||||||
|
? List<AndroidResource>.from(map['startAnimations']
|
||||||
|
.map((e) => AndroidResource.fromMap(e?.cast<String, dynamic>())!))
|
||||||
|
: null,
|
||||||
|
exitAnimations: map['exitAnimations'] != null
|
||||||
|
? List<AndroidResource>.from(map['exitAnimations']
|
||||||
|
.map((e) => AndroidResource.fromMap(e?.cast<String, dynamic>())!))
|
||||||
|
: null,
|
||||||
|
preferredBarTintColor: map['preferredBarTintColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['preferredBarTintColor'])
|
||||||
|
: null,
|
||||||
|
preferredControlTintColor: map['preferredControlTintColor'] != null
|
||||||
|
? UtilColor.fromStringRepresentation(map['preferredControlTintColor'])
|
||||||
|
: null,
|
||||||
|
activityButton: ActivityButton.fromMap(
|
||||||
|
map['activityButton']?.cast<String, dynamic>()),
|
||||||
|
eventAttribution: UIEventAttribution.fromMap(
|
||||||
|
map['eventAttribution']?.cast<String, dynamic>()),
|
||||||
|
);
|
||||||
|
instance.shareState =
|
||||||
|
CustomTabsShareState.fromNativeValue(map['shareState']);
|
||||||
|
instance.showTitle = map['showTitle'];
|
||||||
|
instance.enableUrlBarHiding = map['enableUrlBarHiding'];
|
||||||
|
instance.instantAppsEnabled = map['instantAppsEnabled'];
|
||||||
|
instance.keepAliveEnabled = map['keepAliveEnabled'];
|
||||||
|
instance.isSingleInstance = map['isSingleInstance'];
|
||||||
|
instance.noHistory = map['noHistory'];
|
||||||
|
instance.isTrustedWebActivity = map['isTrustedWebActivity'];
|
||||||
|
instance.additionalTrustedOrigins =
|
||||||
|
map['additionalTrustedOrigins']?.cast<String>();
|
||||||
|
instance.screenOrientation =
|
||||||
|
TrustedWebActivityScreenOrientation.fromNativeValue(
|
||||||
|
map['screenOrientation']);
|
||||||
|
instance.alwaysUseBrowserUI = map['alwaysUseBrowserUI'];
|
||||||
|
instance.entersReaderIfAvailable = map['entersReaderIfAvailable'];
|
||||||
|
instance.barCollapsingEnabled = map['barCollapsingEnabled'];
|
||||||
|
instance.dismissButtonStyle =
|
||||||
|
DismissButtonStyle.fromNativeValue(map['dismissButtonStyle']);
|
||||||
|
instance.presentationStyle =
|
||||||
|
ModalPresentationStyle.fromNativeValue(map['presentationStyle']);
|
||||||
|
instance.transitionStyle =
|
||||||
|
ModalTransitionStyle.fromNativeValue(map['transitionStyle']);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"shareState": shareState?.toNativeValue(),
|
||||||
|
"showTitle": showTitle,
|
||||||
|
"toolbarBackgroundColor": toolbarBackgroundColor?.toHex(),
|
||||||
|
"navigationBarColor": navigationBarColor?.toHex(),
|
||||||
|
"navigationBarDividerColor": navigationBarDividerColor?.toHex(),
|
||||||
|
"secondaryToolbarColor": secondaryToolbarColor?.toHex(),
|
||||||
|
"enableUrlBarHiding": enableUrlBarHiding,
|
||||||
|
"instantAppsEnabled": instantAppsEnabled,
|
||||||
|
"packageName": packageName,
|
||||||
|
"keepAliveEnabled": keepAliveEnabled,
|
||||||
|
"isSingleInstance": isSingleInstance,
|
||||||
|
"noHistory": noHistory,
|
||||||
|
"isTrustedWebActivity": isTrustedWebActivity,
|
||||||
|
"additionalTrustedOrigins": additionalTrustedOrigins,
|
||||||
|
"displayMode": displayMode?.toMap(),
|
||||||
|
"screenOrientation": screenOrientation?.toNativeValue(),
|
||||||
|
"startAnimations": startAnimations?.map((e) => e.toMap()).toList(),
|
||||||
|
"exitAnimations": exitAnimations?.map((e) => e.toMap()).toList(),
|
||||||
|
"alwaysUseBrowserUI": alwaysUseBrowserUI,
|
||||||
|
"entersReaderIfAvailable": entersReaderIfAvailable,
|
||||||
|
"barCollapsingEnabled": barCollapsingEnabled,
|
||||||
|
"dismissButtonStyle": dismissButtonStyle?.toNativeValue(),
|
||||||
|
"preferredBarTintColor": preferredBarTintColor?.toHex(),
|
||||||
|
"preferredControlTintColor": preferredControlTintColor?.toHex(),
|
||||||
|
"presentationStyle": presentationStyle?.toNativeValue(),
|
||||||
|
"transitionStyle": transitionStyle?.toNativeValue(),
|
||||||
|
"activityButton": activityButton?.toMap(),
|
||||||
|
"eventAttribution": eventAttribution?.toMap(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns a copy of ChromeSafariBrowserSettings.
|
||||||
|
ChromeSafariBrowserSettings copy() {
|
||||||
|
return ChromeSafariBrowserSettings.fromMap(toMap()) ??
|
||||||
|
ChromeSafariBrowserSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ChromeSafariBrowserSettings{shareState: $shareState, showTitle: $showTitle, toolbarBackgroundColor: $toolbarBackgroundColor, navigationBarColor: $navigationBarColor, navigationBarDividerColor: $navigationBarDividerColor, secondaryToolbarColor: $secondaryToolbarColor, enableUrlBarHiding: $enableUrlBarHiding, instantAppsEnabled: $instantAppsEnabled, packageName: $packageName, keepAliveEnabled: $keepAliveEnabled, isSingleInstance: $isSingleInstance, noHistory: $noHistory, isTrustedWebActivity: $isTrustedWebActivity, additionalTrustedOrigins: $additionalTrustedOrigins, displayMode: $displayMode, screenOrientation: $screenOrientation, startAnimations: $startAnimations, exitAnimations: $exitAnimations, alwaysUseBrowserUI: $alwaysUseBrowserUI, entersReaderIfAvailable: $entersReaderIfAvailable, barCollapsingEnabled: $barCollapsingEnabled, dismissButtonStyle: $dismissButtonStyle, preferredBarTintColor: $preferredBarTintColor, preferredControlTintColor: $preferredControlTintColor, presentationStyle: $presentationStyle, transitionStyle: $transitionStyle, activityButton: $activityButton, eventAttribution: $eventAttribution}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
export 'chrome_safari_browser.dart';
|
export 'chrome_safari_browser.dart';
|
||||||
export 'chrome_safari_browser_settings.dart';
|
export 'chrome_safari_browser_settings.dart'
|
||||||
|
show
|
||||||
|
ChromeSafariBrowserOptions,
|
||||||
|
ChromeSafariBrowserSettings,
|
||||||
|
ChromeSafariBrowserClassOptions;
|
||||||
export 'android/main.dart';
|
export 'android/main.dart';
|
||||||
export 'apple/main.dart';
|
export 'apple/main.dart';
|
||||||
|
|
|
@ -783,22 +783,3 @@ class _InAppWebViewState extends State<InAppWebView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on PlatformViewsService {
|
|
||||||
static SurfaceAndroidViewController initExpensiveAndroidView2({
|
|
||||||
required int id,
|
|
||||||
required String viewType,
|
|
||||||
required TextDirection layoutDirection,
|
|
||||||
dynamic creationParams,
|
|
||||||
MessageCodec<dynamic>? creationParamsCodec,
|
|
||||||
VoidCallback? onFocus,
|
|
||||||
}) {
|
|
||||||
return PlatformViewsService.initSurfaceAndroidView(
|
|
||||||
id: id,
|
|
||||||
viewType: viewType,
|
|
||||||
layoutDirection: layoutDirection,
|
|
||||||
creationParams: creationParams,
|
|
||||||
creationParamsCodec: const StandardMessageCodec(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3683,6 +3683,24 @@ class InAppWebViewController {
|
||||||
'setWebContentsDebuggingEnabled', args);
|
'setWebContentsDebuggingEnabled', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Gets the WebView variations encoded to be used as the X-Client-Data HTTP header.
|
||||||
|
///
|
||||||
|
///The app is responsible for adding the X-Client-Data header to any request
|
||||||
|
///that may use variations metadata, such as requests to Google web properties.
|
||||||
|
///The returned string will be a base64 encoded ClientVariations proto:
|
||||||
|
///https://source.chromium.org/chromium/chromium/src/+/main:components/variations/proto/client_variations.proto
|
||||||
|
///
|
||||||
|
///The string may be empty if the header is not available.
|
||||||
|
///
|
||||||
|
///**NOTE for Android native WebView**: This method should only be called if [WebViewFeature.isFeatureSupported] returns `true` for [WebViewFeature.GET_VARIATIONS_HEADER].
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView ([Official API - WebViewCompat.getVariationsHeader](https://developer.android.com/reference/androidx/webkit/WebViewCompat#getVariationsHeader()))
|
||||||
|
static Future<String?> getVariationsHeader() async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
return await _staticChannel.invokeMethod('getVariationsHeader', args);
|
||||||
|
}
|
||||||
|
|
||||||
///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
|
///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
|
||||||
///
|
///
|
||||||
///[urlScheme] represents the URL scheme associated with the resource.
|
///[urlScheme] represents the URL scheme associated with the resource.
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
import 'ui_image.dart';
|
||||||
|
|
||||||
|
part 'activity_button.g.dart';
|
||||||
|
|
||||||
|
///Class that represents a custom button to show in `SFSafariViewController`'s toolbar.
|
||||||
|
///When tapped, it will invoke a Share or Action Extension bundled with your app.
|
||||||
|
///The default VoiceOver description of this button is the `CFBundleDisplayName` set in the extension's `Info.plist`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
@ExchangeableObject()
|
||||||
|
class ActivityButton_ {
|
||||||
|
///The name of the image asset or file.
|
||||||
|
UIImage_ templateImage;
|
||||||
|
|
||||||
|
///The name of the system symbol image.
|
||||||
|
String extensionIdentifier;
|
||||||
|
|
||||||
|
@ExchangeableObjectConstructor()
|
||||||
|
ActivityButton_(
|
||||||
|
{required this.templateImage, required this.extensionIdentifier});
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'activity_button.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents a custom button to show in `SFSafariViewController`'s toolbar.
|
||||||
|
///When tapped, it will invoke a Share or Action Extension bundled with your app.
|
||||||
|
///The default VoiceOver description of this button is the `CFBundleDisplayName` set in the extension's `Info.plist`.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
class ActivityButton {
|
||||||
|
///The name of the image asset or file.
|
||||||
|
UIImage templateImage;
|
||||||
|
|
||||||
|
///The name of the system symbol image.
|
||||||
|
String extensionIdentifier;
|
||||||
|
ActivityButton(
|
||||||
|
{required this.templateImage, required this.extensionIdentifier});
|
||||||
|
|
||||||
|
///Gets a possible [ActivityButton] instance from a [Map] value.
|
||||||
|
static ActivityButton? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = ActivityButton(
|
||||||
|
templateImage:
|
||||||
|
UIImage.fromMap(map['templateImage']?.cast<String, dynamic>())!,
|
||||||
|
extensionIdentifier: map['extensionIdentifier'],
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"templateImage": templateImage.toMap(),
|
||||||
|
"extensionIdentifier": extensionIdentifier,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ActivityButton{templateImage: $templateImage, extensionIdentifier: $extensionIdentifier}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
part 'android_resource.g.dart';
|
||||||
|
|
||||||
|
///Class that represents an android resource.
|
||||||
|
@ExchangeableObject()
|
||||||
|
class AndroidResource_ {
|
||||||
|
///Android resource name.
|
||||||
|
///
|
||||||
|
///A list of available `android.R.anim` can be found
|
||||||
|
///[here](https://developer.android.com/reference/android/R.anim).
|
||||||
|
///
|
||||||
|
///A list of available `androidx.appcompat.R.anim` can be found
|
||||||
|
///[here](https://android.googlesource.com/platform/frameworks/support/+/HEAD/appcompat/appcompat/src/main/res/anim/)
|
||||||
|
///(abc_*.xml files).
|
||||||
|
///In this case, [defPackage] must match your App Android package name.
|
||||||
|
String name;
|
||||||
|
|
||||||
|
///Optional default resource type to find, if "type/" is not included in the name.
|
||||||
|
///Can be `null` to require an explicit type.
|
||||||
|
///
|
||||||
|
///Example: "anim"
|
||||||
|
String? defType;
|
||||||
|
|
||||||
|
///Optional default package to find, if "package:" is not included in the name.
|
||||||
|
///Can be `null` to require an explicit package.
|
||||||
|
///
|
||||||
|
///Example: "android" if you want use resources from `android.R.`
|
||||||
|
String? defPackage;
|
||||||
|
|
||||||
|
AndroidResource_({required this.name, this.defType, this.defPackage});
|
||||||
|
|
||||||
|
static AndroidResource_ anim({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource_(
|
||||||
|
name: name, defType: "anim", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidResource_ layout({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource_(
|
||||||
|
name: name, defType: "layout", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidResource_ id({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource_(name: name, defType: "id", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'android_resource.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents an android resource.
|
||||||
|
class AndroidResource {
|
||||||
|
///Android resource name.
|
||||||
|
///
|
||||||
|
///A list of available `android.R.anim` can be found
|
||||||
|
///[here](https://developer.android.com/reference/android/R.anim).
|
||||||
|
///
|
||||||
|
///A list of available `androidx.appcompat.R.anim` can be found
|
||||||
|
///[here](https://android.googlesource.com/platform/frameworks/support/+/HEAD/appcompat/appcompat/src/main/res/anim/)
|
||||||
|
///(abc_*.xml files).
|
||||||
|
///In this case, [defPackage] must match your App Android package name.
|
||||||
|
String name;
|
||||||
|
|
||||||
|
///Optional default resource type to find, if "type/" is not included in the name.
|
||||||
|
///Can be `null` to require an explicit type.
|
||||||
|
///
|
||||||
|
///Example: "anim"
|
||||||
|
String? defType;
|
||||||
|
|
||||||
|
///Optional default package to find, if "package:" is not included in the name.
|
||||||
|
///Can be `null` to require an explicit package.
|
||||||
|
///
|
||||||
|
///Example: "android" if you want use resources from `android.R.`
|
||||||
|
String? defPackage;
|
||||||
|
AndroidResource({required this.name, this.defType, this.defPackage});
|
||||||
|
|
||||||
|
///Gets a possible [AndroidResource] instance from a [Map] value.
|
||||||
|
static AndroidResource? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = AndroidResource(
|
||||||
|
name: map['name'],
|
||||||
|
defType: map['defType'],
|
||||||
|
defPackage: map['defPackage'],
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidResource anim({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource(name: name, defType: "anim", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidResource layout({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource(
|
||||||
|
name: name, defType: "layout", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidResource id({required String name, String? defPackage}) {
|
||||||
|
return AndroidResource(name: name, defType: "id", defPackage: defPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"name": name,
|
||||||
|
"defType": defType,
|
||||||
|
"defPackage": defPackage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AndroidResource{name: $name, defType: $defType, defPackage: $defPackage}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
import '../chrome_safari_browser/chrome_safari_browser.dart';
|
||||||
|
|
||||||
|
part 'custom_tabs_navigation_event_type.g.dart';
|
||||||
|
|
||||||
|
///The type corresponding to the navigation event of [ChromeSafariBrowser.onNavigationEvent].
|
||||||
|
@ExchangeableEnum()
|
||||||
|
class CustomTabsNavigationEventType_ {
|
||||||
|
// ignore: unused_field
|
||||||
|
final int _value;
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final int? _nativeValue = null;
|
||||||
|
|
||||||
|
const CustomTabsNavigationEventType_._internal(this._value);
|
||||||
|
|
||||||
|
///Sent when the tab has started loading a page.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 1)])
|
||||||
|
static const STARTED = const CustomTabsNavigationEventType_._internal(1);
|
||||||
|
|
||||||
|
///Sent when the tab has finished loading a page.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 2)])
|
||||||
|
static const FINISHED = const CustomTabsNavigationEventType_._internal(2);
|
||||||
|
|
||||||
|
///Sent when the tab couldn't finish loading due to a failure.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 3)])
|
||||||
|
static const FAILED = const CustomTabsNavigationEventType_._internal(3);
|
||||||
|
|
||||||
|
///Sent when loading was aborted by a user action before it finishes like clicking on a link or refreshing the page.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 4)])
|
||||||
|
static const ABORTED = const CustomTabsNavigationEventType_._internal(4);
|
||||||
|
|
||||||
|
///Sent when the tab becomes visible.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 5)])
|
||||||
|
static const TAB_SHOWN = const CustomTabsNavigationEventType_._internal(5);
|
||||||
|
|
||||||
|
///Sent when the tab becomes hidden.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 6)])
|
||||||
|
static const TAB_HIDDEN = const CustomTabsNavigationEventType_._internal(6);
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'custom_tabs_navigation_event_type.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableEnumGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///The type corresponding to the navigation event of [ChromeSafariBrowser.onNavigationEvent].
|
||||||
|
class CustomTabsNavigationEventType {
|
||||||
|
final int _value;
|
||||||
|
final int? _nativeValue;
|
||||||
|
const CustomTabsNavigationEventType._internal(this._value, this._nativeValue);
|
||||||
|
// ignore: unused_element
|
||||||
|
factory CustomTabsNavigationEventType._internalMultiPlatform(
|
||||||
|
int value, Function nativeValue) =>
|
||||||
|
CustomTabsNavigationEventType._internal(value, nativeValue());
|
||||||
|
|
||||||
|
///Sent when the tab has started loading a page.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final STARTED =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(1, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Sent when the tab has finished loading a page.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final FINISHED =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(2, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Sent when the tab couldn't finish loading due to a failure.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final FAILED =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(3, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Sent when loading was aborted by a user action before it finishes like clicking on a link or refreshing the page.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final ABORTED =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(4, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Sent when the tab becomes visible.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final TAB_SHOWN =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(5, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Sent when the tab becomes hidden.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final TAB_HIDDEN =
|
||||||
|
CustomTabsNavigationEventType._internalMultiPlatform(6, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 6;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Set of all values of [CustomTabsNavigationEventType].
|
||||||
|
static final Set<CustomTabsNavigationEventType> values = [
|
||||||
|
CustomTabsNavigationEventType.STARTED,
|
||||||
|
CustomTabsNavigationEventType.FINISHED,
|
||||||
|
CustomTabsNavigationEventType.FAILED,
|
||||||
|
CustomTabsNavigationEventType.ABORTED,
|
||||||
|
CustomTabsNavigationEventType.TAB_SHOWN,
|
||||||
|
CustomTabsNavigationEventType.TAB_HIDDEN,
|
||||||
|
].toSet();
|
||||||
|
|
||||||
|
///Gets a possible [CustomTabsNavigationEventType] instance from [int] value.
|
||||||
|
static CustomTabsNavigationEventType? fromValue(int? value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return CustomTabsNavigationEventType.values
|
||||||
|
.firstWhere((element) => element.toValue() == value);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Gets a possible [CustomTabsNavigationEventType] instance from a native value.
|
||||||
|
static CustomTabsNavigationEventType? fromNativeValue(int? value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return CustomTabsNavigationEventType.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 1:
|
||||||
|
return 'STARTED';
|
||||||
|
case 2:
|
||||||
|
return 'FINISHED';
|
||||||
|
case 3:
|
||||||
|
return 'FAILED';
|
||||||
|
case 4:
|
||||||
|
return 'ABORTED';
|
||||||
|
case 5:
|
||||||
|
return 'TAB_SHOWN';
|
||||||
|
case 6:
|
||||||
|
return 'TAB_HIDDEN';
|
||||||
|
}
|
||||||
|
return _value.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
part 'custom_tabs_relation_type.g.dart';
|
||||||
|
|
||||||
|
///Custom Tabs relation for which the result is available.
|
||||||
|
@ExchangeableEnum()
|
||||||
|
class CustomTabsRelationType_ {
|
||||||
|
// ignore: unused_field
|
||||||
|
final int _value;
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final int? _nativeValue = null;
|
||||||
|
|
||||||
|
const CustomTabsRelationType_._internal(this._value);
|
||||||
|
|
||||||
|
///For App -> Web transitions, requests the app to use the declared origin to be used as origin for the client app in the web APIs context.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 1)])
|
||||||
|
static const USE_AS_ORIGIN = const CustomTabsRelationType_._internal(1);
|
||||||
|
|
||||||
|
///Requests the ability to handle all URLs from a given origin.
|
||||||
|
@EnumSupportedPlatforms(platforms: [EnumAndroidPlatform(value: 2)])
|
||||||
|
static const HANDLE_ALL_URLS = const CustomTabsRelationType_._internal(2);
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'custom_tabs_relation_type.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableEnumGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Custom Tabs relation for which the result is available.
|
||||||
|
class CustomTabsRelationType {
|
||||||
|
final int _value;
|
||||||
|
final int? _nativeValue;
|
||||||
|
const CustomTabsRelationType._internal(this._value, this._nativeValue);
|
||||||
|
// ignore: unused_element
|
||||||
|
factory CustomTabsRelationType._internalMultiPlatform(
|
||||||
|
int value, Function nativeValue) =>
|
||||||
|
CustomTabsRelationType._internal(value, nativeValue());
|
||||||
|
|
||||||
|
///For App -> Web transitions, requests the app to use the declared origin to be used as origin for the client app in the web APIs context.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final USE_AS_ORIGIN =
|
||||||
|
CustomTabsRelationType._internalMultiPlatform(1, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Requests the ability to handle all URLs from a given origin.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- Android native WebView
|
||||||
|
static final HANDLE_ALL_URLS =
|
||||||
|
CustomTabsRelationType._internalMultiPlatform(2, () {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
///Set of all values of [CustomTabsRelationType].
|
||||||
|
static final Set<CustomTabsRelationType> values = [
|
||||||
|
CustomTabsRelationType.USE_AS_ORIGIN,
|
||||||
|
CustomTabsRelationType.HANDLE_ALL_URLS,
|
||||||
|
].toSet();
|
||||||
|
|
||||||
|
///Gets a possible [CustomTabsRelationType] instance from [int] value.
|
||||||
|
static CustomTabsRelationType? fromValue(int? value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return CustomTabsRelationType.values
|
||||||
|
.firstWhere((element) => element.toValue() == value);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Gets a possible [CustomTabsRelationType] instance from a native value.
|
||||||
|
static CustomTabsRelationType? fromNativeValue(int? value) {
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return CustomTabsRelationType.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 1:
|
||||||
|
return 'USE_AS_ORIGIN';
|
||||||
|
case 2:
|
||||||
|
return 'HANDLE_ALL_URLS';
|
||||||
|
}
|
||||||
|
return _value.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -215,3 +215,11 @@ export 'printer.dart' show Printer;
|
||||||
export 'window_type.dart' show WindowType;
|
export 'window_type.dart' show WindowType;
|
||||||
export 'window_style_mask.dart' show WindowStyleMask;
|
export 'window_style_mask.dart' show WindowStyleMask;
|
||||||
export 'window_titlebar_separator_style.dart' show WindowTitlebarSeparatorStyle;
|
export 'window_titlebar_separator_style.dart' show WindowTitlebarSeparatorStyle;
|
||||||
|
export 'custom_tabs_navigation_event_type.dart'
|
||||||
|
show CustomTabsNavigationEventType;
|
||||||
|
export 'custom_tabs_relation_type.dart' show CustomTabsRelationType;
|
||||||
|
export 'prewarming_token.dart' show PrewarmingToken;
|
||||||
|
export 'android_resource.dart' show AndroidResource;
|
||||||
|
export 'ui_image.dart' show UIImage;
|
||||||
|
export 'activity_button.dart' show ActivityButton;
|
||||||
|
export 'ui_event_attribution.dart' show UIEventAttribution;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
import '../chrome_safari_browser/chrome_safari_browser.dart';
|
||||||
|
|
||||||
|
part 'prewarming_token.g.dart';
|
||||||
|
|
||||||
|
///Class that represents the Prewarming Token returned by [ChromeSafariBrowser.prewarmConnections].
|
||||||
|
@ExchangeableObject()
|
||||||
|
class PrewarmingToken_ {
|
||||||
|
///Prewarming Token id.
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
PrewarmingToken_({required this.id});
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'prewarming_token.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents the Prewarming Token returned by [ChromeSafariBrowser.prewarmConnections].
|
||||||
|
class PrewarmingToken {
|
||||||
|
///Prewarming Token id.
|
||||||
|
final String id;
|
||||||
|
PrewarmingToken({required this.id});
|
||||||
|
|
||||||
|
///Gets a possible [PrewarmingToken] instance from a [Map] value.
|
||||||
|
static PrewarmingToken? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = PrewarmingToken(
|
||||||
|
id: map['id'],
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PrewarmingToken{id: $id}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
part 'ui_event_attribution.g.dart';
|
||||||
|
|
||||||
|
///Class that represents an object that contains event attribution information for Private Click Measurement.
|
||||||
|
///
|
||||||
|
///Apps use event attribution objects to send data to the browser when opening an external website that supports Private Click Measurement (PCM).
|
||||||
|
///For more information on the proposed PCM web standard, see [Introducing Private Click Measurement](https://webkit.org/blog/11529/introducing-private-click-measurement-pcm/)
|
||||||
|
///and [Private Click Measurement Draft Community Group Report](https://privacycg.github.io/private-click-measurement/).
|
||||||
|
///
|
||||||
|
///Check [UIEventAttribution](https://developer.apple.com/documentation/uikit/uieventattribution) for details.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
@ExchangeableObject()
|
||||||
|
class UIEventAttribution_ {
|
||||||
|
///An 8-bit number that identifies the source of the click for attribution. Value must be between 0 and 255.
|
||||||
|
int sourceIdentifier;
|
||||||
|
|
||||||
|
///The destination URL of the attribution.
|
||||||
|
Uri destinationURL;
|
||||||
|
|
||||||
|
///A description of the source of the attribution.
|
||||||
|
String sourceDescription;
|
||||||
|
|
||||||
|
///A string that describes the entity that purchased the attributed content.
|
||||||
|
String purchaser;
|
||||||
|
|
||||||
|
@ExchangeableObjectConstructor()
|
||||||
|
UIEventAttribution_(
|
||||||
|
{required this.sourceIdentifier,
|
||||||
|
required this.destinationURL,
|
||||||
|
required this.sourceDescription,
|
||||||
|
required this.purchaser});
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'ui_event_attribution.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents an object that contains event attribution information for Private Click Measurement.
|
||||||
|
///
|
||||||
|
///Apps use event attribution objects to send data to the browser when opening an external website that supports Private Click Measurement (PCM).
|
||||||
|
///For more information on the proposed PCM web standard, see [Introducing Private Click Measurement](https://webkit.org/blog/11529/introducing-private-click-measurement-pcm/)
|
||||||
|
///and [Private Click Measurement Draft Community Group Report](https://privacycg.github.io/private-click-measurement/).
|
||||||
|
///
|
||||||
|
///Check [UIEventAttribution](https://developer.apple.com/documentation/uikit/uieventattribution) for details.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
class UIEventAttribution {
|
||||||
|
///An 8-bit number that identifies the source of the click for attribution. Value must be between 0 and 255.
|
||||||
|
int sourceIdentifier;
|
||||||
|
|
||||||
|
///The destination URL of the attribution.
|
||||||
|
Uri destinationURL;
|
||||||
|
|
||||||
|
///A description of the source of the attribution.
|
||||||
|
String sourceDescription;
|
||||||
|
|
||||||
|
///A string that describes the entity that purchased the attributed content.
|
||||||
|
String purchaser;
|
||||||
|
UIEventAttribution(
|
||||||
|
{required this.sourceIdentifier,
|
||||||
|
required this.destinationURL,
|
||||||
|
required this.sourceDescription,
|
||||||
|
required this.purchaser});
|
||||||
|
|
||||||
|
///Gets a possible [UIEventAttribution] instance from a [Map] value.
|
||||||
|
static UIEventAttribution? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = UIEventAttribution(
|
||||||
|
sourceIdentifier: map['sourceIdentifier'],
|
||||||
|
destinationURL: (Uri.tryParse(map['destinationURL']) ?? Uri()),
|
||||||
|
sourceDescription: map['sourceDescription'],
|
||||||
|
purchaser: map['purchaser'],
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"sourceIdentifier": sourceIdentifier,
|
||||||
|
"destinationURL": destinationURL.toString(),
|
||||||
|
"sourceDescription": sourceDescription,
|
||||||
|
"purchaser": purchaser,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'UIEventAttribution{sourceIdentifier: $sourceIdentifier, destinationURL: $destinationURL, sourceDescription: $sourceDescription, purchaser: $purchaser}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||||
|
|
||||||
|
part 'ui_image.g.dart';
|
||||||
|
|
||||||
|
///Class that represents an object that manages iOS image data in your app.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
@ExchangeableObject()
|
||||||
|
class UIImage_ {
|
||||||
|
///The name of the image asset or file.
|
||||||
|
String? name;
|
||||||
|
|
||||||
|
///The name of the system symbol image.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 13.0+.
|
||||||
|
String? systemName;
|
||||||
|
|
||||||
|
///The data object containing the image data.
|
||||||
|
Uint8List? data;
|
||||||
|
|
||||||
|
@ExchangeableObjectConstructor()
|
||||||
|
UIImage_({this.name, this.systemName, this.data}) {
|
||||||
|
assert(this.name != null || this.systemName != null || this.data != null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'ui_image.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ExchangeableObjectGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
///Class that represents an object that manages iOS image data in your app.
|
||||||
|
///
|
||||||
|
///**Supported Platforms/Implementations**:
|
||||||
|
///- iOS
|
||||||
|
class UIImage {
|
||||||
|
///The name of the image asset or file.
|
||||||
|
String? name;
|
||||||
|
|
||||||
|
///The name of the system symbol image.
|
||||||
|
///
|
||||||
|
///**NOTE**: available on iOS 13.0+.
|
||||||
|
String? systemName;
|
||||||
|
|
||||||
|
///The data object containing the image data.
|
||||||
|
Uint8List? data;
|
||||||
|
UIImage({this.name, this.systemName, this.data}) {
|
||||||
|
assert(this.name != null || this.systemName != null || this.data != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Gets a possible [UIImage] instance from a [Map] value.
|
||||||
|
static UIImage? fromMap(Map<String, dynamic>? map) {
|
||||||
|
if (map == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final instance = UIImage(
|
||||||
|
name: map['name'],
|
||||||
|
systemName: map['systemName'],
|
||||||
|
data: map['data'],
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"name": name,
|
||||||
|
"systemName": systemName,
|
||||||
|
"data": data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///Converts instance to a map.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'UIImage{name: $name, systemName: $systemName, data: $data}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
name: flutter_inappwebview
|
name: flutter_inappwebview
|
||||||
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
||||||
version: 6.0.0-beta.8
|
version: 6.0.0-beta.9
|
||||||
homepage: https://inappwebview.dev/
|
homepage: https://inappwebview.dev/
|
||||||
repository: https://github.com/pichillilorenzo/flutter_inappwebview
|
repository: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||||
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues
|
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues
|
||||||
|
|
|
@ -34,13 +34,17 @@ dart $PROJECT_DIR/tool/env.dart
|
||||||
cd $PROJECT_DIR/test_node_server
|
cd $PROJECT_DIR/test_node_server
|
||||||
node index.js &
|
node index.js &
|
||||||
|
|
||||||
|
# Only for Android
|
||||||
|
# Open Chrome on the development device, navigate to chrome://flags, search for an item called Enable command line on non-rooted devices and change it to ENABLED and then restart the browser.
|
||||||
|
adb shell "echo '_ --disable-digital-asset-link-verification-for-url=\"https://flutter.dev\"' > /data/local/tmp/chrome-command-line" || true
|
||||||
|
|
||||||
flutter --version
|
flutter --version
|
||||||
flutter clean
|
flutter clean
|
||||||
flutter pub get
|
flutter pub get
|
||||||
cd $PROJECT_DIR/example
|
cd $PROJECT_DIR/example
|
||||||
flutter clean
|
flutter clean
|
||||||
if [ ! -z "$2" ] && [ $PLATFORM = "web" ]; then
|
if [ ! -z "$2" ] && [ $PLATFORM = "web" ]; then
|
||||||
flutter driver --driver=test_driver/integration_test.dart --target=integration_test/webview_flutter_test.dart --device-id=chrome
|
flutter driver --driver=test_driver/integration_test.dart --target=integration_test/webview_flutter_test.dart --device-id=chrome
|
||||||
else
|
else
|
||||||
flutter driver --driver=test_driver/integration_test.dart --target=integration_test/webview_flutter_test.dart
|
flutter driver --driver=test_driver/integration_test.dart --target=integration_test/webview_flutter_test.dart
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -153,6 +153,19 @@ app.get("/", (req, res) => {
|
||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get("/echo-headers", (req, res) => {
|
||||||
|
res.send(`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre style="word-wrap: break-word; white-space: pre-wrap;">${JSON.stringify(req.headers)}</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
|
||||||
app.get('/test-index', (req, res) => {
|
app.get('/test-index', (req, res) => {
|
||||||
res.sendFile(__dirname + '/public/index.html');
|
res.sendFile(__dirname + '/public/index.html');
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue