Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/android_intent/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ public void onMethodCall(MethodCall call, Result result) {
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"));
}
Expand Down
14 changes: 14 additions & 0 deletions packages/android_intent/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -117,6 +118,15 @@ class ExplicitIntentsWidget extends StatelessWidget {
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',
Expand Down Expand Up @@ -162,6 +172,10 @@ class ExplicitIntentsWidget extends StatelessWidget {
child: const Text('Tap here to open link in Google Chrome.'),
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.'),
Expand Down
44 changes: 41 additions & 3 deletions packages/android_intent/lib/android_intent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const String kChannelName = 'plugins.flutter.io/android_intent';
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.
Expand All @@ -24,6 +25,7 @@ class AndroidIntent {
/// If not null, then [package] but also be provided.
const AndroidIntent({
@required this.action,
this.flags,
this.category,
this.data,
this.arguments,
Expand All @@ -34,7 +36,22 @@ class AndroidIntent {
_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;
Expand All @@ -43,13 +60,34 @@ class AndroidIntent {
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;
}
Expand Down
37 changes: 37 additions & 0 deletions packages/android_intent/lib/flag.dart
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 6 additions & 2 deletions packages/android_intent/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: android_intent
description: Flutter plugin for launching Android Intents. Not supported on iOS.
author: Flutter Team <[email protected]>
homepage: https://github.com/flutter/plugins/tree/master/packages/android_intent
version: 0.3.2
version: 0.3.3

flutter:
plugin:
Expand All @@ -15,7 +15,11 @@ dependencies:
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"
85 changes: 85 additions & 0 deletions packages/android_intent/test/android_intent_test.dart
Original file line number Diff line number Diff line change
@@ -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 {}