Merge pull request #74 from dart-lang/2_1_0

2.1.0
diff --git a/.analysis_options b/.analysis_options
index 7c8153d..c071edc 100644
--- a/.analysis_options
+++ b/.analysis_options
@@ -2,3 +2,5 @@
   strong-mode: true
   language:
     enableConditionalDirectives: true
+  exclude:
+    - example/flutter_example/**
diff --git a/changelog.md b/changelog.md
index 5343a35..e6fcc6d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 2.1.0-dev
+- added `Analytics.getSessionValue()`
+- added `Analytics.onSend`
+- added `AnalyticsImpl.sendRaw()`
+
 ## 2.0.0
 - added a `usage` implementation for Flutter (uses conditional directives)
 - removed `lib/usage_html.dart`; use the new Analytics.create() static method
diff --git a/lib/src/usage_impl.dart b/lib/src/usage_impl.dart
index f726195..18191e8 100644
--- a/lib/src/usage_impl.dart
+++ b/lib/src/usage_impl.dart
@@ -76,6 +76,8 @@
 
   String _url;
 
+  StreamController<Map<String, dynamic>> _sendController = new StreamController.broadcast(sync: true);
+
   AnalyticsImpl(
     this.trackingId,
     this.properties,
@@ -155,6 +157,8 @@
     return _sendPayload('exception', args);
   }
 
+  dynamic getSessionValue(String param) => _variableMap[param];
+
   void setSessionValue(String param, dynamic value) {
     if (value == null) {
       _variableMap.remove(param);
@@ -163,6 +167,8 @@
     }
   }
 
+  Stream<Map<String, dynamic>> get onSend => _sendController.stream;
+
   Future waitForLastPing({Duration timeout}) {
     Future f = Future.wait(_futures).catchError((e) => null);
 
@@ -184,8 +190,21 @@
     }
   }
 
-  // Valid values for [hitType] are: 'pageview', 'screenview', 'event',
-  // 'transaction', 'item', 'social', 'exception', and 'timing'.
+  /**
+   * Send raw data to analytics. Callers should generally use one of the typed
+   * methods (`sendScreenView`, `sendEvent`, ...).
+   *
+   * Valid values for [hitType] are: 'pageview', 'screenview', 'event',
+   * 'transaction', 'item', 'social', 'exception', and 'timing'.
+   */
+  Future sendRaw(String hitType, Map<String, dynamic> args) {
+    return _sendPayload(hitType, args);
+  }
+
+  /**
+   * Valid values for [hitType] are: 'pageview', 'screenview', 'event',
+   * 'transaction', 'item', 'social', 'exception', and 'timing'.
+   */
   Future _sendPayload(String hitType, Map<String, dynamic> args) {
     if (_bucket.removeDrop()) {
       _initClientId();
@@ -199,6 +218,8 @@
       args['cid'] = _clientId;
       args['t'] = hitType;
 
+      _sendController.add(args);
+
       return _recordFuture(postHandler.sendPost(_url, args));
     } else {
       return new Future.value();
diff --git a/lib/usage.dart b/lib/usage.dart
index dd5c32b..e46e819 100644
--- a/lib/usage.dart
+++ b/lib/usage.dart
@@ -114,6 +114,11 @@
   Future sendException(String description, {bool fatal});
 
   /**
+   * Gets a session variable value.
+   */
+  dynamic getSessionValue(String param);
+
+  /**
    * Sets a session variable value. The value is persistent for the life of the
    * [Analytics] instance. This variable will be sent in with every analytics
    * hit. A list of valid variable names can be found here:
@@ -122,6 +127,16 @@
   void setSessionValue(String param, dynamic value);
 
   /**
+   * Fires events when the usage library sends any data over the network. This
+   * will not fire if analytics has been disabled or if the throttling algorithim
+   * has been engaged.
+   *
+   * This method is public to allow library clients to more easily test their
+   * analytics implementations.
+   */
+  Stream<Map<String, dynamic>> get onSend;
+
+  /**
    * Wait for all of the outstanding analytics pings to complete. The returned
    * `Future` will always complete without errors. You can pass in an optional
    * `Duration` to specify to only wait for a certain amount of time.
@@ -186,6 +201,9 @@
   bool optIn = false;
   bool hasSetOptIn = true;
 
+  /// Events are never added to this controller for the mock implementation.
+  StreamController<Map<String, dynamic>> _sendController = new StreamController.broadcast();
+
   /**
    * Create a new [AnalyticsMock]. If [logCalls] is true, all calls will be
    * logged to stdout.
@@ -218,8 +236,12 @@
   Future sendException(String description, {bool fatal}) =>
       _log('exception', {'description': description, 'fatal': fatal});
 
+  dynamic getSessionValue(String param) => null;
+
   void setSessionValue(String param, dynamic value) { }
 
+  Stream<Map<String, dynamic>> get onSend => _sendController.stream;
+
   Future waitForLastPing({Duration timeout}) => new Future.value();
 
   Future _log(String hitType, Map m) {
diff --git a/pubspec.yaml b/pubspec.yaml
index 1e6d3e0..b63c159 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: usage
-version: 2.0.0
+version: 2.1.0-dev
 description: A Google Analytics wrapper for both command-line, web, and Flutter apps.
 homepage: https://github.com/dart-lang/usage
 author: Dart Team <misc@dartlang.org>