[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
diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
index 07a5ef3..abb3ab1 100644
--- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md
+++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 3.0.3
+* Converts internal implentation to Pigeon.
* Updates minimum Flutter version to 3.0.
## 3.0.2
diff --git a/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart
new file mode 100644
index 0000000..a1d46c1
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart
@@ -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;
+ }
+ }
+}
diff --git a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart
index b0ee8cb..41c403e 100644
--- a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart
+++ b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart
@@ -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();
@@ -23,10 +27,7 @@
@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
@@ -39,16 +40,9 @@
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;
}
}
diff --git a/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt b/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt
new file mode 100644
index 0000000..1236b63
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt
@@ -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.
diff --git a/packages/url_launcher/url_launcher_windows/pigeons/messages.dart b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart
new file mode 100644
index 0000000..9607cdf
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart
@@ -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);
+}
diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml
index 63ca778..de4f5ed 100644
--- a/packages/url_launcher/url_launcher_windows/pubspec.yaml
+++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml
@@ -2,7 +2,7 @@
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"
@@ -24,4 +24,5 @@
dev_dependencies:
flutter_test:
sdk: flutter
+ pigeon: ^5.0.1
test: ^1.16.3
diff --git a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart
index 8b55b29..7f48f64 100644
--- a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart
+++ b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart
@@ -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();
+ late _FakeUrlLauncherApi api;
+ late UrlLauncherWindows plugin;
- 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);
+ setUp(() {
+ api = _FakeUrlLauncherApi();
+ plugin = UrlLauncherWindows(api: api);
+ });
- // Return null explicitly instead of relying on the implicit null
- // returned by the method channel if no return statement is specified.
- return null;
+ test('registers instance', () {
+ UrlLauncherWindows.registerWith();
+ expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
+ });
+
+ group('canLaunch', () {
+ test('handles true', () async {
+ api.canLaunch = true;
+
+ final bool result = await plugin.canLaunch('http://example.com/');
+
+ expect(result, isTrue);
+ expect(api.argument, 'http://example.com/');
});
- test('registers instance', () {
- UrlLauncherWindows.registerWith();
- expect(UrlLauncherPlatform.instance, isA<UrlLauncherWindows>());
- });
+ test('handles false', () async {
+ api.canLaunch = false;
- tearDown(() {
- log.clear();
- });
+ final bool result = await plugin.canLaunch('http://example.com/');
- 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/',
- })
- ],
- );
- });
-
- test('canLaunch should return false if platform returns null', () async {
- final UrlLauncherWindows launcher = UrlLauncherWindows();
- final bool canLaunch = await launcher.canLaunch('http://example.com/');
-
- expect(canLaunch, false);
- });
-
- 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('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'},
- })
- ],
- );
- });
-
- 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>{},
- })
- ],
- );
- });
-
- 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);
+ expect(result, isFalse);
+ expect(api.argument, 'http://example.com/');
});
});
+
+ group('launch', () {
+ test('handles success', () async {
+ api.canLaunch = true;
+
+ expect(
+ 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('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');
+ }
+ }
}
diff --git a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt
index a4185ac..a34bcb3 100644
--- a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt
+++ b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt
@@ -5,6 +5,8 @@
set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
list(APPEND PLUGIN_SOURCES
+ "messages.g.cpp"
+ "messages.g.h"
"system_apis.cpp"
"system_apis.h"
"url_launcher_plugin.cpp"
diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp
new file mode 100644
index 0000000..eb1cf79
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp
@@ -0,0 +1,113 @@
+// 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
+
+#undef _HAS_EXCEPTIONS
+
+#include "messages.g.h"
+
+#include <flutter/basic_message_channel.h>
+#include <flutter/binary_messenger.h>
+#include <flutter/encodable_value.h>
+#include <flutter/standard_message_codec.h>
+
+#include <map>
+#include <optional>
+#include <string>
+
+namespace url_launcher_windows {
+
+/// The codec used by UrlLauncherApi.
+const flutter::StandardMessageCodec& UrlLauncherApi::GetCodec() {
+ return flutter::StandardMessageCodec::GetInstance(
+ &flutter::StandardCodecSerializer::GetInstance());
+}
+
+// Sets up an instance of `UrlLauncherApi` to handle messages through the
+// `binary_messenger`.
+void UrlLauncherApi::SetUp(flutter::BinaryMessenger* binary_messenger,
+ UrlLauncherApi* api) {
+ {
+ auto channel =
+ std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
+ binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl",
+ &GetCodec());
+ if (api != nullptr) {
+ channel->SetMessageHandler(
+ [api](const flutter::EncodableValue& message,
+ const flutter::MessageReply<flutter::EncodableValue>& reply) {
+ try {
+ const auto& args = std::get<flutter::EncodableList>(message);
+ const auto& encodable_url_arg = args.at(0);
+ if (encodable_url_arg.IsNull()) {
+ reply(WrapError("url_arg unexpectedly null."));
+ return;
+ }
+ const auto& url_arg = std::get<std::string>(encodable_url_arg);
+ ErrorOr<bool> output = api->CanLaunchUrl(url_arg);
+ if (output.has_error()) {
+ reply(WrapError(output.error()));
+ return;
+ }
+ flutter::EncodableList wrapped;
+ wrapped.push_back(
+ flutter::EncodableValue(std::move(output).TakeValue()));
+ reply(flutter::EncodableValue(std::move(wrapped)));
+ } catch (const std::exception& exception) {
+ reply(WrapError(exception.what()));
+ }
+ });
+ } else {
+ channel->SetMessageHandler(nullptr);
+ }
+ }
+ {
+ auto channel =
+ std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
+ binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.launchUrl",
+ &GetCodec());
+ if (api != nullptr) {
+ channel->SetMessageHandler(
+ [api](const flutter::EncodableValue& message,
+ const flutter::MessageReply<flutter::EncodableValue>& reply) {
+ try {
+ const auto& args = std::get<flutter::EncodableList>(message);
+ const auto& encodable_url_arg = args.at(0);
+ if (encodable_url_arg.IsNull()) {
+ reply(WrapError("url_arg unexpectedly null."));
+ return;
+ }
+ const auto& url_arg = std::get<std::string>(encodable_url_arg);
+ std::optional<FlutterError> output = api->LaunchUrl(url_arg);
+ if (output.has_value()) {
+ reply(WrapError(output.value()));
+ return;
+ }
+ flutter::EncodableList wrapped;
+ wrapped.push_back(flutter::EncodableValue());
+ reply(flutter::EncodableValue(std::move(wrapped)));
+ } catch (const std::exception& exception) {
+ reply(WrapError(exception.what()));
+ }
+ });
+ } else {
+ channel->SetMessageHandler(nullptr);
+ }
+ }
+}
+
+flutter::EncodableValue UrlLauncherApi::WrapError(
+ std::string_view error_message) {
+ return flutter::EncodableValue(flutter::EncodableList{
+ flutter::EncodableValue(std::string(error_message)),
+ flutter::EncodableValue("Error"), flutter::EncodableValue()});
+}
+flutter::EncodableValue UrlLauncherApi::WrapError(const FlutterError& error) {
+ return flutter::EncodableValue(flutter::EncodableList{
+ flutter::EncodableValue(error.message()),
+ flutter::EncodableValue(error.code()), error.details()});
+}
+
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.g.h b/packages/url_launcher/url_launcher_windows/windows/messages.g.h
new file mode 100644
index 0000000..cb8e95f
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.h
@@ -0,0 +1,86 @@
+// 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
+
+#ifndef PIGEON_H_
+#define PIGEON_H_
+#include <flutter/basic_message_channel.h>
+#include <flutter/binary_messenger.h>
+#include <flutter/encodable_value.h>
+#include <flutter/standard_message_codec.h>
+
+#include <map>
+#include <optional>
+#include <string>
+
+namespace url_launcher_windows {
+
+// Generated class from Pigeon.
+
+class FlutterError {
+ public:
+ explicit FlutterError(const std::string& code) : code_(code) {}
+ explicit FlutterError(const std::string& code, const std::string& message)
+ : code_(code), message_(message) {}
+ explicit FlutterError(const std::string& code, const std::string& message,
+ const flutter::EncodableValue& details)
+ : code_(code), message_(message), details_(details) {}
+
+ const std::string& code() const { return code_; }
+ const std::string& message() const { return message_; }
+ const flutter::EncodableValue& details() const { return details_; }
+
+ private:
+ std::string code_;
+ std::string message_;
+ flutter::EncodableValue details_;
+};
+
+template <class T>
+class ErrorOr {
+ public:
+ ErrorOr(const T& rhs) { new (&v_) T(rhs); }
+ ErrorOr(const T&& rhs) { v_ = std::move(rhs); }
+ ErrorOr(const FlutterError& rhs) { new (&v_) FlutterError(rhs); }
+ ErrorOr(const FlutterError&& rhs) { v_ = std::move(rhs); }
+
+ bool has_error() const { return std::holds_alternative<FlutterError>(v_); }
+ const T& value() const { return std::get<T>(v_); };
+ const FlutterError& error() const { return std::get<FlutterError>(v_); };
+
+ private:
+ friend class UrlLauncherApi;
+ ErrorOr() = default;
+ T TakeValue() && { return std::get<T>(std::move(v_)); }
+
+ std::variant<T, FlutterError> v_;
+};
+
+// Generated interface from Pigeon that represents a handler of messages from
+// Flutter.
+class UrlLauncherApi {
+ public:
+ UrlLauncherApi(const UrlLauncherApi&) = delete;
+ UrlLauncherApi& operator=(const UrlLauncherApi&) = delete;
+ virtual ~UrlLauncherApi(){};
+ virtual ErrorOr<bool> CanLaunchUrl(const std::string& url) = 0;
+ virtual std::optional<FlutterError> LaunchUrl(const std::string& url) = 0;
+
+ // The codec used by UrlLauncherApi.
+ static const flutter::StandardMessageCodec& GetCodec();
+ // Sets up an instance of `UrlLauncherApi` to handle messages through the
+ // `binary_messenger`.
+ static void SetUp(flutter::BinaryMessenger* binary_messenger,
+ UrlLauncherApi* api);
+ static flutter::EncodableValue WrapError(std::string_view error_message);
+ static flutter::EncodableValue WrapError(const FlutterError& error);
+
+ protected:
+ UrlLauncherApi() = default;
+};
+
+} // namespace url_launcher_windows
+
+#endif // PIGEON_H_
diff --git a/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp b/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp
index abd690b..cde95ee 100644
--- a/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp
+++ b/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp
@@ -5,7 +5,7 @@
#include <windows.h>
-namespace url_launcher_plugin {
+namespace url_launcher_windows {
SystemApis::SystemApis() {}
@@ -35,4 +35,4 @@
show_flags);
}
-} // namespace url_launcher_plugin
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/system_apis.h b/packages/url_launcher/url_launcher_windows/windows/system_apis.h
index 7b56704..c56c410 100644
--- a/packages/url_launcher/url_launcher_windows/windows/system_apis.h
+++ b/packages/url_launcher/url_launcher_windows/windows/system_apis.h
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include <windows.h>
-namespace url_launcher_plugin {
+namespace url_launcher_windows {
// An interface wrapping system APIs used by the plugin, for mocking.
class SystemApis {
@@ -53,4 +53,4 @@
int show_flags);
};
-} // namespace url_launcher_plugin
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp
index 191d51a..9dd2be5 100644
--- a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp
+++ b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp
@@ -9,11 +9,13 @@
#include <windows.h>
#include <memory>
+#include <optional>
#include <string>
+#include "messages.g.h"
#include "url_launcher_plugin.h"
-namespace url_launcher_plugin {
+namespace url_launcher_windows {
namespace test {
namespace {
@@ -42,30 +44,10 @@
(override));
};
-class MockMethodResult : public flutter::MethodResult<> {
- public:
- MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result),
- (override));
- MOCK_METHOD(void, ErrorInternal,
- (const std::string& error_code, const std::string& error_message,
- const EncodableValue* details),
- (override));
- MOCK_METHOD(void, NotImplementedInternal, (), (override));
-};
-
-std::unique_ptr<EncodableValue> CreateArgumentsWithUrl(const std::string& url) {
- EncodableMap args = {
- {EncodableValue("url"), EncodableValue(url)},
- };
- return std::make_unique<EncodableValue>(args);
-}
-
} // namespace
TEST(UrlLauncherPlugin, CanLaunchSuccessTrue) {
std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();
- std::unique_ptr<MockMethodResult> result =
- std::make_unique<MockMethodResult>();
// Return success values from the registery commands.
HKEY fake_key = reinterpret_cast<HKEY>(1);
@@ -73,20 +55,16 @@
.WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS)));
EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_SUCCESS));
EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS));
- // Expect a success response.
- EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true))));
UrlLauncherPlugin plugin(std::move(system));
- plugin.HandleMethodCall(
- flutter::MethodCall("canLaunch",
- CreateArgumentsWithUrl("https://some.url.com")),
- std::move(result));
+ ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");
+
+ ASSERT_FALSE(result.has_error());
+ EXPECT_TRUE(result.value());
}
TEST(UrlLauncherPlugin, CanLaunchQueryFailure) {
std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();
- std::unique_ptr<MockMethodResult> result =
- std::make_unique<MockMethodResult>();
// Return success values from the registery commands, except for the query,
// to simulate a scheme that is in the registry, but has no URL handler.
@@ -95,68 +73,52 @@
.WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS)));
EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_FILE_NOT_FOUND));
EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS));
- // Expect a success response.
- EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false))));
UrlLauncherPlugin plugin(std::move(system));
- plugin.HandleMethodCall(
- flutter::MethodCall("canLaunch",
- CreateArgumentsWithUrl("https://some.url.com")),
- std::move(result));
+ ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");
+
+ ASSERT_FALSE(result.has_error());
+ EXPECT_FALSE(result.value());
}
TEST(UrlLauncherPlugin, CanLaunchHandlesOpenFailure) {
std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();
- std::unique_ptr<MockMethodResult> result =
- std::make_unique<MockMethodResult>();
// Return failure for opening.
EXPECT_CALL(*system, RegOpenKeyExW).WillOnce(Return(ERROR_BAD_PATHNAME));
- // Expect a success response.
- EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false))));
UrlLauncherPlugin plugin(std::move(system));
- plugin.HandleMethodCall(
- flutter::MethodCall("canLaunch",
- CreateArgumentsWithUrl("https://some.url.com")),
- std::move(result));
+ ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");
+
+ ASSERT_FALSE(result.has_error());
+ EXPECT_FALSE(result.value());
}
TEST(UrlLauncherPlugin, LaunchSuccess) {
std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();
- std::unique_ptr<MockMethodResult> result =
- std::make_unique<MockMethodResult>();
// Return a success value (>32) from launching.
EXPECT_CALL(*system, ShellExecuteW)
.WillOnce(Return(reinterpret_cast<HINSTANCE>(33)));
- // Expect a success response.
- EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true))));
UrlLauncherPlugin plugin(std::move(system));
- plugin.HandleMethodCall(
- flutter::MethodCall("launch",
- CreateArgumentsWithUrl("https://some.url.com")),
- std::move(result));
+ std::optional<FlutterError> error = plugin.LaunchUrl("https://some.url.com");
+
+ EXPECT_FALSE(error.has_value());
}
TEST(UrlLauncherPlugin, LaunchReportsFailure) {
std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();
- std::unique_ptr<MockMethodResult> result =
- std::make_unique<MockMethodResult>();
// Return a faile value (<=32) from launching.
EXPECT_CALL(*system, ShellExecuteW)
.WillOnce(Return(reinterpret_cast<HINSTANCE>(32)));
- // Expect an error response.
- EXPECT_CALL(*result, ErrorInternal);
UrlLauncherPlugin plugin(std::move(system));
- plugin.HandleMethodCall(
- flutter::MethodCall("launch",
- CreateArgumentsWithUrl("https://some.url.com")),
- std::move(result));
+ std::optional<FlutterError> error = plugin.LaunchUrl("https://some.url.com");
+
+ EXPECT_TRUE(error.has_value());
}
} // namespace test
-} // namespace url_launcher_plugin
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp
index d5f2012..1dfee16 100644
--- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp
+++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp
@@ -13,7 +13,9 @@
#include <sstream>
#include <string>
-namespace url_launcher_plugin {
+#include "messages.g.h"
+
+namespace url_launcher_windows {
namespace {
@@ -62,18 +64,9 @@
// static
void UrlLauncherPlugin::RegisterWithRegistrar(
flutter::PluginRegistrar* registrar) {
- auto channel = std::make_unique<flutter::MethodChannel<>>(
- registrar->messenger(), "plugins.flutter.io/url_launcher_windows",
- &flutter::StandardMethodCodec::GetInstance());
-
std::unique_ptr<UrlLauncherPlugin> plugin =
std::make_unique<UrlLauncherPlugin>();
-
- channel->SetMethodCallHandler(
- [plugin_pointer = plugin.get()](const auto& call, auto result) {
- plugin_pointer->HandleMethodCall(call, std::move(result));
- });
-
+ UrlLauncherApi::SetUp(registrar->messenger(), plugin.get());
registrar->AddPlugin(std::move(plugin));
}
@@ -85,37 +78,7 @@
UrlLauncherPlugin::~UrlLauncherPlugin() = default;
-void UrlLauncherPlugin::HandleMethodCall(
- const flutter::MethodCall<>& method_call,
- std::unique_ptr<flutter::MethodResult<>> result) {
- if (method_call.method_name().compare("launch") == 0) {
- std::string url = GetUrlArgument(method_call);
- if (url.empty()) {
- result->Error("argument_error", "No URL provided");
- return;
- }
-
- std::optional<std::string> error = LaunchUrl(url);
- if (error) {
- result->Error("open_error", error.value());
- return;
- }
- result->Success(EncodableValue(true));
- } else if (method_call.method_name().compare("canLaunch") == 0) {
- std::string url = GetUrlArgument(method_call);
- if (url.empty()) {
- result->Error("argument_error", "No URL provided");
- return;
- }
-
- bool can_launch = CanLaunchUrl(url);
- result->Success(EncodableValue(can_launch));
- } else {
- result->NotImplemented();
- }
-}
-
-bool UrlLauncherPlugin::CanLaunchUrl(const std::string& url) {
+ErrorOr<bool> UrlLauncherPlugin::CanLaunchUrl(const std::string& url) {
size_t separator_location = url.find(":");
if (separator_location == std::string::npos) {
return false;
@@ -134,7 +97,7 @@
return has_handler;
}
-std::optional<std::string> UrlLauncherPlugin::LaunchUrl(
+std::optional<FlutterError> UrlLauncherPlugin::LaunchUrl(
const std::string& url) {
std::wstring url_wide = Utf16FromUtf8(url);
@@ -147,9 +110,9 @@
std::ostringstream error_message;
error_message << "Failed to open " << url << ": ShellExecute error code "
<< status;
- return std::optional<std::string>(error_message.str());
+ return FlutterError("open_error", error_message.str());
}
return std::nullopt;
}
-} // namespace url_launcher_plugin
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h
index 45e70e5..e51cde6 100644
--- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h
+++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h
@@ -10,11 +10,12 @@
#include <sstream>
#include <string>
+#include "messages.g.h"
#include "system_apis.h"
-namespace url_launcher_plugin {
+namespace url_launcher_windows {
-class UrlLauncherPlugin : public flutter::Plugin {
+class UrlLauncherPlugin : public flutter::Plugin, public UrlLauncherApi {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar);
@@ -31,18 +32,12 @@
UrlLauncherPlugin(const UrlLauncherPlugin&) = delete;
UrlLauncherPlugin& operator=(const UrlLauncherPlugin&) = delete;
- // Called when a method is called on the plugin channel.
- void HandleMethodCall(const flutter::MethodCall<>& method_call,
- std::unique_ptr<flutter::MethodResult<>> result);
+ // UrlLauncherApi:
+ ErrorOr<bool> CanLaunchUrl(const std::string& url) override;
+ std::optional<FlutterError> LaunchUrl(const std::string& url) override;
private:
- // Returns whether or not the given URL has a registered handler.
- bool CanLaunchUrl(const std::string& url);
-
- // Attempts to launch the given URL. On failure, returns an error string.
- std::optional<std::string> LaunchUrl(const std::string& url);
-
std::unique_ptr<SystemApis> system_apis_;
};
-} // namespace url_launcher_plugin
+} // namespace url_launcher_windows
diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp
index 05de586..7267093 100644
--- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp
+++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp
@@ -9,7 +9,7 @@
void UrlLauncherWindowsRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
- url_launcher_plugin::UrlLauncherPlugin::RegisterWithRegistrar(
+ url_launcher_windows::UrlLauncherPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}