Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 15d799b

Browse files
[url_launcher] Convert Windows to Pigeon (#6991)
* Initial definition matching current API * Rename, autoformat * Update native implementation and unit tests * Update Dart; remove unnecessary Pigeon test API * Version bump * autoformat * Adjust mock API setup * Improve comment
1 parent bc0174f commit 15d799b

16 files changed

+432
-262
lines changed

packages/url_launcher/url_launcher_windows/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 3.0.3
22

3+
* Converts internal implentation to Pigeon.
34
* Updates minimum Flutter version to 3.0.
45

56
## 3.0.2
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2013 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+
// Autogenerated from Pigeon (v5.0.1), do not edit directly.
5+
// See also: https://pub.dev/packages/pigeon
6+
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
7+
import 'dart:async';
8+
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
9+
10+
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
11+
import 'package:flutter/services.dart';
12+
13+
class UrlLauncherApi {
14+
/// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is
15+
/// available for dependency injection. If it is left null, the default
16+
/// BinaryMessenger will be used which routes to the host platform.
17+
UrlLauncherApi({BinaryMessenger? binaryMessenger})
18+
: _binaryMessenger = binaryMessenger;
19+
final BinaryMessenger? _binaryMessenger;
20+
21+
static const MessageCodec<Object?> codec = StandardMessageCodec();
22+
23+
Future<bool> canLaunchUrl(String arg_url) async {
24+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
25+
'dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl', codec,
26+
binaryMessenger: _binaryMessenger);
27+
final List<Object?>? replyList =
28+
await channel.send(<Object?>[arg_url]) as List<Object?>?;
29+
if (replyList == null) {
30+
throw PlatformException(
31+
code: 'channel-error',
32+
message: 'Unable to establish connection on channel.',
33+
);
34+
} else if (replyList.length > 1) {
35+
throw PlatformException(
36+
code: replyList[0]! as String,
37+
message: replyList[1] as String?,
38+
details: replyList[2],
39+
);
40+
} else if (replyList[0] == null) {
41+
throw PlatformException(
42+
code: 'null-error',
43+
message: 'Host platform returned null value for non-null return value.',
44+
);
45+
} else {
46+
return (replyList[0] as bool?)!;
47+
}
48+
}
49+
50+
Future<void> launchUrl(String arg_url) async {
51+
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
52+
'dev.flutter.pigeon.UrlLauncherApi.launchUrl', codec,
53+
binaryMessenger: _binaryMessenger);
54+
final List<Object?>? replyList =
55+
await channel.send(<Object?>[arg_url]) as List<Object?>?;
56+
if (replyList == null) {
57+
throw PlatformException(
58+
code: 'channel-error',
59+
message: 'Unable to establish connection on channel.',
60+
);
61+
} else if (replyList.length > 1) {
62+
throw PlatformException(
63+
code: replyList[0]! as String,
64+
message: replyList[1] as String?,
65+
details: replyList[2],
66+
);
67+
} else {
68+
return;
69+
}
70+
}
71+
}

packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:async';
6-
7-
import 'package:flutter/services.dart';
5+
import 'package:flutter/foundation.dart';
86
import 'package:url_launcher_platform_interface/link.dart';
97
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
108

11-
const MethodChannel _channel =
12-
MethodChannel('plugins.flutter.io/url_launcher_windows');
9+
import 'src/messages.g.dart';
1310

