diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index f5523ced4717d..04facb782b06d 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.2.0 + +* Adds a new `inAppBrowserView` launch mode, to distinguish in-app browser + views (such as Android Custom Tabs or SFSafariViewController) from simple + web views. +* Adds `supportsMode` and `supportsCloseForMode` to query platform support for + launching and closing with various modes. + ## 2.1.5 * Updates documentation to mention support for Android Custom Tabs. diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart b/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart index ca9d8e1c9175b..610df29f70915 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart @@ -13,9 +13,13 @@ enum PreferredLaunchMode { /// implementation. platformDefault, - /// Loads the URL in an in-app web view (e.g., Android Custom Tabs, Safari View Controller). + /// Loads the URL in an in-app web view (e.g., Android WebView). inAppWebView, + /// Loads the URL in an in-app browser view (e.g., Android Custom Tabs, + /// SFSafariViewController). + inAppBrowserView, + /// Passes the URL to the OS to be handled by another application. externalApplication, diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart index 8928d4249e905..da7404f98a1b6 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart @@ -9,6 +9,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../link.dart'; import '../method_channel_url_launcher.dart'; import '../url_launcher_platform_interface.dart'; +import 'types.dart'; /// The interface that implementations of url_launcher must implement. /// @@ -72,6 +73,7 @@ abstract class UrlLauncherPlatform extends PlatformInterface { Future launchUrl(String url, LaunchOptions options) { final bool isWebURL = url.startsWith('http:') || url.startsWith('https:'); final bool useWebView = options.mode == PreferredLaunchMode.inAppWebView || + options.mode == PreferredLaunchMode.inAppBrowserView || (isWebURL && options.mode == PreferredLaunchMode.platformDefault); return launch( @@ -87,8 +89,31 @@ abstract class UrlLauncherPlatform extends PlatformInterface { ); } - /// Closes the WebView, if one was opened earlier by [launch]. + /// Closes the web view, if one was opened earlier by [launchUrl]. + /// + /// This will only work if the launch mode (the actual launch mode used, + /// not the requested launch mode, which may not match if [supportsMode] is + /// false for the requested mode) was one for which [supportsCloseForMode] + /// returns true. Future closeWebView() { throw UnimplementedError('closeWebView() has not been implemented.'); } + + /// Returns true if the given launch mode is supported by the current + /// implementation. + /// + /// Clients are not required to query this, as implementations are strongly + /// encouraged to automatically fall back to other modes if a launch is + /// requested using an unsupported mode (matching historical behavior of the + /// plugin, and thus maximizing compatibility with existing code). + Future supportsMode(PreferredLaunchMode mode) { + return Future.value(mode == PreferredLaunchMode.platformDefault); + } + + /// Returns true if the given launch mode can be closed with [closeWebView]. + Future supportsCloseForMode(PreferredLaunchMode mode) { + // This is the historical documented behavior, so default to that for + // compatibility. + return Future.value(mode == PreferredLaunchMode.inAppWebView); + } } diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 5aa135fbc3157..a999793f58e94 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.5 +version: 2.2.0 environment: sdk: ">=2.19.0 <4.0.0" diff --git a/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart index f764f679f96de..56920596eb770 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart @@ -118,4 +118,55 @@ void main() { expect(launcher.headers['foo'], 'bar'); expect(launcher.webOnlyWindowName, 'a_name'); }); + + test('supportsMode defaults to true for platform default', () async { + final UrlLauncherPlatform launcher = CapturingUrlLauncher(); + + expect( + await launcher.supportsMode(PreferredLaunchMode.platformDefault), true); + }); + + test('supportsMode defaults to false for all specific values', () async { + final UrlLauncherPlatform launcher = CapturingUrlLauncher(); + + expect(await launcher.supportsMode(PreferredLaunchMode.externalApplication), + false); + expect( + await launcher + .supportsMode(PreferredLaunchMode.externalNonBrowserApplication), + false); + expect(await launcher.supportsMode(PreferredLaunchMode.inAppBrowserView), + false); + expect( + await launcher.supportsMode(PreferredLaunchMode.inAppWebView), false); + }); + + test('supportsCloseForMode defaults to true for in-app web views', () async { + final UrlLauncherPlatform launcher = CapturingUrlLauncher(); + + expect( + await launcher.supportsCloseForMode(PreferredLaunchMode.inAppWebView), + true); + }); + + test('supportsCloseForMode defaults to false for all other values', () async { + final UrlLauncherPlatform launcher = CapturingUrlLauncher(); + + expect( + await launcher + .supportsCloseForMode(PreferredLaunchMode.externalApplication), + false); + expect( + await launcher.supportsCloseForMode( + PreferredLaunchMode.externalNonBrowserApplication), + false); + expect( + await launcher + .supportsCloseForMode(PreferredLaunchMode.inAppBrowserView), + false); + expect( + await launcher + .supportsCloseForMode(PreferredLaunchMode.platformDefault), + false); + }); }