-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
url_launcher_android.dart
165 lines (149 loc) · 5.8 KB
/
url_launcher_android.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 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:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';
import 'package:url_launcher_platform_interface/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'src/messages.g.dart';
/// An implementation of [UrlLauncherPlatform] for Android.
class UrlLauncherAndroid extends UrlLauncherPlatform {
/// Creates a new plugin implementation instance.
UrlLauncherAndroid({
@visibleForTesting UrlLauncherApi? api,
}) : _hostApi = api ?? UrlLauncherApi();
final UrlLauncherApi _hostApi;
/// Registers this class as the default instance of [UrlLauncherPlatform].
static void registerWith() {
UrlLauncherPlatform.instance = UrlLauncherAndroid();
}
@override
final LinkDelegate? linkDelegate = null;
@override
Future<bool> canLaunch(String url) async {
final bool canLaunchSpecificUrl = await _hostApi.canLaunchUrl(url);
if (!canLaunchSpecificUrl) {
final String scheme = _getUrlScheme(url);
// canLaunch can return false when a custom application is registered to
// handle a web URL, but the caller doesn't have permission to see what
// that handler is. If that happens, try a web URL (with the same scheme
// variant, to be safe) that should not have a custom handler. If that
// returns true, then there is a browser, which means that there is
// at least one handler for the original URL.
if (scheme == 'http' || scheme == 'https') {
return _hostApi.canLaunchUrl('$scheme://flutter.dev');
}
}
return canLaunchSpecificUrl;
}
@override
Future<void> closeWebView() {
return _hostApi.closeWebView();
}
@override
Future<bool> launch(
String url, {
required bool useSafariVC,
required bool useWebView,
required bool enableJavaScript,
required bool enableDomStorage,
required bool universalLinksOnly,
required Map<String, String> headers,
String? webOnlyWindowName,
}) async {
return launchUrl(
url,
LaunchOptions(
mode: useWebView
? PreferredLaunchMode.inAppWebView
: PreferredLaunchMode.externalApplication,
webViewConfiguration: InAppWebViewConfiguration(
enableDomStorage: enableDomStorage,
enableJavaScript: enableJavaScript,
headers: headers)));
}
@override
Future<bool> launchUrl(String url, LaunchOptions options) async {
final bool inApp;
switch (options.mode) {
case PreferredLaunchMode.inAppWebView:
case PreferredLaunchMode.inAppBrowserView:
inApp = true;
case PreferredLaunchMode.externalApplication:
case PreferredLaunchMode.externalNonBrowserApplication:
// TODO(stuartmorgan): Add full support for
// externalNonBrowsingApplication; see
// https://github.com/flutter/flutter/issues/66721.
// Currently it's treated the same as externalApplication.
inApp = false;
case PreferredLaunchMode.platformDefault:
// Intentionally treat any new values as platformDefault; see comment in
// supportsMode.
// ignore: no_default_cases
default:
// By default, open web URLs in the application.
inApp = url.startsWith('http:') || url.startsWith('https:');
break;
}
final bool succeeded;
if (inApp) {
succeeded = await _hostApi.openUrlInApp(
url,
// Prefer custom tabs unless a webview was specifically requested.
options.mode != PreferredLaunchMode.inAppWebView,
WebViewOptions(
enableJavaScript: options.webViewConfiguration.enableJavaScript,
enableDomStorage: options.webViewConfiguration.enableDomStorage,
headers: options.webViewConfiguration.headers,
),
BrowserOptions(
showTitle: options.browserConfiguration.showTitle,
),
);
} else {
succeeded =
await _hostApi.launchUrl(url, options.webViewConfiguration.headers);
}
// TODO(stuartmorgan): Remove this special handling as part of a
// breaking change to rework failure handling across all platform. The
// current behavior is backwards compatible with the previous Java error.
if (!succeeded) {
throw PlatformException(
code: 'ACTIVITY_NOT_FOUND',
message: 'No Activity found to handle intent { $url }');
}
return succeeded;
}
@override
Future<bool> supportsMode(PreferredLaunchMode mode) async {
switch (mode) {
case PreferredLaunchMode.platformDefault:
case PreferredLaunchMode.inAppWebView:
case PreferredLaunchMode.externalApplication:
return true;
case PreferredLaunchMode.inAppBrowserView:
return _hostApi.supportsCustomTabs();
// Default is a desired behavior here since support for new modes is
// always opt-in, and the enum lives in a different package, so silently
// adding "false" for new values is the correct behavior.
// ignore: no_default_cases
default:
return false;
}
}
@override
Future<bool> supportsCloseForMode(PreferredLaunchMode mode) async {
return mode == PreferredLaunchMode.inAppWebView;
}
// Returns the part of [url] up to the first ':', or an empty string if there
// is no ':'. This deliberately does not use [Uri] to extract the scheme
// so that it works on strings that aren't actually valid URLs, since Android
// is very lenient about what it accepts for launching.
String _getUrlScheme(String url) {
final int schemeEnd = url.indexOf(':');
if (schemeEnd == -1) {
return '';
}
return url.substring(0, schemeEnd);
}
}