[android_intent] add flags option (#2000)
Added "flags" option to call intent.addFlags(int) in native.
diff --git a/packages/android_intent/CHANGELOG.md b/packages/android_intent/CHANGELOG.md
index ea1b8f8..7a818f3 100644
--- a/packages/android_intent/CHANGELOG.md
+++ b/packages/android_intent/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.3
+
+* Added "flags" option to call intent.addFlags(int) in native.
+
## 0.3.2
* Added "action_location_source_settings" action to start Location Settings Activity.
diff --git a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
index 9c924d6..a66116c 100644
--- a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
+++ b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
@@ -126,6 +126,9 @@
if (mRegistrar.activity() == null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ if (call.argument("flag") != null) {
+ intent.addFlags((Integer) call.argument("flags"));
+ }
if (call.argument("category") != null) {
intent.addCategory((String) call.argument("category"));
}
diff --git a/packages/android_intent/example/lib/main.dart b/packages/android_intent/example/lib/main.dart
index c94ffe5..becf3d6 100644
--- a/packages/android_intent/example/lib/main.dart
+++ b/packages/android_intent/example/lib/main.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:android_intent/android_intent.dart';
+import 'package:android_intent/flag.dart';
import 'package:flutter/material.dart';
import 'package:platform/platform.dart';
@@ -117,6 +118,15 @@
intent.launch();
}
+ void _startActivityInNewTask() {
+ final AndroidIntent intent = AndroidIntent(
+ action: 'action_view',
+ data: Uri.encodeFull('https://flutter.io'),
+ flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
+ );
+ intent.launch();
+ }
+
void _testExplicitIntentFallback() {
final AndroidIntent intent = AndroidIntent(
action: 'action_view',
@@ -163,6 +173,10 @@
onPressed: _openLinkInGoogleChrome,
),
RaisedButton(
+ child: const Text('Tap here to start activity in new task.'),
+ onPressed: _startActivityInNewTask,
+ ),
+ RaisedButton(
child: const Text(
'Tap here to test explicit intent fallback to implicit.'),
onPressed: _testExplicitIntentFallback,
diff --git a/packages/android_intent/lib/android_intent.dart b/packages/android_intent/lib/android_intent.dart
index 5e00b30..9c036cf 100644
--- a/packages/android_intent/lib/android_intent.dart
+++ b/packages/android_intent/lib/android_intent.dart
@@ -14,6 +14,7 @@
class AndroidIntent {
/// Builds an Android intent with the following parameters
/// [action] refers to the action parameter of the intent.
+ /// [flags] is the list of int that will be converted to native flags.
/// [category] refers to the category of the intent, can be null.
/// [data] refers to the string format of the URI that will be passed to
/// intent.
@@ -24,6 +25,7 @@
/// If not null, then [package] but also be provided.
const AndroidIntent({
@required this.action,
+ this.flags,
this.category,
this.data,
this.arguments,
@@ -34,7 +36,22 @@
_channel = const MethodChannel(kChannelName),
_platform = platform ?? const LocalPlatform();
+ @visibleForTesting
+ AndroidIntent.private({
+ @required this.action,
+ @required Platform platform,
+ @required MethodChannel channel,
+ this.flags,
+ this.category,
+ this.data,
+ this.arguments,
+ this.package,
+ this.componentName,
+ }) : _channel = channel,
+ _platform = platform;
+
final String action;
+ final List<int> flags;
final String category;
final String data;
final Map<String, dynamic> arguments;
@@ -43,13 +60,34 @@
final MethodChannel _channel;
final Platform _platform;
+ bool _isPowerOfTwo(int x) {
+ /* First x in the below expression is for the case when x is 0 */
+ return x != 0 && ((x & (x - 1)) == 0);
+ }
+
+ @visibleForTesting
+ int convertFlags(List<int> flags) {
+ int finalValue = 0;
+ for (int i = 0; i < flags.length; i++) {
+ if (!_isPowerOfTwo(flags[i])) {
+ throw ArgumentError.value(flags[i], 'flag\'s value must be power of 2');
+ }
+ finalValue |= flags[i];
+ }
+ return finalValue;
+ }
+
/// Launch the intent.
///
- /// This works only on Android platforms. Please guard the call so that your
- /// iOS app does not crash. Checked mode will throw an assert exception.
+ /// This works only on Android platforms.
Future<void> launch() async {
- assert(_platform.isAndroid);
+ if (!_platform.isAndroid) {
+ return;
+ }
final Map<String, dynamic> args = <String, dynamic>{'action': action};
+ if (flags != null) {
+ args['flags'] = convertFlags(flags);
+ }
if (category != null) {
args['category'] = category;
}
diff --git a/packages/android_intent/lib/flag.dart b/packages/android_intent/lib/flag.dart
new file mode 100644
index 0000000..b4e6ed1
--- /dev/null
+++ b/packages/android_intent/lib/flag.dart
@@ -0,0 +1,37 @@
+// flag values from https://developer.android.com/reference/android/content/Intent.html
+class Flag {
+ static const int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304;
+ static const int FLAG_ACTIVITY_CLEAR_TASK = 32768;
+ static const int FLAG_ACTIVITY_CLEAR_TOP = 67108864;
+ static const int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288;
+ static const int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608;
+ static const int FLAG_ACTIVITY_FORWARD_RESULT = 33554432;
+ static const int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576;
+ static const int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096;
+ static const int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048;
+ static const int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728;
+ static const int FLAG_ACTIVITY_NEW_DOCUMENT = 524288;
+ static const int FLAG_ACTIVITY_NEW_TASK = 268435456;
+ static const int FLAG_ACTIVITY_NO_ANIMATION = 65536;
+ static const int FLAG_ACTIVITY_NO_HISTORY = 1073741824;
+ static const int FLAG_ACTIVITY_NO_USER_ACTION = 262144;
+ static const int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 16777216;
+ static const int FLAG_ACTIVITY_REORDER_TO_FRONT = 131072;
+ static const int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 2097152;
+ static const int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 8192;
+ static const int FLAG_ACTIVITY_SINGLE_TOP = 536870912;
+ static const int FLAG_ACTIVITY_TASK_ON_HOME = 16384;
+ static const int FLAG_DEBUG_LOG_RESOLUTION = 8;
+ static const int FLAG_EXCLUDE_STOPPED_PACKAGES = 16;
+ static const int FLAG_FROM_BACKGROUND = 4;
+ static const int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64;
+ static const int FLAG_GRANT_PREFIX_URI_PERMISSION = 128;
+ static const int FLAG_GRANT_READ_URI_PERMISSION = 1;
+ static const int FLAG_GRANT_WRITE_URI_PERMISSION = 2;
+ static const int FLAG_INCLUDE_STOPPED_PACKAGES = 32;
+ static const int FLAG_RECEIVER_FOREGROUND = 268435456;
+ static const int FLAG_RECEIVER_NO_ABORT = 134217728;
+ static const int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824;
+ static const int FLAG_RECEIVER_REPLACE_PENDING = 536870912;
+ static const int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 2097152;
+}
diff --git a/packages/android_intent/pubspec.yaml b/packages/android_intent/pubspec.yaml
index cf205a6..11cbc31 100644
--- a/packages/android_intent/pubspec.yaml
+++ b/packages/android_intent/pubspec.yaml
@@ -2,7 +2,7 @@
description: Flutter plugin for launching Android Intents. Not supported on iOS.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/android_intent
-version: 0.3.2
+version: 0.3.3
flutter:
plugin:
@@ -15,7 +15,11 @@
sdk: flutter
platform: ^2.0.0
meta: ^1.0.5
-
+dev_dependencies:
+ test: ^1.3.0
+ mockito: ^3.0.0
+ flutter_test:
+ sdk: flutter
environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
flutter: ">=1.2.0 <2.0.0"
diff --git a/packages/android_intent/test/android_intent_test.dart b/packages/android_intent/test/android_intent_test.dart
new file mode 100644
index 0000000..b13438b
--- /dev/null
+++ b/packages/android_intent/test/android_intent_test.dart
@@ -0,0 +1,85 @@
+// Copyright 2019 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.
+
+import 'package:android_intent/flag.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:android_intent/android_intent.dart';
+import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
+
+void main() {
+ AndroidIntent androidIntent;
+ MockMethodChannel mockChannel;
+ setUp(() {
+ mockChannel = MockMethodChannel();
+ });
+ group('AndroidIntent', () {
+ test('pass right params', () async {
+ androidIntent = AndroidIntent.private(
+ action: 'action_view',
+ data: Uri.encodeFull('https://flutter.io'),
+ flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
+ channel: mockChannel,
+ platform: FakePlatform(operatingSystem: 'android'));
+ androidIntent.launch();
+ verify(mockChannel.invokeMethod<void>('launch', <String, Object>{
+ 'action': 'action_view',
+ 'data': Uri.encodeFull('https://flutter.io'),
+ 'flags': androidIntent.convertFlags(<int>[Flag.FLAG_ACTIVITY_NEW_TASK]),
+ }));
+ });
+ test('pass null value to action param', () async {
+ androidIntent = AndroidIntent.private(
+ action: null,
+ channel: mockChannel,
+ platform: FakePlatform(operatingSystem: 'android'));
+ androidIntent.launch();
+ verify(mockChannel.invokeMethod<void>('launch', <String, Object>{
+ 'action': null,
+ }));
+ });
+
+ test('call in ios platform', () async {
+ androidIntent = AndroidIntent.private(
+ action: null,
+ channel: mockChannel,
+ platform: FakePlatform(operatingSystem: 'ios'));
+ androidIntent.launch();
+ verifyZeroInteractions(mockChannel);
+ });
+ });
+ group('convertFlags ', () {
+ androidIntent = const AndroidIntent(
+ action: 'action_view',
+ );
+ test('add filled flag list', () async {
+ final List<int> flags = <int>[];
+ flags.add(Flag.FLAG_ACTIVITY_NEW_TASK);
+ flags.add(Flag.FLAG_ACTIVITY_NEW_DOCUMENT);
+ expect(
+ androidIntent.convertFlags(flags),
+ 268959744,
+ );
+ });
+ test('add flags whose values are not power of 2', () async {
+ final List<int> flags = <int>[];
+ flags.add(100);
+ flags.add(10);
+ expect(
+ () => androidIntent.convertFlags(flags),
+ throwsArgumentError,
+ );
+ });
+ test('add empty flag list', () async {
+ final List<int> flags = <int>[];
+ expect(
+ androidIntent.convertFlags(flags),
+ 0,
+ );
+ });
+ });
+}
+
+class MockMethodChannel extends Mock implements MethodChannel {}