1411
/// An implementation of [UrlLauncherPlatform] for Windows.
1512
class UrlLauncherWindows extends UrlLauncherPlatform {
13+
/// Creates a new plugin implementation instance.
14+
UrlLauncherWindows({
15+
@visibleForTesting UrlLauncherApi? api,
16+
}) : _hostApi = api ?? UrlLauncherApi();
17+
18+
final UrlLauncherApi _hostApi;
19+
1620
/// Registers this class as the default instance of [UrlLauncherPlatform].
1721
static void registerWith() {
1822
UrlLauncherPlatform.instance = UrlLauncherWindows();
@@ -23,10 +27,7 @@ class UrlLauncherWindows extends UrlLauncherPlatform {
2327

2428
@override
2529
Future<bool> canLaunch(String url) {
26-
return _channel.invokeMethod<bool>(
27-
'canLaunch',
28-
<String, Object>{'url': url},
29-
).then((bool? value) => value ?? false);
30+
return _hostApi.canLaunchUrl(url);
3031
}
3132

3233
@override
@@ -39,16 +40,9 @@ class UrlLauncherWindows extends UrlLauncherPlatform {
3940
required bool universalLinksOnly,
4041
required Map<String, String> headers,
4142
String? webOnlyWindowName,
42-
}) {
43-
return _channel.invokeMethod<bool>(
44-
'launch',
45-
<String, Object>{
46-
'url': url,
47-
'enableJavaScript': enableJavaScript,
48-
'enableDomStorage': enableDomStorage,
49-
'universalLinksOnly': universalLinksOnly,
50-
'headers': headers,
51-
},
52-
).then((bool? value) => value ?? false);
43+
}) async {
44+
await _hostApi.launchUrl(url);
45+
// Failure is handled via a PlatformException from `launchUrl`.
46+
return true;
5347
}
5448
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Copyright 2013 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.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2013 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:pigeon/pigeon.dart';
6+
7+
@ConfigurePigeon(PigeonOptions(
8+
dartOut: 'lib/src/messages.g.dart',
9+
cppOptions: CppOptions(namespace: 'url_launcher_windows'),
10+
cppHeaderOut: 'windows/messages.g.h',
11+
cppSourceOut: 'windows/messages.g.cpp',
12+
copyrightHeader: 'pigeons/copyright.txt',
13+
))
14+
@HostApi(dartHostTestHandler: 'TestUrlLauncherApi')
15+
abstract class UrlLauncherApi {
16+
bool canLaunchUrl(String url);
17+
void launchUrl(String url);
18+
}

packages/url_launcher/url_launcher_windows/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: url_launcher_windows
22
description: Windows implementation of the url_launcher plugin.
33
repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22
5-
version: 3.0.2
5+
version: 3.0.3
66

77
environment:
88
sdk: ">=2.12.0 <3.0.0"
@@ -24,4 +24,5 @@ dependencies:
2424
dev_dependencies:
2525
flutter_test:
2626
sdk: flutter
27+
pigeon: ^5.0.1
2728
test: ^1.16.3

packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart

Lines changed: 79 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -5,140 +5,101 @@
55
import 'package:flutter/services.dart';
66
import 'package:flutter_test/flutter_test.dart';
77
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
8+
import 'package:url_launcher_windows/src/messages.g.dart';
89
import 'package:url_launcher_windows/url_launcher_windows.dart';
910

1011
void main() {
11-
TestWidgetsFlutterBinding.ensureInitialized();
12-
13-
group('$UrlLauncherWindows', () {
14-
const MethodChannel channel =
15-
MethodChannel('plugins.flutter.io/url_launcher_windows');
16-
final List<MethodCall> log = <MethodCall>[];
17-
channel.setMockMethodCallHandler((MethodCall methodCall) async {
18-
log.add(methodCall);
19-
20-
// Return null explicitly instead of relying on the implicit null
21-
// returned by the method channel if no return statement is specified.
22-
return null;
23-
});
12+
late _FakeUrlLauncherApi api;
13+
late UrlLauncherWindows plugin;
2414

25-
test('registers instance', () {
26-
UrlLauncherWindows.registerWith();
27-
expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
28-
});
15+
setUp(() {
16+
api = _FakeUrlLauncherApi();
17+
plugin = UrlLauncherWindows(api: api);
18+
});
2919

30-
tearDown(() {
31-
log.clear();
32-
});
20+
test('registers instance', () {
21+
UrlLauncherWindows.registerWith();
22+
expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
23+
});
3324

34-
test('canLaunch', () async {
35-
final UrlLauncherWindows launcher = UrlLauncherWindows();
36-
await launcher.canLaunch('http://example.com/');
37-
expect(
38-
log,
39-
<Matcher>[
40-
isMethodCall('canLaunch', arguments: <String, Object>{
41-
'url': 'http://example.com/',
42-
})
43-
],
44-
);
45-
});
25+
group('canLaunch', () {
26+
test('handles true', () async {
27+
api.canLaunch = true;
4628

47-
test('canLaunch should return false if platform returns null', () async {
48-
final UrlLauncherWindows launcher = UrlLauncherWindows();
49-
final bool canLaunch = await launcher.canLaunch('http://example.com/');
29+
final bool result = await plugin.canLaunch('http://example.com/');
5030

51-
expect(canLaunch, false);
31+
expect(result, isTrue);
32+
expect(api.argument, 'http://example.com/');
5233
});
5334

54-
test('launch', () async {
55-
final UrlLauncherWindows launcher = UrlLauncherWindows();
56-
await launcher.launch(
57-
'http://example.com/',
58-
useSafariVC: true,
59-
useWebView: false,
60-
enableJavaScript: false,
61-
enableDomStorage: false,
62-
universalLinksOnly: false,
63-
headers: const <String, String>{},
64-
);
65-
expect(
66-
log,
67-
<Matcher>[
68-
isMethodCall('launch', arguments: <String, Object>{
69-
'url': 'http://example.com/',
70-
'enableJavaScript': false,
71-
'enableDomStorage': false,
72-
'universalLinksOnly': false,
73-
'headers': <String, String>{},
74-
})
75-
],
76-
);
77-
});
35+
test('handles false', () async {
36+
api.canLaunch = false;
7837

79-
test('launch with headers', () async {
80-
final UrlLauncherWindows launcher = UrlLauncherWindows();
81-
await launcher.launch(
82-
'http://example.com/',
83-
useSafariVC: true,
84-
useWebView: false,
85-
enableJavaScript: false,
86-
enableDomStorage: false,
87-
universalLinksOnly: false,
88-
headers: const <String, String>{'key': 'value'},
89-
);
90-
expect(
91-
log,
92-
<Matcher>[
93-
isMethodCall('launch', arguments: <String, Object>{
94-
'url': 'http://example.com/',
95-
'enableJavaScript': false,
96-
'enableDomStorage': false,
97-
'universalLinksOnly': false,
98-
'headers': <String, String>{'key': 'value'},
99-
})
100-
],
101-
);
38+
final bool result = await plugin.canLaunch('http://example.com/');
39+
40+
expect(result, isFalse);
41+
expect(api.argument, 'http://example.com/');
10242
});
43+
});
44+
45+
group('launch', () {
46+
test('handles success', () async {
47+
api.canLaunch = true;
10348

104-
test('launch universal links only', () async {
105-
final UrlLauncherWindows launcher = UrlLauncherWindows();
106-
await launcher.launch(
107-
'http://example.com/',
108-
useSafariVC: false,
109-
useWebView: false,
110-
enableJavaScript: false,
111-
enableDomStorage: false,
112-
universalLinksOnly: true,
113-
headers: const <String, String>{},
114-
);
11549
expect(
116-
log,
117-
<Matcher>[
118-
isMethodCall('launch', arguments: <String, Object>{
119-
'url': 'http://example.com/',
120-
'enableJavaScript': false,
121-
'enableDomStorage': false,
122-
'universalLinksOnly': true,
123-
'headers': <String, String>{},
124-
})
125-
],
126-
);
50+
plugin.launch(
51+
'http://example.com/',
52+
useSafariVC: true,
53+
useWebView: false,
54+
enableJavaScript: false,
55+
enableDomStorage: false,
56+
universalLinksOnly: false,
57+
headers: const <String, String>{},
58+
),
59+
completes);
60+
expect(api.argument, 'http://example.com/');
12761
});
12862

129-
test('launch should return false if platform returns null', () async {
130-
final UrlLauncherWindows launcher = UrlLauncherWindows();
131-
final bool launched = await launcher.launch(
132-
'http://example.com/',
133-
useSafariVC: true,
134-
useWebView: false,
135-
enableJavaScript: false,
136-
enableDomStorage: false,
137-
universalLinksOnly: false,
138-
headers: const <String, String>{},
139-
);
140-
141-
expect(launched, false);
63+
test('handles failure', () async {
64+
api.canLaunch = false;
65+
66+
await expectLater(
67+
plugin.launch(
68+
'http://example.com/',
69+
useSafariVC: true,
70+
useWebView: false,
71+
enableJavaScript: false,
72+
enableDomStorage: false,
73+
universalLinksOnly: false,
74+
headers: const <String, String>{},
75+
),
76+
throwsA(isA<PlatformException>()));
77+
expect(api.argument, 'http://example.com/');
14278
});
14379
});
14480
}
81+
82+
class _FakeUrlLauncherApi implements UrlLauncherApi {
83+
/// The argument that was passed to an API call.
84+
String? argument;
85+
86+
/// Controls the behavior of the fake implementations.
87+
///
88+
/// - [canLaunchUrl] returns this value.
89+
/// - [launchUrl] throws if this is false.
90+
bool canLaunch = false;
91+
92+
@override
93+
Future<bool> canLaunchUrl(String url) async {
94+
argument = url;
95+
return canLaunch;
96+
}
97+
98+
@override
99+
Future<void> launchUrl(String url) async {
100+
argument = url;
101+
if (!canLaunch) {
102+
throw PlatformException(code: 'Failed');
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)