Skip to content

Commit 9563255

Browse files
thanhdatvomormih
authored andcommitted
[android_intent] add flags option (flutter#2000)
Added "flags" option to call intent.addFlags(int) in native.
1 parent 5bec448 commit 9563255

7 files changed

Lines changed: 190 additions & 5 deletions

File tree

packages/android_intent/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.3
2+
3+
* Added "flags" option to call intent.addFlags(int) in native.
4+
15
## 0.3.2
26

37
* Added "action_location_source_settings" action to start Location Settings Activity.

packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ public void onMethodCall(MethodCall call, Result result) {
126126
if (mRegistrar.activity() == null) {
127127
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
128128
}
129+
if (call.argument("flag") != null) {
130+
intent.addFlags((Integer) call.argument("flags"));
131+
}
129132
if (call.argument("category") != null) {
130133
intent.addCategory((String) call.argument("category"));
131134
}

packages/android_intent/example/lib/main.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'package:android_intent/android_intent.dart';
6+
import 'package:android_intent/flag.dart';
67
import 'package:flutter/material.dart';
78
import 'package:platform/platform.dart';
89

@@ -117,6 +118,15 @@ class ExplicitIntentsWidget extends StatelessWidget {
117118
intent.launch();
118119
}
119120

121+
void _startActivityInNewTask() {
122+
final AndroidIntent intent = AndroidIntent(
123+
action: 'action_view',
124+
data: Uri.encodeFull('https://flutter.io'),
125+
flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
126+
);
127+
intent.launch();
128+
}
129+
120130
void _testExplicitIntentFallback() {
121131
final AndroidIntent intent = AndroidIntent(
122132
action: 'action_view',
@@ -162,6 +172,10 @@ class ExplicitIntentsWidget extends StatelessWidget {
162172
child: const Text('Tap here to open link in Google Chrome.'),
163173
onPressed: _openLinkInGoogleChrome,
164174
),
175+
RaisedButton(
176+
child: const Text('Tap here to start activity in new task.'),
177+
onPressed: _startActivityInNewTask,
178+
),
165179
RaisedButton(
166180
child: const Text(
167181
'Tap here to test explicit intent fallback to implicit.'),

packages/android_intent/lib/android_intent.dart

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const String kChannelName = 'plugins.flutter.io/android_intent';
1414
class AndroidIntent {
1515
/// Builds an Android intent with the following parameters
1616
/// [action] refers to the action parameter of the intent.
17+
/// [flags] is the list of int that will be converted to native flags.
1718
/// [category] refers to the category of the intent, can be null.
1819
/// [data] refers to the string format of the URI that will be passed to
1920
/// intent.
@@ -24,6 +25,7 @@ class AndroidIntent {
2425
/// If not null, then [package] but also be provided.
2526
const AndroidIntent({
2627
@required this.action,
28+
this.flags,
2729
this.category,
2830
this.data,
2931
this.arguments,
@@ -34,7 +36,22 @@ class AndroidIntent {
3436
_channel = const MethodChannel(kChannelName),
3537
_platform = platform ?? const LocalPlatform();
3638

39+
@visibleForTesting
40+
AndroidIntent.private({
41+
@required this.action,
42+
@required Platform platform,
43+
@required MethodChannel channel,
44+
this.flags,
45+
this.category,
46+
this.data,
47+
this.arguments,
48+
this.package,
49+
this.componentName,
50+
}) : _channel = channel,
51+
_platform = platform;
52+
3753
final String action;
54+
final List<int> flags;
3855
final String category;
3956
final String data;
4057
final Map<String, dynamic> arguments;
@@ -43,13 +60,34 @@ class AndroidIntent {
4360
final MethodChannel _channel;
4461
final Platform _platform;
4562

63+
bool _isPowerOfTwo(int x) {
64+
/* First x in the below expression is for the case when x is 0 */
65+
return x != 0 && ((x & (x - 1)) == 0);
66+
}
67+
68+
@visibleForTesting
69+
int convertFlags(List<int> flags) {
70+
int finalValue = 0;
71+
for (int i = 0; i < flags.length; i++) {
72+
if (!_isPowerOfTwo(flags[i])) {
73+
throw ArgumentError.value(flags[i], 'flag\'s value must be power of 2');
74+
}
75+
finalValue |= flags[i];
76+
}
77+
return finalValue;
78+
}
79+
4680
/// Launch the intent.
4781
///
48-
/// This works only on Android platforms. Please guard the call so that your
49-
/// iOS app does not crash. Checked mode will throw an assert exception.
82+
/// This works only on Android platforms.
5083
Future<void> launch() async {
51-
assert(_platform.isAndroid);
84+
if (!_platform.isAndroid) {
85+
return;
86+
}
5287
final Map<String, dynamic> args = <String, dynamic>{'action': action};
88+
if (flags != null) {
89+
args['flags'] = convertFlags(flags);
90+
}
5391
if (category != null) {
5492
args['category'] = category;
5593
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// flag values from https://developer.android.com/reference/android/content/Intent.html
2+
class Flag {
3+
static const int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304;
4+
static const int FLAG_ACTIVITY_CLEAR_TASK = 32768;
5+
static const int FLAG_ACTIVITY_CLEAR_TOP = 67108864;
6+
static const int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288;
7+
static const int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608;
8+
static const int FLAG_ACTIVITY_FORWARD_RESULT = 33554432;
9+
static const int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576;
10+
static const int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096;
11+
static const int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048;
12+
static const int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728;
13+
static const int FLAG_ACTIVITY_NEW_DOCUMENT = 524288;
14+
static const int FLAG_ACTIVITY_NEW_TASK = 268435456;
15+
static const int FLAG_ACTIVITY_NO_ANIMATION = 65536;
16+
static const int FLAG_ACTIVITY_NO_HISTORY = 1073741824;
17+
static const int FLAG_ACTIVITY_NO_USER_ACTION = 262144;
18+
static const int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 16777216;
19+
static const int FLAG_ACTIVITY_REORDER_TO_FRONT = 131072;
20+
static const int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 2097152;
21+
static const int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 8192;
22+
static const int FLAG_ACTIVITY_SINGLE_TOP = 536870912;
23+
static const int FLAG_ACTIVITY_TASK_ON_HOME = 16384;
24+
static const int FLAG_DEBUG_LOG_RESOLUTION = 8;
25+
static const int FLAG_EXCLUDE_STOPPED_PACKAGES = 16;
26+
static const int FLAG_FROM_BACKGROUND = 4;
27+
static const int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64;
28+
static const int FLAG_GRANT_PREFIX_URI_PERMISSION = 128;
29+
static const int FLAG_GRANT_READ_URI_PERMISSION = 1;
30+
static const int FLAG_GRANT_WRITE_URI_PERMISSION = 2;
31+
static const int FLAG_INCLUDE_STOPPED_PACKAGES = 32;
32+
static const int FLAG_RECEIVER_FOREGROUND = 268435456;
33+
static const int FLAG_RECEIVER_NO_ABORT = 134217728;
34+
static const int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824;
35+
static const int FLAG_RECEIVER_REPLACE_PENDING = 536870912;
36+
static const int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 2097152;
37+
}

packages/android_intent/pubspec.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: android_intent
22
description: Flutter plugin for launching Android Intents. Not supported on iOS.
33
author: Flutter Team <flutter-dev@googlegroups.com>
44
homepage: https://github.com/flutter/plugins/tree/master/packages/android_intent
5-
version: 0.3.2
5+
version: 0.3.3
66

77
flutter:
88
plugin:
@@ -15,7 +15,11 @@ dependencies:
1515
sdk: flutter
1616
platform: ^2.0.0
1717
meta: ^1.0.5
18-
18+
dev_dependencies:
19+
test: ^1.3.0
20+
mockito: ^3.0.0
21+
flutter_test:
22+
sdk: flutter
1923
environment:
2024
sdk: ">=2.0.0-dev.28.0 <3.0.0"
2125
flutter: ">=1.2.0 <2.0.0"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:android_intent/flag.dart';
6+
import 'package:flutter/services.dart';
7+
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:android_intent/android_intent.dart';
9+
import 'package:mockito/mockito.dart';
10+
import 'package:platform/platform.dart';
11+
12+
void main() {
13+
AndroidIntent androidIntent;
14+
MockMethodChannel mockChannel;
15+
setUp(() {
16+
mockChannel = MockMethodChannel();
17+
});
18+
group('AndroidIntent', () {
19+
test('pass right params', () async {
20+
androidIntent = AndroidIntent.private(
21+
action: 'action_view',
22+
data: Uri.encodeFull('https://flutter.io'),
23+
flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
24+
channel: mockChannel,
25+
platform: FakePlatform(operatingSystem: 'android'));
26+
androidIntent.launch();
27+
verify(mockChannel.invokeMethod<void>('launch', <String, Object>{
28+
'action': 'action_view',
29+
'data': Uri.encodeFull('https://flutter.io'),
30+
'flags': androidIntent.convertFlags(<int>[Flag.FLAG_ACTIVITY_NEW_TASK]),
31+
}));
32+
});
33+
test('pass null value to action param', () async {
34+
androidIntent = AndroidIntent.private(
35+
action: null,
36+
channel: mockChannel,
37+
platform: FakePlatform(operatingSystem: 'android'));
38+
androidIntent.launch();
39+
verify(mockChannel.invokeMethod<void>('launch', <String, Object>{
40+
'action': null,
41+
}));
42+
});
43+
44+
test('call in ios platform', () async {
45+
androidIntent = AndroidIntent.private(
46+
action: null,
47+
channel: mockChannel,
48+
platform: FakePlatform(operatingSystem: 'ios'));
49+
androidIntent.launch();
50+
verifyZeroInteractions(mockChannel);
51+
});
52+
});
53+
group('convertFlags ', () {
54+
androidIntent = const AndroidIntent(
55+
action: 'action_view',
56+
);
57+
test('add filled flag list', () async {
58+
final List<int> flags = <int>[];
59+
flags.add(Flag.FLAG_ACTIVITY_NEW_TASK);
60+
flags.add(Flag.FLAG_ACTIVITY_NEW_DOCUMENT);
61+
expect(
62+
androidIntent.convertFlags(flags),
63+
268959744,
64+
);
65+
});
66+
test('add flags whose values are not power of 2', () async {
67+
final List<int> flags = <int>[];
68+
flags.add(100);
69+
flags.add(10);
70+
expect(
71+
() => androidIntent.convertFlags(flags),
72+
throwsArgumentError,
73+
);
74+
});
75+
test('add empty flag list', () async {
76+
final List<int> flags = <int>[];
77+
expect(
78+
androidIntent.convertFlags(flags),
79+
0,
80+
);
81+
});
82+
});
83+
}
84+
85+
class MockMethodChannel extends Mock implements MethodChannel {}

0 commit comments

Comments
 (0)