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

[url_launcher] Convert Windows to Pigeon #6991

Merged
merged 10 commits into from
Jan 25, 2023
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
3 changes: 2 additions & 1 deletion packages/url_launcher/url_launcher_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 3.0.3

* Converts internal implentation to Pigeon.
* Updates minimum Flutter version to 3.0.

## 3.0.2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2013 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.
// Autogenerated from Pigeon (v5.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// 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
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;

import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';

class UrlLauncherApi {
/// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
UrlLauncherApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;

static const MessageCodec<Object?> codec = StandardMessageCodec();

Future<bool> canLaunchUrl(String arg_url) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_url]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else if (replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (replyList[0] as bool?)!;
}
}

Future<void> launchUrl(String arg_url) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.UrlLauncherApi.launchUrl', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_url]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:url_launcher_platform_interface/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

const MethodChannel _channel =
MethodChannel('plugins.flutter.io/url_launcher_windows');
import 'src/messages.g.dart';

/// An implementation of [UrlLauncherPlatform] for Windows.
class UrlLauncherWindows extends UrlLauncherPlatform {
/// Creates a new plugin implementation instance.
UrlLauncherWindows({
@visibleForTesting UrlLauncherApi? api,
}) : _hostApi = api ?? UrlLauncherApi();

final UrlLauncherApi _hostApi;

/// Registers this class as the default instance of [UrlLauncherPlatform].
static void registerWith() {
UrlLauncherPlatform.instance = UrlLauncherWindows();
Expand All @@ -23,10 +27,7 @@ class UrlLauncherWindows extends UrlLauncherPlatform {

@override
Future<bool> canLaunch(String url) {
return _channel.invokeMethod<bool>(
'canLaunch',
<String, Object>{'url': url},
).then((bool? value) => value ?? false);
return _hostApi.canLaunchUrl(url);
}

@override
Expand All @@ -39,16 +40,9 @@ class UrlLauncherWindows extends UrlLauncherPlatform {
required bool universalLinksOnly,
required Map<String, String> headers,
String? webOnlyWindowName,
}) {
return _channel.invokeMethod<bool>(
'launch',
<String, Object>{
'url': url,
'enableJavaScript': enableJavaScript,
'enableDomStorage': enableDomStorage,
'universalLinksOnly': universalLinksOnly,
'headers': headers,
},
).then((bool? value) => value ?? false);
}) async {
await _hostApi.launchUrl(url);
// Failure is handled via a PlatformException from `launchUrl`.
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Copyright 2013 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2013 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:pigeon/pigeon.dart';

@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/src/messages.g.dart',
cppOptions: CppOptions(namespace: 'url_launcher_windows'),
cppHeaderOut: 'windows/messages.g.h',
cppSourceOut: 'windows/messages.g.cpp',
copyrightHeader: 'pigeons/copyright.txt',
))
@HostApi(dartHostTestHandler: 'TestUrlLauncherApi')
abstract class UrlLauncherApi {
bool canLaunchUrl(String url);
void launchUrl(String url);
}
3 changes: 2 additions & 1 deletion packages/url_launcher/url_launcher_windows/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: url_launcher_windows
description: Windows implementation of the url_launcher plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22
version: 3.0.2
version: 3.0.3

environment:
sdk: ">=2.12.0 <3.0.0"
Expand All @@ -24,4 +24,5 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
pigeon: ^5.0.1
test: ^1.16.3
Original file line number Diff line number Diff line change
Expand Up @@ -5,140 +5,101 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:url_launcher_windows/src/messages.g.dart';
import 'package:url_launcher_windows/url_launcher_windows.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

group('$UrlLauncherWindows', () {
const MethodChannel channel =
MethodChannel('plugins.flutter.io/url_launcher_windows');
final List<MethodCall> log = <MethodCall>[];
channel.setMockMethodCallHandler((MethodCall methodCall) async {
log.add(methodCall);

// Return null explicitly instead of relying on the implicit null
// returned by the method channel if no return statement is specified.
return null;
});
late _FakeUrlLauncherApi api;
late UrlLauncherWindows plugin;

test('registers instance', () {
UrlLauncherWindows.registerWith();
expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
});
setUp(() {
api = _FakeUrlLauncherApi();
plugin = UrlLauncherWindows(api: api);
});

tearDown(() {
log.clear();
});
test('registers instance', () {
UrlLauncherWindows.registerWith();
expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
});

test('canLaunch', () async {
final UrlLauncherWindows launcher = UrlLauncherWindows();
await launcher.canLaunch('http://example.com/');
expect(
log,
<Matcher>[
isMethodCall('canLaunch', arguments: <String, Object>{
'url': 'http://example.com/',
})
],
);
});
group('canLaunch', () {
test('handles true', () async {
api.canLaunch = true;

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

expect(canLaunch, false);
expect(result, isTrue);
expect(api.argument, 'http://example.com/');
});

test('launch', () async {
final UrlLauncherWindows launcher = UrlLauncherWindows();
await launcher.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
);
expect(
log,
<Matcher>[
isMethodCall('launch', arguments: <String, Object>{
'url': 'http://example.com/',
'enableJavaScript': false,
'enableDomStorage': false,
'universalLinksOnly': false,
'headers': <String, String>{},
})
],
);
});
test('handles false', () async {
api.canLaunch = false;

test('launch with headers', () async {
final UrlLauncherWindows launcher = UrlLauncherWindows();
await launcher.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{'key': 'value'},
);
expect(
log,
<Matcher>[
isMethodCall('launch', arguments: <String, Object>{
'url': 'http://example.com/',
'enableJavaScript': false,
'enableDomStorage': false,
'universalLinksOnly': false,
'headers': <String, String>{'key': 'value'},
})
],
);
final bool result = await plugin.canLaunch('http://example.com/');

expect(result, isFalse);
expect(api.argument, 'http://example.com/');
});
});

group('launch', () {
test('handles success', () async {
api.canLaunch = true;

test('launch universal links only', () async {
final UrlLauncherWindows launcher = UrlLauncherWindows();
await launcher.launch(
'http://example.com/',
useSafariVC: false,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: true,
headers: const <String, String>{},
);
expect(
log,
<Matcher>[
isMethodCall('launch', arguments: <String, Object>{
'url': 'http://example.com/',
'enableJavaScript': false,
'enableDomStorage': false,
'universalLinksOnly': true,
'headers': <String, String>{},
})
],
);
plugin.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
),
completes);
expect(api.argument, 'http://example.com/');
});

test('launch should return false if platform returns null', () async {
final UrlLauncherWindows launcher = UrlLauncherWindows();
final bool launched = await launcher.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
);

expect(launched, false);
test('handles failure', () async {
api.canLaunch = false;

await expectLater(
plugin.launch(
'http://example.com/',
useSafariVC: true,
useWebView: false,
enableJavaScript: false,
enableDomStorage: false,
universalLinksOnly: false,
headers: const <String, String>{},
),
throwsA(isA<PlatformException>()));
expect(api.argument, 'http://example.com/');
});
});
}

class _FakeUrlLauncherApi implements UrlLauncherApi {
/// The argument that was passed to an API call.
String? argument;

/// Controls the behavior of the fake implementations.
///
/// - [canLaunchUrl] returns this value.
/// - [launchUrl] throws if this is false.
bool canLaunch = false;

@override
Future<bool> canLaunchUrl(String url) async {
argument = url;
return canLaunch;
}

@override
Future<void> launchUrl(String url) async {
argument = url;
if (!canLaunch) {
throw PlatformException(code: 'Failed');
}
}
}
Loading