Index: packages/usage/lib/src/usage_impl_io.dart |
diff --git a/packages/usage/lib/src/usage_impl_io.dart b/packages/usage/lib/src/usage_impl_io.dart |
index d59793e402b592a2059fd1632899dfd45661b8bf..6e82d410567bc7ea6bdb756cbb35488485732ded 100644 |
--- a/packages/usage/lib/src/usage_impl_io.dart |
+++ b/packages/usage/lib/src/usage_impl_io.dart |
@@ -2,31 +2,71 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-library usage_impl_io; |
- |
import 'dart:async'; |
-import 'dart:convert' show JSON; |
+import 'dart:convert' show JSON, JsonEncoder; |
import 'dart:io'; |
import 'package:path/path.dart' as path; |
import 'usage_impl.dart'; |
+/// An interface to a Google Analytics session, suitable for use in command-line |
+/// applications. |
+/// |
+/// `trackingId`, `applicationName`, and `applicationVersion` values should be supplied. |
+/// `analyticsUrl` is optional, and lets user's substitute their own analytics URL for |
+/// the default. |
+/// |
+/// `documentDirectory` is where the analytics settings are stored. It |
+/// defaults to the user home directory. For regular `dart:io` apps this doesn't need to |
+/// be supplied. For Flutter applications, you should pass in a value like |
+/// `PathProvider.getApplicationDocumentsDirectory()`. |
+class AnalyticsIO extends AnalyticsImpl { |
+ AnalyticsIO( |
+ String trackingId, String applicationName, String applicationVersion, |
+ {String analyticsUrl, Directory documentDirectory}) |
+ : super( |
+ trackingId, |
+ new IOPersistentProperties(applicationName, |
+ documentDirPath: documentDirectory?.path), |
+ new IOPostHandler(), |
+ applicationName: applicationName, |
+ applicationVersion: applicationVersion, |
+ analyticsUrl: analyticsUrl) { |
+ final String locale = getPlatformLocale(); |
+ if (locale != null) { |
+ setSessionValue('ul', locale); |
+ } |
+ } |
+} |
+ |
String _createUserAgent() { |
- // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) |
- // Dart/1.8.0-edge.41170 (macos; macos; macos; null) |
- String os = Platform.operatingSystem; |
- String locale = Platform.environment['LANG']; |
- return "Dart/${_dartVersion()} (${os}; ${os}; ${os}; ${locale})"; |
+ final String locale = getPlatformLocale() ?? ''; |
+ |
+ if (Platform.isAndroid) { |
+ return 'Mozilla/5.0 (Android; Mobile; ${locale})'; |
+ } else if (Platform.isIOS) { |
+ return 'Mozilla/5.0 (iPhone; U; CPU iPhone OS like Mac OS X; ${locale})'; |
+ } else if (Platform.isMacOS) { |
+ return 'Mozilla/5.0 (Macintosh; Intel Mac OS X; Macintosh; ${locale})'; |
+ } else if (Platform.isWindows) { |
+ return 'Mozilla/5.0 (Windows; Windows; Windows; ${locale})'; |
+ } else if (Platform.isLinux) { |
+ return 'Mozilla/5.0 (Linux; Linux; Linux; ${locale})'; |
+ } else { |
+ // Dart/1.8.0 (macos; macos; macos; en_US) |
+ String os = Platform.operatingSystem; |
+ return "Dart/${getDartVersion()} (${os}; ${os}; ${os}; ${locale})"; |
+ } |
} |
-String _userHomeDir() { |
+String userHomeDir() { |
String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME'; |
String value = Platform.environment[envKey]; |
return value == null ? '.' : value; |
} |
-String _dartVersion() { |
+String getDartVersion() { |
String ver = Platform.version; |
int index = ver.indexOf(' '); |
if (index != -1) ver = ver.substring(0, index); |
@@ -37,51 +77,103 @@ class IOPostHandler extends PostHandler { |
final String _userAgent; |
final HttpClient mockClient; |
- IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent(); |
+ HttpClient _client; |
- Future sendPost(String url, Map<String, dynamic> parameters) { |
- // Add custom parameters for OS and the Dart version. |
- parameters['cd1'] = Platform.operatingSystem; |
- parameters['cd2'] = 'dart ${_dartVersion()}'; |
+ IOPostHandler({this.mockClient}) : _userAgent = _createUserAgent(); |
+ @override |
+ Future sendPost(String url, Map<String, dynamic> parameters) async { |
String data = postEncode(parameters); |
- HttpClient client = mockClient != null ? mockClient : new HttpClient(); |
- client.userAgent = _userAgent; |
- return client.postUrl(Uri.parse(url)).then((HttpClientRequest req) { |
+ if (_client == null) { |
+ _client = mockClient != null ? mockClient : new HttpClient(); |
+ _client.userAgent = _userAgent; |
+ } |
+ |
+ try { |
+ HttpClientRequest req = await _client.postUrl(Uri.parse(url)); |
req.write(data); |
- return req.close(); |
- }).then((HttpClientResponse response) { |
+ HttpClientResponse response = await req.close(); |
response.drain(); |
- }).catchError((e) { |
+ } catch (exception) { |
// Catch errors that can happen during a request, but that we can't do |
- // anything about, e.g. a missing internet conenction. |
- }); |
+ // anything about, e.g. a missing internet connection. |
+ } |
} |
+ |
+ @override |
+ void close() => _client?.close(); |
} |
+JsonEncoder _jsonEncoder = new JsonEncoder.withIndent(' '); |
+ |
class IOPersistentProperties extends PersistentProperties { |
File _file; |
Map _map; |
- IOPersistentProperties(String name) : super(name) { |
+ IOPersistentProperties(String name, {String documentDirPath}) : super(name) { |
String fileName = '.${name.replaceAll(' ', '_')}'; |
- _file = new File(path.join(_userHomeDir(), fileName)); |
- _file.createSync(); |
- String contents = _file.readAsStringSync(); |
- if (contents.isEmpty) contents = '{}'; |
- _map = JSON.decode(contents); |
+ documentDirPath ??= userHomeDir(); |
+ _file = new File(path.join(documentDirPath, fileName)); |
+ if (!_file.existsSync()) { |
+ _file.createSync(); |
+ } |
+ syncSettings(); |
} |
- dynamic operator[](String key) => _map[key]; |
+ IOPersistentProperties.fromFile(File file) : super(path.basename(file.path)) { |
+ _file = file; |
+ if (!_file.existsSync()) { |
+ _file.createSync(); |
+ } |
+ syncSettings(); |
+ } |
+ |
+ @override |
+ dynamic operator [](String key) => _map[key]; |
+ |
+ @override |
+ void operator []=(String key, dynamic value) { |
+ if (value == null && !_map.containsKey(key)) return; |
+ if (_map[key] == value) return; |
- void operator[]=(String key, dynamic value) { |
if (value == null) { |
_map.remove(key); |
} else { |
_map[key] = value; |
} |
- _file.writeAsStringSync(JSON.encode(_map) + '\n'); |
+ try { |
+ _file.writeAsStringSync(_jsonEncoder.convert(_map) + '\n'); |
+ } catch (_) {} |
+ } |
+ |
+ @override |
+ void syncSettings() { |
+ try { |
+ String contents = _file.readAsStringSync(); |
+ if (contents.isEmpty) contents = '{}'; |
+ _map = JSON.decode(contents); |
+ } catch (_) { |
+ _map = {}; |
+ } |
+ } |
+} |
+ |
+/// Return the string for the platform's locale; return's `null` if the locale |
+/// can't be determined. |
+String getPlatformLocale() { |
+ String locale = Platform.localeName; |
+ if (locale == null) return null; |
+ |
+ if (locale != null) { |
+ // Convert `en_US.UTF-8` to `en_US`. |
+ int index = locale.indexOf('.'); |
+ if (index != -1) locale = locale.substring(0, index); |
+ |
+ // Convert `en_US` to `en-us`. |
+ locale = locale.replaceAll('_', '-').toLowerCase(); |
} |
+ |
+ return locale; |
} |