diff --git a/packages/android_intent/CHANGELOG.md b/packages/android_intent/CHANGELOG.md index 3d16c1422441..47f1917e3e8a 100644 --- a/packages/android_intent/CHANGELOG.md +++ b/packages/android_intent/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.6 + +* Marks the `action` parameter as optional +* Adds an assertion to ensure the intent receives an action, component or both. + ## 0.3.5+1 * Make the pedantic dev_dependency explicit. diff --git a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/IntentSender.java b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/IntentSender.java index aac9226b9465..5aa7919699da 100644 --- a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/IntentSender.java +++ b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/IntentSender.java @@ -42,7 +42,7 @@ public IntentSender(@Nullable Activity activity, @Nullable Context applicationCo * back to {@code applicationContext} and adds {@link Intent#FLAG_ACTIVITY_NEW_TASK} to the intent * before launching it. * - * @param action the Intent action, such as {@code ACTION_VIEW}. + * @param action the Intent action, such as {@code ACTION_VIEW} if non-null. * @param flags forwarded to {@link Intent#addFlags(int)} if non-null. * @param category forwarded to {@link Intent#addCategory(String)} if non-null. * @param data forwarded to {@link Intent#setData(Uri)} if non-null and 'type' parameter is null. @@ -57,7 +57,7 @@ public IntentSender(@Nullable Activity activity, @Nullable Context applicationCo * Intent#setDataAndType(Uri, String)} */ void send( - String action, + @Nullable String action, @Nullable Integer flags, @Nullable String category, @Nullable Uri data, @@ -70,8 +70,11 @@ void send( return; } - Intent intent = new Intent(action); + Intent intent = new Intent(); + if (action != null) { + intent.setAction(action); + } if (flags != null) { intent.addFlags(flags); } diff --git a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/MethodCallHandlerImpl.java b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/MethodCallHandlerImpl.java index a77d181015de..09846d5e51ef 100644 --- a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/MethodCallHandlerImpl.java +++ b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/MethodCallHandlerImpl.java @@ -91,6 +91,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { } private static String convertAction(String action) { + if (action == null) { + return null; + } + switch (action) { case "action_view": return Intent.ACTION_VIEW; diff --git a/packages/android_intent/android/src/test/java/io/flutter/plugins/androidintent/MethodCallHandlerImplTest.java b/packages/android_intent/android/src/test/java/io/flutter/plugins/androidintent/MethodCallHandlerImplTest.java index 4ce7ddc3718e..cf0a28e822d4 100644 --- a/packages/android_intent/android/src/test/java/io/flutter/plugins/androidintent/MethodCallHandlerImplTest.java +++ b/packages/android_intent/android/src/test/java/io/flutter/plugins/androidintent/MethodCallHandlerImplTest.java @@ -217,8 +217,34 @@ public void onMethodCall_setsComponentName() { Intent intent = shadowOf((Application) context).getNextStartedActivity(); assertNotNull(intent); assertNotNull(intent.getComponent()); - assertEquals(expectedComponent.getPackageName(), intent.getPackage()); - assertEquals(expectedComponent.flattenToString(), intent.getComponent().flattenToString()); + assertEquals("foo", intent.getAction()); + assertEquals("io.flutter.plugins.androidintent", intent.getPackage()); + assertEquals( + "io.flutter.plugins.androidintent/MainActivity", intent.getComponent().flattenToString()); + } + + @Test + public void onMethodCall_setsOnlyComponentName() { + sender.setApplicationContext(context); + Map args = new HashMap<>(); + ComponentName expectedComponent = + new ComponentName("io.flutter.plugins.androidintent", "MainActivity"); + args.put("package", expectedComponent.getPackageName()); + args.put("componentName", expectedComponent.getClassName()); + Result result = mock(Result.class); + ShadowPackageManager shadowPm = + shadowOf(ApplicationProvider.getApplicationContext().getPackageManager()); + shadowPm.addActivityIfNotPresent(expectedComponent); + + methodCallHandler.onMethodCall(new MethodCall("launch", args), result); + + verify(result, times(1)).success(null); + Intent intent = shadowOf((Application) context).getNextStartedActivity(); + assertNotNull(intent); + assertNotNull(intent.getComponent()); + assertEquals("io.flutter.plugins.androidintent", intent.getPackage()); + assertEquals( + "io.flutter.plugins.androidintent/MainActivity", intent.getComponent().flattenToString()); } @Test diff --git a/packages/android_intent/lib/android_intent.dart b/packages/android_intent/lib/android_intent.dart index eb2f486408cc..26e270a3a79b 100644 --- a/packages/android_intent/lib/android_intent.dart +++ b/packages/android_intent/lib/android_intent.dart @@ -29,7 +29,7 @@ class AndroidIntent { /// If not null, then [package] but also be provided. /// [type] refers to the type of the intent, can be null. const AndroidIntent({ - @required this.action, + this.action, this.flags, this.category, this.data, @@ -38,7 +38,8 @@ class AndroidIntent { this.componentName, Platform platform, this.type, - }) : assert(action != null), + }) : assert(action != null || componentName != null, + 'action or component (or both) must be specified'), _channel = const MethodChannel(_kChannelName), _platform = platform ?? const LocalPlatform(); @@ -46,9 +47,9 @@ class AndroidIntent { /// app code, it may break without warning. @visibleForTesting AndroidIntent.private({ - @required this.action, @required Platform platform, @required MethodChannel channel, + this.action, this.flags, this.category, this.data, @@ -56,7 +57,9 @@ class AndroidIntent { this.package, this.componentName, this.type, - }) : _channel = channel, + }) : assert(action != null || componentName != null, + 'action or component (or both) must be specified'), + _channel = channel, _platform = platform; /// This is the general verb that the intent should attempt to do. This @@ -131,7 +134,10 @@ class AndroidIntent { if (!_platform.isAndroid) { return; } - final Map args = {'action': action}; + final Map args = {}; + if (action != null) { + args['action'] = action; + } if (flags != null) { args['flags'] = convertFlags(flags); } diff --git a/packages/android_intent/pubspec.yaml b/packages/android_intent/pubspec.yaml index c6e38bc0d93b..2faf45e915f6 100644 --- a/packages/android_intent/pubspec.yaml +++ b/packages/android_intent/pubspec.yaml @@ -1,7 +1,7 @@ name: android_intent description: Flutter plugin for launching Android Intents. Not supported on iOS. homepage: https://github.com/flutter/plugins/tree/master/packages/android_intent -version: 0.3.5+1 +version: 0.3.6 flutter: plugin: diff --git a/packages/android_intent/test/android_intent_test.dart b/packages/android_intent/test/android_intent_test.dart index e36a8b7827c9..60cf3b58a507 100644 --- a/packages/android_intent/test/android_intent_test.dart +++ b/packages/android_intent/test/android_intent_test.dart @@ -15,6 +15,7 @@ void main() { setUp(() { mockChannel = MockMethodChannel(); }); + group('AndroidIntent', () { test('pass right params', () async { androidIntent = AndroidIntent.private( @@ -32,26 +33,53 @@ void main() { 'type': 'video/*', })); }); - test('pass null value to action param', () async { + + test('raises error if neither an action nor a component is provided', () { + try { + androidIntent = AndroidIntent(data: 'https://flutter.io'); + fail('should raise an AssertionError'); + } on AssertionError catch (e) { + expect(e.message, 'action or component (or both) must be specified'); + } catch (e) { + fail('should raise an AssertionError'); + } + }); + test('can send Intent with an action and no component', () async { androidIntent = AndroidIntent.private( - action: null, - channel: mockChannel, - platform: FakePlatform(operatingSystem: 'android')); + action: 'action_view', + channel: mockChannel, + platform: FakePlatform(operatingSystem: 'android'), + ); await androidIntent.launch(); verify(mockChannel.invokeMethod('launch', { - 'action': null, + 'action': 'action_view', + })); + }); + + test('can send Intent with a component and no action', () async { + androidIntent = AndroidIntent.private( + package: 'packageName', + componentName: 'componentName', + channel: mockChannel, + platform: FakePlatform(operatingSystem: 'android'), + ); + await androidIntent.launch(); + verify(mockChannel.invokeMethod('launch', { + 'package': 'packageName', + 'componentName': 'componentName', })); }); test('call in ios platform', () async { androidIntent = AndroidIntent.private( - action: null, + action: 'action_view', channel: mockChannel, platform: FakePlatform(operatingSystem: 'ios')); await androidIntent.launch(); verifyZeroInteractions(mockChannel); }); }); + group('convertFlags ', () { androidIntent = const AndroidIntent( action: 'action_view',