diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 995d64c441dd..ab636c60137a 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.7.8 + +* Fixed a situation where an app would crash if the url_launcher’s `launch` method can’t find an app to open the provided url. It will now throw a clear Dart PlatformException. + ## 5.7.7 * Introduce the Link widget with an implementation for native platforms. diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java index 0b90dfaf32bc..2ca6b7ce3fde 100644 --- a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java +++ b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java @@ -92,6 +92,11 @@ private void onLaunch(MethodCall call, Result result, String url) { if (launchStatus == LaunchStatus.NO_ACTIVITY) { result.error("NO_ACTIVITY", "Launching a URL requires a foreground activity.", null); + } else if (launchStatus == LaunchStatus.ACTIVITY_NOT_FOUND) { + result.error( + "ACTIVITY_NOT_FOUND", + String.format("No Activity found to handle intent { %s }", url), + null); } else { result.success(true); } diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java index 40f2a51f1db2..44ecc337c43b 100644 --- a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java +++ b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java @@ -1,6 +1,7 @@ package io.flutter.plugins.urllauncher; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -48,7 +49,8 @@ boolean canLaunch(String url) { * @param enableJavaScript Only used if {@param useWebView} is true. Enables JS in the WebView. * @param enableDomStorage Only used if {@param useWebView} is true. Enables DOM storage in the * @return {@link LaunchStatus#NO_ACTIVITY} if there's no available {@code applicationContext}. - * {@link LaunchStatus#OK} otherwise. + * {@link LaunchStatus#ACTIVITY_NOT_FOUND} if there's no activity found to handle {@code + * launchIntent}. {@link LaunchStatus#OK} otherwise. */ LaunchStatus launch( String url, @@ -72,7 +74,12 @@ LaunchStatus launch( .putExtra(Browser.EXTRA_HEADERS, headersBundle); } - activity.startActivity(launchIntent); + try { + activity.startActivity(launchIntent); + } catch (ActivityNotFoundException e) { + return LaunchStatus.ACTIVITY_NOT_FOUND; + } + return LaunchStatus.OK; } @@ -87,5 +94,7 @@ enum LaunchStatus { OK, /** No activity was found to launch. */ NO_ACTIVITY, + /** No Activity found that can handle given intent. */ + ACTIVITY_NOT_FOUND, } } diff --git a/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java b/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java index 63ce46f6d0cb..e759dedd5f75 100644 --- a/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java +++ b/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.Bundle; import androidx.test.core.app.ApplicationProvider; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler; @@ -105,6 +106,95 @@ public void onMethodCall_canLaunchReturnsFalse() { verify(result, times(1)).success(false); } + @Test + public void onMethodCall_launchReturnsNoActivityError() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.NO_ACTIVITY); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)) + .error("NO_ACTIVITY", "Launching a URL requires a foreground activity.", null); + } + + @Test + public void onMethodCall_launchReturnsActivityNotFoundError() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.ACTIVITY_NOT_FOUND); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)) + .error( + "ACTIVITY_NOT_FOUND", + String.format("No Activity found to handle intent { %s }", url), + null); + } + + @Test + public void onMethodCall_launchReturnsTrue() { + // Setup mock objects + urlLauncher = mock(UrlLauncher.class); + Result result = mock(Result.class); + // Setup expected values + String url = "foo"; + boolean useWebView = false; + boolean enableJavaScript = false; + boolean enableDomStorage = false; + // Setup arguments map send on the method channel + Map args = new HashMap<>(); + args.put("url", url); + args.put("useWebView", useWebView); + args.put("enableJavaScript", enableJavaScript); + args.put("enableDomStorage", enableDomStorage); + args.put("headers", new HashMap<>()); + // Mock the launch method on the urlLauncher class + when(urlLauncher.launch( + eq(url), any(Bundle.class), eq(useWebView), eq(enableJavaScript), eq(enableDomStorage))) + .thenReturn(UrlLauncher.LaunchStatus.OK); + // Act by calling the "launch" method on the method channel + methodCallHandler = new MethodCallHandlerImpl(urlLauncher); + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + // Verify the results and assert + verify(result, times(1)).success(true); + } + @Test public void onMethodCall_closeWebView() { urlLauncher = mock(UrlLauncher.class); diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index cf837b24f29e..7ffc20c6e5fe 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher -version: 5.7.7 +version: 5.7.8 flutter: plugin: