OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library usage_impl_io; | |
6 | |
7 import 'dart:async'; | 5 import 'dart:async'; |
8 import 'dart:convert' show JSON; | 6 import 'dart:convert' show JSON, JsonEncoder; |
9 import 'dart:io'; | 7 import 'dart:io'; |
10 | 8 |
11 import 'package:path/path.dart' as path; | 9 import 'package:path/path.dart' as path; |
12 | 10 |
13 import 'usage_impl.dart'; | 11 import 'usage_impl.dart'; |
14 | 12 |
15 String _createUserAgent() { | 13 /// An interface to a Google Analytics session, suitable for use in command-line |
16 // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) | 14 /// applications. |
17 // Dart/1.8.0-edge.41170 (macos; macos; macos; null) | 15 /// |
18 String os = Platform.operatingSystem; | 16 /// `trackingId`, `applicationName`, and `applicationVersion` values should be s
upplied. |
19 String locale = Platform.environment['LANG']; | 17 /// `analyticsUrl` is optional, and lets user's substitute their own analytics U
RL for |
20 return "Dart/${_dartVersion()} (${os}; ${os}; ${os}; ${locale})"; | 18 /// the default. |
| 19 /// |
| 20 /// `documentDirectory` is where the analytics settings are stored. It |
| 21 /// defaults to the user home directory. For regular `dart:io` apps this doesn't
need to |
| 22 /// be supplied. For Flutter applications, you should pass in a value like |
| 23 /// `PathProvider.getApplicationDocumentsDirectory()`. |
| 24 class AnalyticsIO extends AnalyticsImpl { |
| 25 AnalyticsIO( |
| 26 String trackingId, String applicationName, String applicationVersion, |
| 27 {String analyticsUrl, Directory documentDirectory}) |
| 28 : super( |
| 29 trackingId, |
| 30 new IOPersistentProperties(applicationName, |
| 31 documentDirPath: documentDirectory?.path), |
| 32 new IOPostHandler(), |
| 33 applicationName: applicationName, |
| 34 applicationVersion: applicationVersion, |
| 35 analyticsUrl: analyticsUrl) { |
| 36 final String locale = getPlatformLocale(); |
| 37 if (locale != null) { |
| 38 setSessionValue('ul', locale); |
| 39 } |
| 40 } |
21 } | 41 } |
22 | 42 |
23 String _userHomeDir() { | 43 String _createUserAgent() { |
| 44 final String locale = getPlatformLocale() ?? ''; |
| 45 |
| 46 if (Platform.isAndroid) { |
| 47 return 'Mozilla/5.0 (Android; Mobile; ${locale})'; |
| 48 } else if (Platform.isIOS) { |
| 49 return 'Mozilla/5.0 (iPhone; U; CPU iPhone OS like Mac OS X; ${locale})'; |
| 50 } else if (Platform.isMacOS) { |
| 51 return 'Mozilla/5.0 (Macintosh; Intel Mac OS X; Macintosh; ${locale})'; |
| 52 } else if (Platform.isWindows) { |
| 53 return 'Mozilla/5.0 (Windows; Windows; Windows; ${locale})'; |
| 54 } else if (Platform.isLinux) { |
| 55 return 'Mozilla/5.0 (Linux; Linux; Linux; ${locale})'; |
| 56 } else { |
| 57 // Dart/1.8.0 (macos; macos; macos; en_US) |
| 58 String os = Platform.operatingSystem; |
| 59 return "Dart/${getDartVersion()} (${os}; ${os}; ${os}; ${locale})"; |
| 60 } |
| 61 } |
| 62 |
| 63 String userHomeDir() { |
24 String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME'; | 64 String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME'; |
25 String value = Platform.environment[envKey]; | 65 String value = Platform.environment[envKey]; |
26 return value == null ? '.' : value; | 66 return value == null ? '.' : value; |
27 } | 67 } |
28 | 68 |
29 String _dartVersion() { | 69 String getDartVersion() { |
30 String ver = Platform.version; | 70 String ver = Platform.version; |
31 int index = ver.indexOf(' '); | 71 int index = ver.indexOf(' '); |
32 if (index != -1) ver = ver.substring(0, index); | 72 if (index != -1) ver = ver.substring(0, index); |
33 return ver; | 73 return ver; |
34 } | 74 } |
35 | 75 |
36 class IOPostHandler extends PostHandler { | 76 class IOPostHandler extends PostHandler { |
37 final String _userAgent; | 77 final String _userAgent; |
38 final HttpClient mockClient; | 78 final HttpClient mockClient; |
39 | 79 |
40 IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent(); | 80 HttpClient _client; |
41 | 81 |
42 Future sendPost(String url, Map<String, dynamic> parameters) { | 82 IOPostHandler({this.mockClient}) : _userAgent = _createUserAgent(); |
43 // Add custom parameters for OS and the Dart version. | |
44 parameters['cd1'] = Platform.operatingSystem; | |
45 parameters['cd2'] = 'dart ${_dartVersion()}'; | |
46 | 83 |
| 84 @override |
| 85 Future sendPost(String url, Map<String, dynamic> parameters) async { |
47 String data = postEncode(parameters); | 86 String data = postEncode(parameters); |
48 | 87 |
49 HttpClient client = mockClient != null ? mockClient : new HttpClient(); | 88 if (_client == null) { |
50 client.userAgent = _userAgent; | 89 _client = mockClient != null ? mockClient : new HttpClient(); |
51 return client.postUrl(Uri.parse(url)).then((HttpClientRequest req) { | 90 _client.userAgent = _userAgent; |
| 91 } |
| 92 |
| 93 try { |
| 94 HttpClientRequest req = await _client.postUrl(Uri.parse(url)); |
52 req.write(data); | 95 req.write(data); |
53 return req.close(); | 96 HttpClientResponse response = await req.close(); |
54 }).then((HttpClientResponse response) { | |
55 response.drain(); | 97 response.drain(); |
56 }).catchError((e) { | 98 } catch (exception) { |
57 // Catch errors that can happen during a request, but that we can't do | 99 // Catch errors that can happen during a request, but that we can't do |
58 // anything about, e.g. a missing internet conenction. | 100 // anything about, e.g. a missing internet connection. |
59 }); | 101 } |
60 } | 102 } |
| 103 |
| 104 @override |
| 105 void close() => _client?.close(); |
61 } | 106 } |
62 | 107 |
| 108 JsonEncoder _jsonEncoder = new JsonEncoder.withIndent(' '); |
| 109 |
63 class IOPersistentProperties extends PersistentProperties { | 110 class IOPersistentProperties extends PersistentProperties { |
64 File _file; | 111 File _file; |
65 Map _map; | 112 Map _map; |
66 | 113 |
67 IOPersistentProperties(String name) : super(name) { | 114 IOPersistentProperties(String name, {String documentDirPath}) : super(name) { |
68 String fileName = '.${name.replaceAll(' ', '_')}'; | 115 String fileName = '.${name.replaceAll(' ', '_')}'; |
69 _file = new File(path.join(_userHomeDir(), fileName)); | 116 documentDirPath ??= userHomeDir(); |
70 _file.createSync(); | 117 _file = new File(path.join(documentDirPath, fileName)); |
71 String contents = _file.readAsStringSync(); | 118 if (!_file.existsSync()) { |
72 if (contents.isEmpty) contents = '{}'; | 119 _file.createSync(); |
73 _map = JSON.decode(contents); | 120 } |
| 121 syncSettings(); |
74 } | 122 } |
75 | 123 |
76 dynamic operator[](String key) => _map[key]; | 124 IOPersistentProperties.fromFile(File file) : super(path.basename(file.path)) { |
| 125 _file = file; |
| 126 if (!_file.existsSync()) { |
| 127 _file.createSync(); |
| 128 } |
| 129 syncSettings(); |
| 130 } |
77 | 131 |
78 void operator[]=(String key, dynamic value) { | 132 @override |
| 133 dynamic operator [](String key) => _map[key]; |
| 134 |
| 135 @override |
| 136 void operator []=(String key, dynamic value) { |
| 137 if (value == null && !_map.containsKey(key)) return; |
| 138 if (_map[key] == value) return; |
| 139 |
79 if (value == null) { | 140 if (value == null) { |
80 _map.remove(key); | 141 _map.remove(key); |
81 } else { | 142 } else { |
82 _map[key] = value; | 143 _map[key] = value; |
83 } | 144 } |
84 | 145 |
85 _file.writeAsStringSync(JSON.encode(_map) + '\n'); | 146 try { |
| 147 _file.writeAsStringSync(_jsonEncoder.convert(_map) + '\n'); |
| 148 } catch (_) {} |
| 149 } |
| 150 |
| 151 @override |
| 152 void syncSettings() { |
| 153 try { |
| 154 String contents = _file.readAsStringSync(); |
| 155 if (contents.isEmpty) contents = '{}'; |
| 156 _map = JSON.decode(contents); |
| 157 } catch (_) { |
| 158 _map = {}; |
| 159 } |
86 } | 160 } |
87 } | 161 } |
| 162 |
| 163 /// Return the string for the platform's locale; return's `null` if the locale |
| 164 /// can't be determined. |
| 165 String getPlatformLocale() { |
| 166 String locale = Platform.localeName; |
| 167 if (locale == null) return null; |
| 168 |
| 169 if (locale != null) { |
| 170 // Convert `en_US.UTF-8` to `en_US`. |
| 171 int index = locale.indexOf('.'); |
| 172 if (index != -1) locale = locale.substring(0, index); |
| 173 |
| 174 // Convert `en_US` to `en-us`. |
| 175 locale = locale.replaceAll('_', '-').toLowerCase(); |
| 176 } |
| 177 |
| 178 return locale; |
| 179 } |
OLD | NEW |