diff --git a/packages/url_launcher/url_launcher_platform_interface/LICENSE b/packages/url_launcher/url_launcher_platform_interface/LICENSE new file mode 100644 index 000000000000..c89293372cf3 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/url_launcher/url_launcher_platform_interface/README.md b/packages/url_launcher/url_launcher_platform_interface/README.md new file mode 100644 index 000000000000..3fd6b02cfdf5 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/README.md @@ -0,0 +1,26 @@ +# url_launcher_platform_interface + +A common platform interface for the [`url_launcher`][1] plugin. + +This interface allows platform-specific implementations of the `url_launcher` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `url_launcher`, extend +[`UrlLauncherPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`UrlLauncherPlatform` by calling +`UrlLauncherPlatform.instance = MyPlatformUrlLauncher()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../url_launcher +[2]: lib/url_launcher_platform_interface.dart diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart new file mode 100644 index 000000000000..3fbd2ee01843 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart @@ -0,0 +1,52 @@ +// Copyright 2017 The Chromium 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 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show required; + +import 'url_launcher_platform_interface.dart'; + +const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); + +/// An implementation of [UrlLauncherPlatform] that uses method channels. +class MethodChannelUrlLauncher extends UrlLauncherPlatform { + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ); + } + + @override + Future closeWebView() { + return _channel.invokeMethod('closeWebView'); + } + + @override + Future launch( + String url, { + @required bool useSafariVC, + @required bool useWebView, + @required bool enableJavaScript, + @required bool enableDomStorage, + @required bool universalLinksOnly, + @required Map headers, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'useSafariVC': useSafariVC, + 'useWebView': useWebView, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ); + } +} diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart new file mode 100644 index 000000000000..8e1d090eef2b --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart @@ -0,0 +1,54 @@ +// Copyright 2017 The Chromium 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 'dart:async'; + +import 'package:meta/meta.dart' show required; + +import 'method_channel_url_launcher.dart'; + +/// The interface that implementations of url_launcher must implement. +/// +/// Platform implementations that live in a separate package should extend this +/// class rather than implement it as `url_launcher` does not consider newly +/// added methods to be breaking changes. Extending this class (using `extends`) +/// ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by +/// newly added [UrlLauncherPlatform] methods. +abstract class UrlLauncherPlatform { + /// The default instance of [UrlLauncherPlatform] to use. + /// + /// Platform-specific plugins should override this with their own + /// platform-specific class that extends [UrlLauncherPlatform] when they + /// register themselves. + /// + /// Defaults to [MethodChannelUrlLauncher]. + static UrlLauncherPlatform instance = MethodChannelUrlLauncher(); + + /// Returns `true` if this platform is able to launch [url]. + Future canLaunch(String url) { + throw UnimplementedError('canLaunch() has not been implemented.'); + } + + /// Returns `true` if the given [url] was successfully launched. + /// + /// For documentation on the other arguments, see the `launch` documentation + /// in `package:url_launcher/url_launcher.dart`. + Future launch( + String url, { + @required bool useSafariVC, + @required bool useWebView, + @required bool enableJavaScript, + @required bool enableDomStorage, + @required bool universalLinksOnly, + @required Map headers, + }) { + throw UnimplementedError('launch() has not been implemented.'); + } + + /// Closes the WebView, if one was opened earlier by [launch]. + Future closeWebView() { + throw UnimplementedError('closeWebView() has not been implemented.'); + } +} diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..99b260efe943 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -0,0 +1,20 @@ +name: url_launcher_platform_interface +description: A common platform interface for the url_launcher plugin. +author: Flutter Team +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 + +dependencies: + flutter: + sdk: flutter + meta: ^1.0.5 + +dev_dependencies: + flutter_test: + sdk: flutter + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart new file mode 100644 index 000000000000..f409441903f3 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart @@ -0,0 +1,260 @@ +// Copyright 2017 The Chromium 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:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_platform_interface/method_channel_url_launcher.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + group('$MethodChannelUrlLauncher', () { + TestWidgetsFlutterBinding.ensureInitialized(); + + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + + final MethodChannelUrlLauncher launcher = MethodChannelUrlLauncher(); + + tearDown(() { + log.clear(); + }); + + test('is the default $UrlLauncherPlatform instance', () { + expect(UrlLauncherPlatform.instance, + isInstanceOf()); + }); + + test('canLaunch', () async { + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('launch', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch force SafariVC', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch universal links only', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': false, + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': true, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView enable javascript', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': true, + 'enableJavaScript': true, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView enable DOM storage', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'useWebView': true, + 'enableJavaScript': false, + 'enableDomStorage': true, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch force SafariVC to false', () async { + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': false, + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('closeWebView default behavior', () async { + await launcher.closeWebView(); + expect( + log, + [isMethodCall('closeWebView', arguments: null)], + ); + }); + }); +}