diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 8b7fc14b412..67b01033497 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 6.3.0 +* Adds support for `BrowserConfiguration`. +* Implements `showTitle` functionality for Android Custom Tabs. * Updates compileSdk version to 34. ## 6.2.3 diff --git a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/Messages.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/Messages.java index f2294f048f8..ff79d632093 100644 --- a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/Messages.java +++ b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.6), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.urllauncher; @@ -156,6 +156,55 @@ ArrayList toList() { } } + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class BrowserOptions { + private @NonNull Boolean showTitle; + + public @NonNull Boolean getShowTitle() { + return showTitle; + } + + public void setShowTitle(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"showTitle\" is null."); + } + this.showTitle = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + BrowserOptions() {} + + public static final class Builder { + + private @Nullable Boolean showTitle; + + public @NonNull Builder setShowTitle(@NonNull Boolean setterArg) { + this.showTitle = setterArg; + return this; + } + + public @NonNull BrowserOptions build() { + BrowserOptions pigeonReturn = new BrowserOptions(); + pigeonReturn.setShowTitle(showTitle); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(showTitle); + return toListResult; + } + + static @NonNull BrowserOptions fromList(@NonNull ArrayList list) { + BrowserOptions pigeonResult = new BrowserOptions(); + Object showTitle = list.get(0); + pigeonResult.setShowTitle((Boolean) showTitle); + return pigeonResult; + } + } + private static class UrlLauncherApiCodec extends StandardMessageCodec { public static final UrlLauncherApiCodec INSTANCE = new UrlLauncherApiCodec(); @@ -165,6 +214,8 @@ private UrlLauncherApiCodec() {} protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { case (byte) 128: + return BrowserOptions.fromList((ArrayList) readValue(buffer)); + case (byte) 129: return WebViewOptions.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -173,8 +224,11 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof WebViewOptions) { + if (value instanceof BrowserOptions) { stream.write(128); + writeValue(stream, ((BrowserOptions) value).toList()); + } else if (value instanceof WebViewOptions) { + stream.write(129); writeValue(stream, ((WebViewOptions) value).toList()); } else { super.writeValue(stream, value); @@ -190,12 +244,13 @@ public interface UrlLauncherApi { /** Opens the URL externally, returning true if successful. */ @NonNull Boolean launchUrl(@NonNull String url, @NonNull Map headers); - /** - * Opens the URL in an in-app Custom Tab or WebView, returning true if it opens successfully. - */ + /** Opens the URL in an in-app WebView, returning true if it opens successfully. */ @NonNull Boolean openUrlInApp( - @NonNull String url, @NonNull Boolean allowCustomTab, @NonNull WebViewOptions options); + @NonNull String url, + @NonNull Boolean allowCustomTab, + @NonNull WebViewOptions webViewOptions, + @NonNull BrowserOptions browserOptions); @NonNull Boolean supportsCustomTabs(); @@ -272,9 +327,12 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable UrlLaunche ArrayList args = (ArrayList) message; String urlArg = (String) args.get(0); Boolean allowCustomTabArg = (Boolean) args.get(1); - WebViewOptions optionsArg = (WebViewOptions) args.get(2); + WebViewOptions webViewOptionsArg = (WebViewOptions) args.get(2); + BrowserOptions browserOptionsArg = (BrowserOptions) args.get(3); try { - Boolean output = api.openUrlInApp(urlArg, allowCustomTabArg, optionsArg); + Boolean output = + api.openUrlInApp( + urlArg, allowCustomTabArg, webViewOptionsArg, browserOptionsArg); wrapped.add(0, output); } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); diff --git a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java index 028338c6981..35094e79944 100644 --- a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java +++ b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java @@ -18,6 +18,7 @@ import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsClient; import androidx.browser.customtabs.CustomTabsIntent; +import io.flutter.plugins.urllauncher.Messages.BrowserOptions; import io.flutter.plugins.urllauncher.Messages.UrlLauncherApi; import io.flutter.plugins.urllauncher.Messages.WebViewOptions; import java.util.Collections; @@ -98,17 +99,20 @@ void setActivity(@Nullable Activity activity) { @Override public @NonNull Boolean openUrlInApp( - @NonNull String url, @NonNull Boolean allowCustomTab, @NonNull WebViewOptions options) { + @NonNull String url, + @NonNull Boolean allowCustomTab, + @NonNull WebViewOptions webViewOptions, + @NonNull BrowserOptions browserOptions) { ensureActivity(); assert activity != null; - Bundle headersBundle = extractBundle(options.getHeaders()); + Bundle headersBundle = extractBundle(webViewOptions.getHeaders()); // Try to launch using Custom Tabs if they have the necessary functionality, unless the caller // specifically requested a web view. - if (allowCustomTab && !containsRestrictedHeader(options.getHeaders())) { + if (allowCustomTab && !containsRestrictedHeader(webViewOptions.getHeaders())) { Uri uri = Uri.parse(url); - if (openCustomTab(activity, uri, headersBundle)) { + if (openCustomTab(activity, uri, headersBundle, browserOptions)) { return true; } } @@ -118,8 +122,8 @@ void setActivity(@Nullable Activity activity) { WebViewActivity.createIntent( activity, url, - options.getEnableJavaScript(), - options.getEnableDomStorage(), + webViewOptions.getEnableJavaScript(), + webViewOptions.getEnableDomStorage(), headersBundle); try { activity.startActivity(launchIntent); @@ -141,9 +145,14 @@ public void closeWebView() { } private static boolean openCustomTab( - @NonNull Context context, @NonNull Uri uri, @NonNull Bundle headersBundle) { - CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build(); + @NonNull Context context, + @NonNull Uri uri, + @NonNull Bundle headersBundle, + @NonNull BrowserOptions options) { + CustomTabsIntent customTabsIntent = + new CustomTabsIntent.Builder().setShowTitle(options.getShowTitle()).build(); customTabsIntent.intent.putExtra(Browser.EXTRA_HEADERS, headersBundle); + try { customTabsIntent.launchUrl(context, uri); } catch (ActivityNotFoundException ex) { diff --git a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/UrlLauncherTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/UrlLauncherTest.java index 3bffbc614f0..9a545a54b43 100644 --- a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/UrlLauncherTest.java +++ b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/UrlLauncherTest.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.Browser; +import androidx.browser.customtabs.CustomTabsIntent; import androidx.test.core.app.ApplicationProvider; import java.util.HashMap; import org.junit.Test; @@ -139,6 +140,7 @@ public void openUrlInApp_opensUrlInWebViewIfNecessary() { boolean enableDomStorage = false; HashMap headers = new HashMap<>(); headers.put("key", "value"); + boolean showTitle = false; boolean result = api.openUrlInApp( @@ -148,7 +150,8 @@ public void openUrlInApp_opensUrlInWebViewIfNecessary() { .setEnableJavaScript(enableJavaScript) .setEnableDomStorage(enableDomStorage) .setHeaders(headers) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(showTitle).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -177,7 +180,8 @@ public void openWebView_opensUrlInWebViewIfRequested() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(new HashMap<>()) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(true).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -200,7 +204,8 @@ public void openWebView_opensUrlInCustomTabs() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(new HashMap<>()) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture(), isNull()); @@ -227,7 +232,8 @@ public void openWebView_opensUrlInCustomTabsWithCORSAllowedHeader() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(headers) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture(), isNull()); @@ -239,6 +245,64 @@ public void openWebView_opensUrlInCustomTabsWithCORSAllowedHeader() { assertEquals(headers.get(headerKey), passedHeaders.getString(headerKey)); } + @Test + public void openWebView_opensUrlInCustomTabsWithShowTitle() { + Activity activity = mock(Activity.class); + UrlLauncher api = new UrlLauncher(ApplicationProvider.getApplicationContext()); + api.setActivity(activity); + String url = "https://flutter.dev"; + HashMap headers = new HashMap<>(); + + boolean result = + api.openUrlInApp( + url, + true, + new Messages.WebViewOptions.Builder() + .setEnableJavaScript(false) + .setEnableDomStorage(false) + .setHeaders(headers) + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(true).build()); + + final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activity).startActivity(intentCaptor.capture(), isNull()); + assertTrue(result); + assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction()); + assertNull(intentCaptor.getValue().getComponent()); + assertEquals( + intentCaptor.getValue().getExtras().getInt(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), + CustomTabsIntent.SHOW_PAGE_TITLE); + } + + @Test + public void openWebView_opensUrlInCustomTabsWithoutShowTitle() { + Activity activity = mock(Activity.class); + UrlLauncher api = new UrlLauncher(ApplicationProvider.getApplicationContext()); + api.setActivity(activity); + String url = "https://flutter.dev"; + HashMap headers = new HashMap<>(); + + boolean result = + api.openUrlInApp( + url, + true, + new Messages.WebViewOptions.Builder() + .setEnableJavaScript(false) + .setEnableDomStorage(false) + .setHeaders(headers) + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); + + final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activity).startActivity(intentCaptor.capture(), isNull()); + assertTrue(result); + assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction()); + assertNull(intentCaptor.getValue().getComponent()); + assertEquals( + intentCaptor.getValue().getExtras().getInt(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), + CustomTabsIntent.NO_TITLE); + } + @Test public void openWebView_fallsBackToWebViewIfCustomTabFails() { Activity activity = mock(Activity.class); @@ -257,7 +321,8 @@ public void openWebView_fallsBackToWebViewIfCustomTabFails() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(new HashMap<>()) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -285,7 +350,8 @@ public void openWebView_handlesEnableJavaScript() { .setEnableJavaScript(enableJavaScript) .setEnableDomStorage(false) .setHeaders(headers) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -312,7 +378,8 @@ public void openWebView_handlesHeaders() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(headers) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -339,7 +406,8 @@ public void openWebView_handlesEnableDomStorage() { .setEnableJavaScript(false) .setEnableDomStorage(enableDomStorage) .setHeaders(headers) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(activity).startActivity(intentCaptor.capture()); @@ -348,6 +416,33 @@ public void openWebView_handlesEnableDomStorage() { intentCaptor.getValue().getExtras().getBoolean(WebViewActivity.ENABLE_DOM_EXTRA)); } + @Test + public void openWebView_handlesEnableShowTitle() { + Activity activity = mock(Activity.class); + UrlLauncher api = new UrlLauncher(ApplicationProvider.getApplicationContext()); + api.setActivity(activity); + boolean enableDomStorage = true; + HashMap headers = new HashMap<>(); + boolean showTitle = true; + + api.openUrlInApp( + "https://flutter.dev", + true, + new Messages.WebViewOptions.Builder() + .setEnableJavaScript(false) + .setEnableDomStorage(enableDomStorage) + .setHeaders(headers) + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(showTitle).build()); + + final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activity).startActivity(intentCaptor.capture(), isNull()); + + assertEquals( + intentCaptor.getValue().getExtras().getInt(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE), + CustomTabsIntent.SHOW_PAGE_TITLE); + } + @Test public void openWebView_throwsForNoCurrentActivity() { UrlLauncher api = new UrlLauncher(ApplicationProvider.getApplicationContext()); @@ -364,7 +459,8 @@ public void openWebView_throwsForNoCurrentActivity() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(new HashMap<>()) - .build())); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build())); assertEquals("NO_ACTIVITY", exception.code); } @@ -388,7 +484,8 @@ public void openWebView_returnsFalse() { .setEnableJavaScript(false) .setEnableDomStorage(false) .setHeaders(new HashMap<>()) - .build()); + .build(), + new Messages.BrowserOptions.Builder().setShowTitle(false).build()); assertFalse(result); } diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index e3a13c571f7..0d99e391616 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - url_launcher_platform_interface: ^2.2.0 + url_launcher_platform_interface: ^2.3.1 dev_dependencies: flutter_test: diff --git a/packages/url_launcher/url_launcher_android/lib/src/messages.g.dart b/packages/url_launcher/url_launcher_android/lib/src/messages.g.dart index 9d6ce26ed85..546f969c880 100644 --- a/packages/url_launcher/url_launcher_android/lib/src/messages.g.dart +++ b/packages/url_launcher/url_launcher_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.6), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import @@ -43,13 +43,37 @@ class WebViewOptions { } } +class BrowserOptions { + BrowserOptions({ + required this.showTitle, + }); + + bool showTitle; + + Object encode() { + return [ + showTitle, + ]; + } + + static BrowserOptions decode(Object result) { + result as List; + return BrowserOptions( + showTitle: result[0]! as bool, + ); + } +} + class _UrlLauncherApiCodec extends StandardMessageCodec { const _UrlLauncherApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is WebViewOptions) { + if (value is BrowserOptions) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is WebViewOptions) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -59,6 +83,8 @@ class _UrlLauncherApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return BrowserOptions.decode(readValue(buffer)!); + case 129: return WebViewOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -135,17 +161,23 @@ class UrlLauncherApi { } } - /// Opens the URL in an in-app Custom Tab or WebView, returning true if it - /// opens successfully. - Future openUrlInApp(String arg_url, bool arg_allowCustomTab, - WebViewOptions arg_options) async { + /// Opens the URL in an in-app WebView, returning true if it opens + /// successfully. + Future openUrlInApp( + String arg_url, + bool arg_allowCustomTab, + WebViewOptions arg_webViewOptions, + BrowserOptions arg_browserOptions) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.url_launcher_android.UrlLauncherApi.openUrlInApp', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_url, arg_allowCustomTab, arg_options]) - as List?; + final List? replyList = await channel.send([ + arg_url, + arg_allowCustomTab, + arg_webViewOptions, + arg_browserOptions + ]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart index 7955abe2a7c..2c6d01c9b31 100644 --- a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart +++ b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart @@ -99,17 +99,23 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { final bool succeeded; if (inApp) { succeeded = await _hostApi.openUrlInApp( - url, - // Prefer custom tabs unless a webview was specifically requested. - options.mode != PreferredLaunchMode.inAppWebView, - WebViewOptions( - enableJavaScript: options.webViewConfiguration.enableJavaScript, - enableDomStorage: options.webViewConfiguration.enableDomStorage, - headers: options.webViewConfiguration.headers)); + url, + // Prefer custom tabs unless a webview was specifically requested. + options.mode != PreferredLaunchMode.inAppWebView, + WebViewOptions( + enableJavaScript: options.webViewConfiguration.enableJavaScript, + enableDomStorage: options.webViewConfiguration.enableDomStorage, + headers: options.webViewConfiguration.headers, + ), + BrowserOptions( + showTitle: options.browserConfiguration.showTitle, + ), + ); } else { succeeded = await _hostApi.launchUrl(url, options.webViewConfiguration.headers); } + // TODO(stuartmorgan): Remove this special handling as part of a // breaking change to rework failure handling across all platform. The // current behavior is backwards compatible with the previous Java error. @@ -118,6 +124,7 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { code: 'ACTIVITY_NOT_FOUND', message: 'No Activity found to handle intent { $url }'); } + return succeeded; } diff --git a/packages/url_launcher/url_launcher_android/pigeons/messages.dart b/packages/url_launcher/url_launcher_android/pigeons/messages.dart index d7184413409..6f39272c4f7 100644 --- a/packages/url_launcher/url_launcher_android/pigeons/messages.dart +++ b/packages/url_launcher/url_launcher_android/pigeons/messages.dart @@ -13,10 +13,12 @@ import 'package:pigeon/pigeon.dart'; /// Configuration options for an in-app WebView. class WebViewOptions { - const WebViewOptions( - {required this.enableJavaScript, - required this.enableDomStorage, - this.headers = const {}}); + const WebViewOptions({ + required this.enableJavaScript, + required this.enableDomStorage, + this.headers = const {}, + }); + final bool enableJavaScript; final bool enableDomStorage; // TODO(stuartmorgan): Declare these as non-nullable generics once @@ -25,6 +27,14 @@ class WebViewOptions { final Map headers; } +/// Configuration options for in-app browser views. +class BrowserOptions { + BrowserOptions({required this.showTitle}); + + /// Whether or not to show the webpage title. + final bool showTitle; +} + @HostApi() abstract class UrlLauncherApi { /// Returns true if the URL can definitely be launched. @@ -35,7 +45,12 @@ abstract class UrlLauncherApi { /// Opens the URL in an in-app Custom Tab or WebView, returning true if it /// opens successfully. - bool openUrlInApp(String url, bool allowCustomTab, WebViewOptions options); + bool openUrlInApp( + String url, + bool allowCustomTab, + WebViewOptions webViewOptions, + BrowserOptions browserOptions, + ); bool supportsCustomTabs(); diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 27ecdfcec8e..111318fa681 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.2.3 +version: 6.3.0 environment: sdk: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - url_launcher_platform_interface: ^2.2.0 + url_launcher_platform_interface: ^2.3.1 dev_dependencies: flutter_test: diff --git a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart index 2b331cbd027..7b469246a5c 100644 --- a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart +++ b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart @@ -64,6 +64,7 @@ void main() { universalLinksOnly: false, headers: const {}, ); + expect(launched, true); expect(api.usedWebView, false); expect(api.passedWebViewOptions?.headers, isEmpty); @@ -166,6 +167,20 @@ void main() { expect(api.passedWebViewOptions?.enableDomStorage, true); }); + test('passes showTitle to webview', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(api: api); + await launcher.launchUrl( + 'http://example.com/', + const LaunchOptions( + browserConfiguration: InAppBrowserConfiguration( + showTitle: true, + ), + ), + ); + + expect(api.passedBrowserOptions?.showTitle, true); + }); + test('passes through no-activity exception', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(api: api); await expectLater( @@ -400,6 +415,7 @@ void main() { class _FakeUrlLauncherApi implements UrlLauncherApi { bool hasCustomTabSupport = true; WebViewOptions? passedWebViewOptions; + BrowserOptions? passedBrowserOptions; bool? usedWebView; bool? allowedCustomTab; bool? closed; @@ -415,7 +431,11 @@ class _FakeUrlLauncherApi implements UrlLauncherApi { @override Future launchUrl(String url, Map headers) async { passedWebViewOptions = WebViewOptions( - enableJavaScript: false, enableDomStorage: false, headers: headers); + enableJavaScript: false, + enableDomStorage: false, + headers: headers, + ); + usedWebView = false; return _launch(url); } @@ -427,8 +447,13 @@ class _FakeUrlLauncherApi implements UrlLauncherApi { @override Future openUrlInApp( - String url, bool allowCustomTab, WebViewOptions options) async { - passedWebViewOptions = options; + String url, + bool allowCustomTab, + WebViewOptions webViewOptions, + BrowserOptions browserOptions, + ) async { + passedWebViewOptions = webViewOptions; + passedBrowserOptions = browserOptions; usedWebView = true; allowedCustomTab = allowCustomTab; return _launch(url);