Merge pull request #111 from dart-lang/usage_3_2_0

expose additional fields on Analytics
diff --git a/lib/src/usage_impl.dart b/lib/src/usage_impl.dart
index 27cee22..e8e10d2 100644
--- a/lib/src/usage_impl.dart
+++ b/lib/src/usage_impl.dart
@@ -120,15 +120,22 @@
     properties['enabled'] = value;
   }
 
-  Future sendScreenView(String viewName) {
+  Future sendScreenView(String viewName, {Map<String, String> parameters}) {
     Map<String, dynamic> args = {'cd': viewName};
+    if (parameters != null) {
+      args.addAll(parameters);
+    }
     return _sendPayload('screenview', args);
   }
 
-  Future sendEvent(String category, String action, {String label, int value}) {
+  Future sendEvent(String category, String action, {String label, int value,
+      Map<String, String> parameters}) {
     Map<String, dynamic> args = {'ec': category, 'ea': action};
     if (label != null) args['el'] = label;
     if (value != null) args['ev'] = value;
+    if (parameters != null) {
+      args.addAll(parameters);
+    }
     return _sendPayload('event', args);
   }
 
diff --git a/lib/usage.dart b/lib/usage.dart
index d60e372..d516172 100644
--- a/lib/usage.dart
+++ b/lib/usage.dart
@@ -83,14 +83,21 @@
 
   /**
    * Sends a screen view hit to Google Analytics.
+   * 
+   * [parameters] can be any analytics key/value pair. Useful
+   * for custom dimensions, etc.
    */
-  Future sendScreenView(String viewName);
+  Future sendScreenView(String viewName, {Map<String, String> parameters});
 
   /**
    * Sends an Event hit to Google Analytics. [label] specifies the event label.
    * [value] specifies the event value. Values must be non-negative.
+   * 
+   * [parameters] can be any analytics key/value pair. Useful
+   * for custom dimensions, etc.
    */
-  Future sendEvent(String category, String action, {String label, int value});
+  Future sendEvent(String category, String action, {String label, int value,
+      Map<String, String> parameters});
 
   /**
    * Sends a Social hit to Google Analytics. [network] specifies the social
@@ -245,16 +252,21 @@
   @override
   String get clientId => '00000000-0000-4000-0000-000000000000';
 
-  Future sendScreenView(String viewName) =>
-      _log('screenView', {'viewName': viewName});
+  Future sendScreenView(String viewName, {Map<String, String> parameters}) {
+    parameters ??= <String, String>{};
+    parameters['viewName'] = viewName;
+    return _log('screenView', parameters);
+  }
 
-  Future sendEvent(String category, String action, {String label, int value}) {
+  Future sendEvent(String category, String action, {String label, int value,
+      Map<String, String> parameters}) {
+    parameters ??= <String, String>{};
     return _log('event', {
       'category': category,
       'action': action,
       'label': label,
       'value': value
-    });
+    }..addAll(parameters));
   }
 
   Future sendSocial(String network, String action, String target) =>
diff --git a/readme.md b/readme.md
index b348773..c9a92c7 100644
--- a/readme.md
+++ b/readme.md
@@ -110,6 +110,12 @@
 For more information, please see the Google Analytics Measurement Protocol
 [Policy](https://developers.google.com/analytics/devguides/collection/protocol/policy).
 
+## Contributing
+
+Run the tests with `pub run test`.
+
+Analyze the code with `dartanalyzer lib/*.dart test/*.dart`.
+
 ## Issues and bugs
 
 Please file reports on the
diff --git a/test/hit_types_test.dart b/test/hit_types_test.dart
index 7aab2d3..e8bf241 100644
--- a/test/hit_types_test.dart
+++ b/test/hit_types_test.dart
@@ -21,6 +21,13 @@
       expect(mock.mockProperties['clientId'], isNotNull);
       expect(mock.mockPostHandler.sentValues, isNot(isEmpty));
     });
+    test('with parameters', () {
+      AnalyticsImplMock mock = createMock();
+      mock.sendScreenView('withParams', parameters: {'cd1': 'foo'});
+      expect(mock.mockProperties['clientId'], isNotNull);
+      expect(mock.mockPostHandler.sentValues, isNot(isEmpty));
+      has(mock.last, 'cd1');
+    });
   });
 
   group('event', () {
@@ -33,6 +40,16 @@
       has(mock.last, 'ea');
     });
 
+    test('with parameters', () {
+      AnalyticsImplMock mock = createMock();
+      mock.sendEvent('withParams', 'save', parameters: {'cd1': 'foo'});
+      expect(mock.mockPostHandler.sentValues, isNot(isEmpty));
+      was(mock.last, 'event');
+      has(mock.last, 'ec');
+      has(mock.last, 'ea');
+      has(mock.last, 'cd1');
+    });
+
     test('optional args', () {
       AnalyticsImplMock mock = createMock();
       mock.sendEvent('files', 'save', label: 'File Save', value: 23);
diff --git a/test/usage_test.dart b/test/usage_test.dart
index 8c4cedb..9d2a4e0 100644
--- a/test/usage_test.dart
+++ b/test/usage_test.dart
@@ -14,7 +14,9 @@
     test('simple', () {
       AnalyticsMock mock = new AnalyticsMock();
       mock.sendScreenView('main');
+      mock.sendScreenView('withParameters', parameters: {'cd1': 'custom'});
       mock.sendEvent('files', 'save');
+      mock.sendEvent('eventWithParameters', 'save', parameters: {'cd1': 'custom'});
       mock.sendSocial('g+', 'plus', 'userid');
       mock.sendTiming('compile', 123);
       mock.startTimer('compile').finish();