From 4d6fa3a31713d78484a2db1fb46bb5768d7f88ac Mon Sep 17 00:00:00 2001 From: Sempakonka <47318358+Sempakonka@users.noreply.github.com> Date: Tue, 6 Oct 2020 12:11:56 +0200 Subject: [PATCH 1/8] Added a new `LaunchStatus` to handle the ActivityNotFound exception that `activity.startActivity` can throw. --- packages/url_launcher/url_launcher/CHANGELOG.md | 4 ++++ .../plugins/urllauncher/MethodCallHandlerImpl.java | 6 +++++- .../io/flutter/plugins/urllauncher/UrlLauncher.java | 11 ++++++++++- packages/url_launcher/url_launcher/pubspec.yaml | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 995d64c441dd..5ed7166ad7b4 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.7.8 + +* Added a new `LaunchStatus` to handle the ActivityNotFound exception that `activity.startActivity` can throw. + ## 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..f22b03953b7b 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,7 +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 { + } + 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..c51d1ee5ca6f 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 @@ -48,6 +48,7 @@ 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#ACTIVITY_NOT_FOUND} if there's no activity found to handle {@code launchIntent}. * {@link LaunchStatus#OK} otherwise. */ LaunchStatus launch( @@ -72,7 +73,13 @@ 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 to handle intent. */ + ACTIVITY_NOT_FOUND, } } 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: From 7dddf0f71757610d609773e94ad02a278d017770 Mon Sep 17 00:00:00 2001 From: Sempakonka <47318358+Sempakonka@users.noreply.github.com> Date: Tue, 6 Oct 2020 12:18:17 +0200 Subject: [PATCH 2/8] Improved documentation --- .../main/java/io/flutter/plugins/urllauncher/UrlLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c51d1ee5ca6f..5f10e91740f0 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 @@ -94,7 +94,7 @@ enum LaunchStatus { OK, /** No activity was found to launch. */ NO_ACTIVITY, - /** No Activity found to handle intent. */ + /** No Activity found that can handle given intent. */ ACTIVITY_NOT_FOUND, } } From 0b930f217779724141132038481a73a362138b04 Mon Sep 17 00:00:00 2001 From: Sempakonka <47318358+Sempakonka@users.noreply.github.com> Date: Wed, 7 Oct 2020 11:43:43 +0200 Subject: [PATCH 3/8] Added needed library --- .../main/java/io/flutter/plugins/urllauncher/UrlLauncher.java | 1 + 1 file changed, 1 insertion(+) 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 5f10e91740f0..5722eddf29a6 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; From bc3037de37a85008a4808b3a40bea5401be17094 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 8 Oct 2020 16:37:35 +0200 Subject: [PATCH 4/8] Fix formatting --- .../plugins/urllauncher/MethodCallHandlerImpl.java | 11 ++++++----- .../io/flutter/plugins/urllauncher/UrlLauncher.java | 7 +++---- 2 files changed, 9 insertions(+), 9 deletions(-) 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 f22b03953b7b..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,11 +92,12 @@ 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 { + } 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 5722eddf29a6..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 @@ -49,8 +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#ACTIVITY_NOT_FOUND} if there's no activity found to handle {@code launchIntent}. - * {@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, @@ -76,8 +76,7 @@ LaunchStatus launch( try { activity.startActivity(launchIntent); - } - catch (ActivityNotFoundException e){ + } catch (ActivityNotFoundException e) { return LaunchStatus.ACTIVITY_NOT_FOUND; } From 90ac3723758d84e17b6d84329addd1bbbdf29bf5 Mon Sep 17 00:00:00 2001 From: Sempakonka <47318358+Sempakonka@users.noreply.github.com> Date: Thu, 15 Oct 2020 15:18:06 +0200 Subject: [PATCH 5/8] Added tests, improved CHANGELOG.md --- .../url_launcher/url_launcher/CHANGELOG.md | 2 +- .../MethodCallHandlerImplTest.java | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 5ed7166ad7b4..ab636c60137a 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,6 @@ ## 5.7.8 -* Added a new `LaunchStatus` to handle the ActivityNotFound exception that `activity.startActivity` can throw. +* 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 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..5d7ded84620e 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 @@ -1,5 +1,6 @@ package io.flutter.plugins.urllauncher; +import android.os.Bundle; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -105,6 +106,115 @@ 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); From 37cc11c8b1d802ad34a72cef3ec4fac0a2d287ce Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 15 Oct 2020 16:03:27 +0200 Subject: [PATCH 6/8] Fix formatting issues --- .../MethodCallHandlerImplTest.java | 52 ++++++------------- 1 file changed, 16 insertions(+), 36 deletions(-) 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 5d7ded84620e..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 @@ -1,6 +1,5 @@ package io.flutter.plugins.urllauncher; -import android.os.Bundle; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -9,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; @@ -124,23 +124,15 @@ public void onMethodCall_launchReturnsNoActivityError() { 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); + 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); + .error("NO_ACTIVITY", "Launching a URL requires a foreground activity.", null); } @Test @@ -161,23 +153,18 @@ public void onMethodCall_launchReturnsActivityNotFoundError() { 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); + 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); + .error( + "ACTIVITY_NOT_FOUND", + String.format("No Activity found to handle intent { %s }", url), + null); } @Test @@ -198,23 +185,16 @@ public void onMethodCall_launchReturnsTrue() { 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); + 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); + verify(result, times(1)).success(true); } - @Test public void onMethodCall_closeWebView() { urlLauncher = mock(UrlLauncher.class); From 382ceb84370fb376f5bbfffe61796370cab59e32 Mon Sep 17 00:00:00 2001 From: Sempakonka <47318358+Sempakonka@users.noreply.github.com> Date: Thu, 15 Oct 2020 15:18:06 +0200 Subject: [PATCH 7/8] Added tests, improved CHANGELOG.md --- .../flutter/plugins/urllauncher/MethodCallHandlerImplTest.java | 1 + 1 file changed, 1 insertion(+) 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 e759dedd5f75..a73e0679439d 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 @@ -1,5 +1,6 @@ package io.flutter.plugins.urllauncher; +import android.os.Bundle; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; From bf3d5cf0fc6cfaabbb99de35e7591824c7686250 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 21 Oct 2020 10:16:15 +0200 Subject: [PATCH 8/8] Fix formatting issues --- .../flutter/plugins/urllauncher/MethodCallHandlerImplTest.java | 1 - 1 file changed, 1 deletion(-) 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 a73e0679439d..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 @@ -1,6 +1,5 @@ package io.flutter.plugins.urllauncher; -import android.os.Bundle; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock;