| // Copyright 2017 The Chromium 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 'dart:async'; | 
 |  | 
 | import 'package:flutter/foundation.dart'; | 
 | import 'package:flutter/services.dart'; | 
 | import 'package:meta/meta.dart'; | 
 |  | 
 | const MethodChannel firebaseChannel = | 
 |     MethodChannel('plugins.flutter.io/firebase_analytics'); | 
 |  | 
 | /// Firebase Analytics API. | 
 | class FirebaseAnalytics { | 
 |   final MethodChannel _channel = firebaseChannel; | 
 |  | 
 |   /// Namespace for analytics API available on Android only. | 
 |   /// | 
 |   /// The value of this field is `null` on non-Android platforms. If you are | 
 |   /// writing cross-platform code, consider using null-aware operator when | 
 |   /// accessing it. | 
 |   /// | 
 |   /// Example: | 
 |   /// | 
 |   ///     FirebaseAnalytics analytics = FirebaseAnalytics(); | 
 |   ///     analytics.android?.setSessionTimeoutDuration(true); | 
 |   final FirebaseAnalyticsAndroid android = | 
 |       defaultTargetPlatform == TargetPlatform.android | 
 |           ? FirebaseAnalyticsAndroid() | 
 |           : null; | 
 |  | 
 |   /// Logs a custom Flutter Analytics event with the given [name] and event [parameters]. | 
 |   Future<void> logEvent( | 
 |       {@required String name, Map<String, dynamic> parameters}) async { | 
 |     if (_reservedEventNames.contains(name)) { | 
 |       throw ArgumentError.value( | 
 |           name, 'name', 'Event name is reserved and cannot be used'); | 
 |     } | 
 |  | 
 |     const String kReservedPrefix = 'firebase_'; | 
 |  | 
 |     if (name.startsWith(kReservedPrefix)) { | 
 |       throw ArgumentError.value(name, 'name', | 
 |           'Prefix "$kReservedPrefix" is reserved and cannot be used.'); | 
 |     } | 
 |  | 
 |     await _channel.invokeMethod<void>('logEvent', <String, dynamic>{ | 
 |       'name': name, | 
 |       'parameters': parameters, | 
 |     }); | 
 |   } | 
 |  | 
 |   /// Sets whether analytics collection is enabled for this app on this device. | 
 |   /// | 
 |   /// This setting is persisted across app sessions. By default it is enabled. | 
 |   Future<void> setAnalyticsCollectionEnabled(bool enabled) async { | 
 |     if (enabled == null) { | 
 |       throw ArgumentError.notNull('enabled'); | 
 |     } | 
 |  | 
 |     await _channel.invokeMethod<void>('setAnalyticsCollectionEnabled', enabled); | 
 |   } | 
 |  | 
 |   /// Sets the user ID property. | 
 |   /// | 
 |   /// This feature must be used in accordance with [Google's Privacy Policy][1]. | 
 |   /// | 
 |   /// [1]: https://www.google.com/policies/privacy/ | 
 |   Future<void> setUserId(String id) async { | 
 |     await _channel.invokeMethod<void>('setUserId', id); | 
 |   } | 
 |  | 
 |   /// Sets the current [screenName], which specifies the current visual context | 
 |   /// in your app. | 
 |   /// | 
 |   /// This helps identify the areas in your app where users spend their time | 
 |   /// and how they interact with your app. | 
 |   /// | 
 |   /// The class name can optionally be overridden by the [screenClassOverride] | 
 |   /// parameter. | 
 |   /// | 
 |   /// The [screenName] and [screenClassOverride] remain in effect until the | 
 |   /// current `Activity` (in Android) or `UIViewController` (in iOS) changes or | 
 |   /// a new call to [setCurrentScreen] is made. | 
 |   /// | 
 |   /// See also: | 
 |   /// | 
 |   /// https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.html#setCurrentScreen(android.app.Activity, java.lang.String, java.lang.String) | 
 |   /// https://firebase.google.com/docs/reference/ios/firebaseanalytics/api/reference/Classes/FIRAnalytics#setscreennamescreenclass | 
 |   Future<void> setCurrentScreen( | 
 |       {@required String screenName, | 
 |       String screenClassOverride = 'Flutter'}) async { | 
 |     if (screenName == null) { | 
 |       throw ArgumentError.notNull('screenName'); | 
 |     } | 
 |  | 
 |     await _channel.invokeMethod<void>('setCurrentScreen', <String, String>{ | 
 |       'screenName': screenName, | 
 |       'screenClassOverride': screenClassOverride, | 
 |     }); | 
 |   } | 
 |  | 
 |   static final RegExp _nonAlphaNumeric = RegExp(r'[^a-zA-Z0-9_]'); | 
 |   static final RegExp _alpha = RegExp(r'[a-zA-Z]'); | 
 |  | 
 |   /// Sets a user property to a given value. | 
 |   /// | 
 |   /// Up to 25 user property names are supported. Once set, user property | 
 |   /// values persist throughout the app lifecycle and across sessions. | 
 |   /// | 
 |   /// [name] is the name of the user property to set. Should contain 1 to 24 | 
 |   /// alphanumeric characters or underscores and must start with an alphabetic | 
 |   /// character. The "firebase_" prefix is reserved and should not be used for | 
 |   /// user property names. | 
 |   Future<void> setUserProperty( | 
 |       {@required String name, @required String value}) async { | 
 |     if (name == null) { | 
 |       throw ArgumentError.notNull('name'); | 
 |     } | 
 |  | 
 |     if (name.isEmpty || | 
 |         name.length > 24 || | 
 |         name.indexOf(_alpha) != 0 || | 
 |         name.contains(_nonAlphaNumeric)) | 
 |       throw ArgumentError.value( | 
 |           name, 'name', 'must contain 1 to 24 alphanumeric characters.'); | 
 |  | 
 |     if (name.startsWith('firebase_')) | 
 |       throw ArgumentError.value(name, 'name', '"firebase_" prefix is reserved'); | 
 |  | 
 |     await _channel.invokeMethod<void>('setUserProperty', <String, String>{ | 
 |       'name': name, | 
 |       'value': value, | 
 |     }); | 
 |   } | 
 |  | 
 |   /// Clears all analytics data for this app from the device and resets the app instance id. | 
 |   Future<void> resetAnalyticsData() async { | 
 |     await _channel.invokeMethod<void>('resetAnalyticsData'); | 
 |   } | 
 |  | 
 |   /// Logs the standard `add_payment_info` event. | 
 |   /// | 
 |   /// This event signifies that a user has submitted their payment information | 
 |   /// to your app. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#ADD_PAYMENT_INFO | 
 |   Future<void> logAddPaymentInfo() { | 
 |     return logEvent(name: 'add_payment_info'); | 
 |   } | 
 |  | 
 |   /// Logs the standard `add_to_cart` event. | 
 |   /// | 
 |   /// This event signifies that an item was added to a cart for purchase. Add | 
 |   /// this event to a funnel with [logEcommercePurchase] to gauge the | 
 |   /// effectiveness of your checkout process. Note: If you supply the | 
 |   /// [value] parameter, you must also supply the [currency] parameter so that | 
 |   /// revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#ADD_TO_CART | 
 |   Future<void> logAddToCart({ | 
 |     @required String itemId, | 
 |     @required String itemName, | 
 |     @required String itemCategory, | 
 |     @required int quantity, | 
 |     double price, | 
 |     double value, | 
 |     String currency, | 
 |     String origin, | 
 |     String itemLocationId, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'add_to_cart', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_ID: itemId, | 
 |         _ITEM_NAME: itemName, | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |         _QUANTITY: quantity, | 
 |         _PRICE: price, | 
 |         _VALUE: value, | 
 |         _CURRENCY: currency, | 
 |         _ORIGIN: origin, | 
 |         _ITEM_LOCATION_ID: itemLocationId, | 
 |         _DESTINATION: destination, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `add_to_wishlist` event. | 
 |   /// | 
 |   /// This event signifies that an item was added to a wishlist. Use this event | 
 |   /// to identify popular gift items in your app. Note: If you supply the | 
 |   /// [value] parameter, you must also supply the [currency] parameter so that | 
 |   /// revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#ADD_TO_WISHLIST | 
 |   Future<void> logAddToWishlist({ | 
 |     @required String itemId, | 
 |     @required String itemName, | 
 |     @required String itemCategory, | 
 |     @required int quantity, | 
 |     double price, | 
 |     double value, | 
 |     String currency, | 
 |     String itemLocationId, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'add_to_wishlist', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_ID: itemId, | 
 |         _ITEM_NAME: itemName, | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |         _QUANTITY: quantity, | 
 |         _PRICE: price, | 
 |         _VALUE: value, | 
 |         _CURRENCY: currency, | 
 |         _ITEM_LOCATION_ID: itemLocationId, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `app_open` event. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#APP_OPEN | 
 |   Future<void> logAppOpen() { | 
 |     return logEvent(name: 'app_open'); | 
 |   } | 
 |  | 
 |   /// Logs the standard `begin_checkout` event. | 
 |   /// | 
 |   /// This event signifies that a user has begun the process of checking out. | 
 |   /// Add this event to a funnel with your [logEcommercePurchase] event to | 
 |   /// gauge the effectiveness of your checkout process. Note: If you supply the | 
 |   /// [value] parameter, you must also supply the [currency] parameter so that | 
 |   /// revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#BEGIN_CHECKOUT | 
 |   Future<void> logBeginCheckout({ | 
 |     double value, | 
 |     String currency, | 
 |     String transactionId, | 
 |     int numberOfNights, | 
 |     int numberOfRooms, | 
 |     int numberOfPassengers, | 
 |     String origin, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |     String travelClass, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'begin_checkout', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _VALUE: value, | 
 |         _CURRENCY: currency, | 
 |         _TRANSACTION_ID: transactionId, | 
 |         _NUMBER_OF_NIGHTS: numberOfNights, | 
 |         _NUMBER_OF_ROOMS: numberOfRooms, | 
 |         _NUMBER_OF_PASSENGERS: numberOfPassengers, | 
 |         _ORIGIN: origin, | 
 |         _DESTINATION: destination, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |         _TRAVEL_CLASS: travelClass, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `campaign_details` event. | 
 |   /// | 
 |   /// Log this event to supply the referral details of a re-engagement campaign. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#CAMPAIGN_DETAILS | 
 |   Future<void> logCampaignDetails({ | 
 |     @required String source, | 
 |     @required String medium, | 
 |     @required String campaign, | 
 |     String term, | 
 |     String content, | 
 |     String aclid, | 
 |     String cp1, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'campaign_details', | 
 |       parameters: filterOutNulls(<String, String>{ | 
 |         _SOURCE: source, | 
 |         _MEDIUM: medium, | 
 |         _CAMPAIGN: campaign, | 
 |         _TERM: term, | 
 |         _CONTENT: content, | 
 |         _ACLID: aclid, | 
 |         _CP1: cp1, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `earn_virtual_currency` event. | 
 |   /// | 
 |   /// This event tracks the awarding of virtual currency in your app. Log this | 
 |   /// along with [logSpendVirtualCurrency] to better understand your virtual | 
 |   /// economy. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#EARN_VIRTUAL_CURRENCY | 
 |   Future<void> logEarnVirtualCurrency({ | 
 |     @required String virtualCurrencyName, | 
 |     @required num value, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'earn_virtual_currency', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _VIRTUAL_CURRENCY_NAME: virtualCurrencyName, | 
 |         _VALUE: value, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `ecommerce_purchase` event. | 
 |   /// | 
 |   /// This event signifies that an item was purchased by a user. Note: This is | 
 |   /// different from the in-app purchase event, which is reported automatically | 
 |   /// for Google Play-based apps. Note: If you supply the [value] parameter, | 
 |   /// you must also supply the [currency] parameter so that revenue metrics can | 
 |   /// be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#ECOMMERCE_PURCHASE | 
 |   Future<void> logEcommercePurchase({ | 
 |     String currency, | 
 |     double value, | 
 |     String transactionId, | 
 |     double tax, | 
 |     double shipping, | 
 |     String coupon, | 
 |     String location, | 
 |     int numberOfNights, | 
 |     int numberOfRooms, | 
 |     int numberOfPassengers, | 
 |     String origin, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |     String travelClass, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'ecommerce_purchase', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CURRENCY: currency, | 
 |         _VALUE: value, | 
 |         _TRANSACTION_ID: transactionId, | 
 |         _TAX: tax, | 
 |         _SHIPPING: shipping, | 
 |         _COUPON: coupon, | 
 |         _LOCATION: location, | 
 |         _NUMBER_OF_NIGHTS: numberOfNights, | 
 |         _NUMBER_OF_ROOMS: numberOfRooms, | 
 |         _NUMBER_OF_PASSENGERS: numberOfPassengers, | 
 |         _ORIGIN: origin, | 
 |         _DESTINATION: destination, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |         _TRAVEL_CLASS: travelClass, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `generate_lead` event. | 
 |   /// | 
 |   /// Log this event when a lead has been generated in the app to understand | 
 |   /// the efficacy of your install and re-engagement campaigns. Note: If you | 
 |   /// supply the [value] parameter, you must also supply the [currency] | 
 |   /// parameter so that revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#GENERATE_LEAD | 
 |   Future<void> logGenerateLead({ | 
 |     String currency, | 
 |     double value, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'generate_lead', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CURRENCY: currency, | 
 |         _VALUE: value, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `join_group` event. | 
 |   /// | 
 |   /// Log this event when a user joins a group such as a guild, team or family. | 
 |   /// Use this event to analyze how popular certain groups or social features | 
 |   /// are in your app. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#JOIN_GROUP | 
 |   Future<void> logJoinGroup({ | 
 |     @required String groupId, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'join_group', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _GROUP_ID: groupId, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `level_up` event. | 
 |   /// | 
 |   /// This event signifies that a player has leveled up in your gaming app. It | 
 |   /// can help you gauge the level distribution of your userbase and help you | 
 |   /// identify certain levels that are difficult to pass. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#LEVEL_UP | 
 |   Future<void> logLevelUp({ | 
 |     @required int level, | 
 |     String character, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'level_up', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _LEVEL: level, | 
 |         _CHARACTER: character, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `level_start` event. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#LEVEL_START | 
 |   Future<void> logLevelStart({ | 
 |     @required String levelName, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'level_start', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _LEVEL_NAME: levelName, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `level_end` event. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#LEVEL_END | 
 |   Future<void> logLevelEnd({ | 
 |     @required String levelName, | 
 |     int success, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'level_end', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _LEVEL_NAME: levelName, | 
 |         _SUCCESS: success, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `login` event. | 
 |   /// | 
 |   /// Apps with a login feature can report this event to signify that a user | 
 |   /// has logged in. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#LOGIN | 
 |   Future<void> logLogin({String loginMethod}) { | 
 |     return logEvent( | 
 |       name: 'login', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _METHOD: loginMethod, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `post_score` event. | 
 |   /// | 
 |   /// Log this event when the user posts a score in your gaming app. This event | 
 |   /// can help you understand how users are actually performing in your game | 
 |   /// and it can help you correlate high scores with certain audiences or | 
 |   /// behaviors. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#POST_SCORE | 
 |   Future<void> logPostScore({ | 
 |     @required int score, | 
 |     int level, | 
 |     String character, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'post_score', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _SCORE: score, | 
 |         _LEVEL: level, | 
 |         _CHARACTER: character, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `present_offer` event. | 
 |   /// | 
 |   /// This event signifies that the app has presented a purchase offer to a | 
 |   /// user. Add this event to a funnel with the [logAddToCart] and | 
 |   /// [logEcommercePurchase] to gauge your conversion process. Note: If you | 
 |   /// supply the [value] parameter, you must also supply the [currency] | 
 |   /// parameter so that revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#PRESENT_OFFER | 
 |   Future<void> logPresentOffer({ | 
 |     @required String itemId, | 
 |     @required String itemName, | 
 |     @required String itemCategory, | 
 |     @required int quantity, | 
 |     double price, | 
 |     double value, | 
 |     String currency, | 
 |     String itemLocationId, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'present_offer', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_ID: itemId, | 
 |         _ITEM_NAME: itemName, | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |         _QUANTITY: quantity, | 
 |         _PRICE: price, | 
 |         _VALUE: value, | 
 |         _CURRENCY: currency, | 
 |         _ITEM_LOCATION_ID: itemLocationId, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `purchase_refund` event. | 
 |   /// | 
 |   /// This event signifies that an item purchase was refunded. Note: If you | 
 |   /// supply the [value] parameter, you must also supply the [currency] | 
 |   /// parameter so that revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#PURCHASE_REFUND | 
 |   Future<void> logPurchaseRefund({ | 
 |     String currency, | 
 |     double value, | 
 |     String transactionId, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'purchase_refund', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CURRENCY: currency, | 
 |         _VALUE: value, | 
 |         _TRANSACTION_ID: transactionId, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `remove_from_cart` event. | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#REMOVE_FROM_CART | 
 |   Future<void> logRemoveFromCart({ | 
 |     @required String itemId, | 
 |     @required String itemName, | 
 |     @required String itemCategory, | 
 |     @required int quantity, | 
 |     double price, | 
 |     double value, | 
 |     String currency, | 
 |     String origin, | 
 |     String itemLocationId, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'remove_from_cart', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _QUANTITY: quantity, | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |         _ITEM_NAME: itemName, | 
 |         _ITEM_ID: itemId, | 
 |         _VALUE: value, | 
 |         _PRICE: price, | 
 |         _CURRENCY: currency, | 
 |         _ITEM_LOCATION_ID: itemLocationId, | 
 |         _ORIGIN: origin, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |         _DESTINATION: destination, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `search` event. | 
 |   /// | 
 |   /// Apps that support search features can use this event to contextualize | 
 |   /// search operations by supplying the appropriate, corresponding parameters. | 
 |   /// This event can help you identify the most popular content in your app. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SEARCH | 
 |   Future<void> logSearch({ | 
 |     @required String searchTerm, | 
 |     int numberOfNights, | 
 |     int numberOfRooms, | 
 |     int numberOfPassengers, | 
 |     String origin, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |     String travelClass, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'search', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _SEARCH_TERM: searchTerm, | 
 |         _NUMBER_OF_NIGHTS: numberOfNights, | 
 |         _NUMBER_OF_ROOMS: numberOfRooms, | 
 |         _NUMBER_OF_PASSENGERS: numberOfPassengers, | 
 |         _ORIGIN: origin, | 
 |         _DESTINATION: destination, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |         _TRAVEL_CLASS: travelClass, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `select_content` event. | 
 |   /// | 
 |   /// This general purpose event signifies that a user has selected some | 
 |   /// content of a certain type in an app. The content can be any object in | 
 |   /// your app. This event can help you identify popular content and categories | 
 |   /// of content in your app. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SELECT_CONTENT | 
 |   Future<void> logSelectContent({ | 
 |     @required String contentType, | 
 |     @required String itemId, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'select_content', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CONTENT_TYPE: contentType, | 
 |         _ITEM_ID: itemId, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `set_checkout_option` event. | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SET_CHECKOUT_OPTION | 
 |   Future<void> logSetCheckoutOption({ | 
 |     @required int checkoutStep, | 
 |     @required String checkoutOption, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'set_checkout_option', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CHECKOUT_STEP: checkoutStep, | 
 |         _CHECKOUT_OPTION: checkoutOption, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `share` event. | 
 |   /// | 
 |   /// Apps with social features can log the Share event to identify the most | 
 |   /// viral content. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SHARE | 
 |   Future<void> logShare({ | 
 |     @required String contentType, | 
 |     @required String itemId, | 
 |     @required String method, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'share', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _CONTENT_TYPE: contentType, | 
 |         _ITEM_ID: itemId, | 
 |         _METHOD: method, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `sign_up` event. | 
 |   /// | 
 |   /// This event indicates that a user has signed up for an account in your | 
 |   /// app. The parameter signifies the method by which the user signed up. Use | 
 |   /// this event to understand the different behaviors between logged in and | 
 |   /// logged out users. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SIGN_UP | 
 |   Future<void> logSignUp({ | 
 |     @required String signUpMethod, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'sign_up', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _METHOD: signUpMethod, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `spend_virtual_currency` event. | 
 |   /// | 
 |   /// This event tracks the sale of virtual goods in your app and can help you | 
 |   /// identify which virtual goods are the most popular objects of purchase. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#SPEND_VIRTUAL_CURRENCY | 
 |   Future<void> logSpendVirtualCurrency({ | 
 |     @required String itemName, | 
 |     @required String virtualCurrencyName, | 
 |     @required num value, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'spend_virtual_currency', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_NAME: itemName, | 
 |         _VIRTUAL_CURRENCY_NAME: virtualCurrencyName, | 
 |         _VALUE: value, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `tutorial_begin` event. | 
 |   /// | 
 |   /// This event signifies the start of the on-boarding process in your app. | 
 |   /// Use this in a funnel with [logTutorialComplete] to understand how many | 
 |   /// users complete this process and move on to the full app experience. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#TUTORIAL_BEGIN | 
 |   Future<void> logTutorialBegin() { | 
 |     return logEvent(name: 'tutorial_begin'); | 
 |   } | 
 |  | 
 |   /// Logs the standard `tutorial_complete` event. | 
 |   /// | 
 |   /// Use this event to signify the user's completion of your app's on-boarding | 
 |   /// process. Add this to a funnel with [logTutorialBegin] to gauge the | 
 |   /// completion rate of your on-boarding process. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#TUTORIAL_COMPLETE | 
 |   Future<void> logTutorialComplete() { | 
 |     return logEvent(name: 'tutorial_complete'); | 
 |   } | 
 |  | 
 |   /// Logs the standard `unlock_achievement` event with a given achievement | 
 |   /// [id]. | 
 |   /// | 
 |   /// Log this event when the user has unlocked an achievement in your game. | 
 |   /// Since achievements generally represent the breadth of a gaming | 
 |   /// experience, this event can help you understand how many users are | 
 |   /// experiencing all that your game has to offer. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#UNLOCK_ACHIEVEMENT | 
 |   Future<void> logUnlockAchievement({ | 
 |     @required String id, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'unlock_achievement', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ACHIEVEMENT_ID: id, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `view_item` event. | 
 |   /// | 
 |   /// This event signifies that some content was shown to the user. This | 
 |   /// content may be a product, a webpage or just a simple image or text. Use | 
 |   /// the appropriate parameters to contextualize the event. Use this event to | 
 |   /// discover the most popular items viewed in your app. Note: If you supply | 
 |   /// the [value] parameter, you must also supply the [currency] parameter so | 
 |   /// that revenue metrics can be computed accurately. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#VIEW_ITEM | 
 |   Future<void> logViewItem({ | 
 |     @required String itemId, | 
 |     @required String itemName, | 
 |     @required String itemCategory, | 
 |     String itemLocationId, | 
 |     double price, | 
 |     int quantity, | 
 |     String currency, | 
 |     double value, | 
 |     String flightNumber, | 
 |     int numberOfPassengers, | 
 |     int numberOfNights, | 
 |     int numberOfRooms, | 
 |     String origin, | 
 |     String destination, | 
 |     String startDate, | 
 |     String endDate, | 
 |     String searchTerm, | 
 |     String travelClass, | 
 |   }) { | 
 |     _requireValueAndCurrencyTogether(value, currency); | 
 |  | 
 |     return logEvent( | 
 |       name: 'view_item', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_ID: itemId, | 
 |         _ITEM_NAME: itemName, | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |         _ITEM_LOCATION_ID: itemLocationId, | 
 |         _PRICE: price, | 
 |         _QUANTITY: quantity, | 
 |         _CURRENCY: currency, | 
 |         _VALUE: value, | 
 |         _FLIGHT_NUMBER: flightNumber, | 
 |         _NUMBER_OF_PASSENGERS: numberOfPassengers, | 
 |         _NUMBER_OF_NIGHTS: numberOfNights, | 
 |         _NUMBER_OF_ROOMS: numberOfRooms, | 
 |         _ORIGIN: origin, | 
 |         _DESTINATION: destination, | 
 |         _START_DATE: startDate, | 
 |         _END_DATE: endDate, | 
 |         _SEARCH_TERM: searchTerm, | 
 |         _TRAVEL_CLASS: travelClass, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `view_item_list` event. | 
 |   /// | 
 |   /// Log this event when the user has been presented with a list of items of a | 
 |   /// certain category. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#VIEW_ITEM_LIST | 
 |   Future<void> logViewItemList({ | 
 |     @required String itemCategory, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'view_item_list', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _ITEM_CATEGORY: itemCategory, | 
 |       }), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Logs the standard `view_search_results` event. | 
 |   /// | 
 |   /// Log this event when the user has been presented with the results of a | 
 |   /// search. | 
 |   /// | 
 |   /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html#VIEW_SEARCH_RESULTS | 
 |   Future<void> logViewSearchResults({ | 
 |     @required String searchTerm, | 
 |   }) { | 
 |     return logEvent( | 
 |       name: 'view_search_results', | 
 |       parameters: filterOutNulls(<String, dynamic>{ | 
 |         _SEARCH_TERM: searchTerm, | 
 |       }), | 
 |     ); | 
 |   } | 
 | } | 
 |  | 
 | /// Android-specific analytics API. | 
 | class FirebaseAnalyticsAndroid { | 
 |   final MethodChannel _channel = firebaseChannel; | 
 |  | 
 |   /// Sets the duration of inactivity that terminates the current session. | 
 |   /// | 
 |   /// The default value is 1800000 (30 minutes). | 
 |   Future<void> setSessionTimeoutDuration(int milliseconds) async { | 
 |     if (milliseconds == null) { | 
 |       throw ArgumentError.notNull('milliseconds'); | 
 |     } | 
 |     await _channel.invokeMethod<void>( | 
 |         'setSessionTimeoutDuration', milliseconds); | 
 |   } | 
 | } | 
 |  | 
 | /// Creates a new map containing all of the key/value pairs from [parameters] | 
 | /// except those whose value is `null`. | 
 | @visibleForTesting | 
 | Map<String, dynamic> filterOutNulls(Map<String, dynamic> parameters) { | 
 |   final Map<String, dynamic> filtered = <String, dynamic>{}; | 
 |   parameters.forEach((String key, dynamic value) { | 
 |     if (value != null) { | 
 |       filtered[key] = value; | 
 |     } | 
 |   }); | 
 |   return filtered; | 
 | } | 
 |  | 
 | @visibleForTesting | 
 | const String valueAndCurrencyMustBeTogetherError = 'If you supply the "value" ' | 
 |     'parameter, you must also supply the "currency" parameter.'; | 
 |  | 
 | void _requireValueAndCurrencyTogether(double value, String currency) { | 
 |   if (value != null && currency == null) { | 
 |     throw ArgumentError(valueAndCurrencyMustBeTogetherError); | 
 |   } | 
 | } | 
 |  | 
 | /// Reserved event names that cannot be used. | 
 | /// | 
 | /// See: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event.html | 
 | const List<String> _reservedEventNames = <String>[ | 
 |   'ad_activeview', | 
 |   'ad_click', | 
 |   'ad_exposure', | 
 |   'ad_impression', | 
 |   'ad_query', | 
 |   'adunit_exposure', | 
 |   'app_clear_data', | 
 |   'app_uninstall', | 
 |   'app_update', | 
 |   'error', | 
 |   'first_open', | 
 |   'first_visit', | 
 |   'in_app_purchase', | 
 |   'notification_dismiss', | 
 |   'notification_foreground', | 
 |   'notification_open', | 
 |   'notification_receive', | 
 |   'os_update', | 
 |   'screen_view', | 
 |   'session_start', | 
 |   'user_engagement', | 
 | ]; | 
 |  | 
 | // The following constants are defined in: | 
 | // | 
 | // https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param.html | 
 |  | 
 | /// Game achievement ID. | 
 | const String _ACHIEVEMENT_ID = 'achievement_id'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` click ID. | 
 | const String _ACLID = 'aclid'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` name; used for keyword analysis to identify a specific | 
 | /// product promotion or strategic campaign. | 
 | const String _CAMPAIGN = 'campaign'; | 
 |  | 
 | /// Character used in game. | 
 | const String _CHARACTER = 'character'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` content; used for A/B testing and content-targeted ads to | 
 | /// differentiate ads or links that point to the same URL. | 
 | const String _CONTENT = 'content'; | 
 |  | 
 | /// Type of content selected. | 
 | const String _CONTENT_TYPE = 'content_type'; | 
 |  | 
 | /// Coupon code for a purchasable item. | 
 | const String _COUPON = 'coupon'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` custom parameter. | 
 | const String _CP1 = 'cp1'; | 
 |  | 
 | /// Purchase currency in 3 letter ISO_4217 format. | 
 | const String _CURRENCY = 'currency'; | 
 |  | 
 | /// Flight or Travel destination. | 
 | const String _DESTINATION = 'destination'; | 
 |  | 
 | /// The arrival date, check-out date, or rental end date for the item. | 
 | const String _END_DATE = 'end_date'; | 
 |  | 
 | /// Indicates that the associated event should either | 
 | /// extend the current session or start a new session | 
 | /// if no session was active when the event was logged. | 
 | // const String _EXTENDED_SESSION = 'extend_session'; | 
 |  | 
 | /// Flight number for travel events. | 
 | const String _FLIGHT_NUMBER = 'flight_number'; | 
 |  | 
 | /// Group/clan/guild id. | 
 | const String _GROUP_ID = 'group_id'; | 
 |  | 
 | /// Item category. | 
 | const String _ITEM_CATEGORY = 'item_category'; | 
 |  | 
 | /// Item ID. | 
 | const String _ITEM_ID = 'item_id'; | 
 |  | 
 | /// The Google Place ID that corresponds to the associated item. | 
 | const String _ITEM_LOCATION_ID = 'item_location_id'; | 
 |  | 
 | /// Item Brand. | 
 | // const String _ITEM_BRAND = 'item_brand'; | 
 |  | 
 | /// Item Variant. | 
 | // const String _ITEM_VARIANT = 'item_variant'; | 
 |  | 
 | /// The list in which the item was presented to the user. | 
 | // const String _ITEM_LIST = 'item_list'; | 
 |  | 
 | /// The checkout step (1..N). | 
 | const String _CHECKOUT_STEP = 'checkout_step'; | 
 |  | 
 | /// Some option on a step in an ecommerce flow. | 
 | const String _CHECKOUT_OPTION = 'checkout_option'; | 
 |  | 
 | /// The name of a creative used in a promotional spot. | 
 | // const String _CREATIVE_NAME = 'creative_name'; | 
 |  | 
 | /// The name of a creative slot. | 
 | // const String _CREATIVE_SLOT = 'creative_slot'; | 
 |  | 
 | /// The store or affiliation from which this transaction occurred. | 
 | // const String _AFFILIATION = 'affiliation'; | 
 |  | 
 | /// The index of an item in a list. | 
 | // const String _INDEX = 'index'; | 
 |  | 
 | /// Item name (String). | 
 | const String _ITEM_NAME = 'item_name'; | 
 |  | 
 | /// Level in game (long). | 
 | const String _LEVEL = 'level'; | 
 |  | 
 | /// The name of a level in a game (String). | 
 | const String _LEVEL_NAME = 'level_name'; | 
 |  | 
 | /// The result of an operation (long). | 
 | const String _SUCCESS = 'success'; | 
 |  | 
 | /// Location. | 
 | const String _LOCATION = 'location'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` medium; used to identify a medium such as email or | 
 | /// cost-per-click (cpc). | 
 | const String _MEDIUM = 'medium'; | 
 |  | 
 | /// Number of nights staying at hotel (long). | 
 | const String _NUMBER_OF_NIGHTS = 'number_of_nights'; | 
 |  | 
 | /// Number of passengers traveling (long). | 
 | const String _NUMBER_OF_PASSENGERS = 'number_of_passengers'; | 
 |  | 
 | /// Number of rooms for travel events (long). | 
 | const String _NUMBER_OF_ROOMS = 'number_of_rooms'; | 
 |  | 
 | /// Flight or Travel origin. | 
 | const String _ORIGIN = 'origin'; | 
 |  | 
 | /// Purchase price (double). | 
 | const String _PRICE = 'price'; | 
 |  | 
 | /// Purchase quantity (long). | 
 | const String _QUANTITY = 'quantity'; | 
 |  | 
 | /// Score in game (long). | 
 | const String _SCORE = 'score'; | 
 |  | 
 | /// The search string/keywords used. | 
 | const String _SEARCH_TERM = 'search_term'; | 
 |  | 
 | /// Shipping cost (double). | 
 | const String _SHIPPING = 'shipping'; | 
 |  | 
 | /// A particular approach used in an operation; for example, "facebook" or | 
 | /// "email" in the context of a sign_up or login event. | 
 | const String _METHOD = 'method'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` source; used to identify a search engine, newsletter, or | 
 | /// other source. | 
 | const String _SOURCE = 'source'; | 
 |  | 
 | /// The departure date, check-in date, or rental start date for the item. | 
 | const String _START_DATE = 'start_date'; | 
 |  | 
 | /// Tax amount (double). | 
 | const String _TAX = 'tax'; | 
 |  | 
 | /// `CAMPAIGN_DETAILS` term; used with paid search to supply the keywords for | 
 | /// ads. | 
 | const String _TERM = 'term'; | 
 |  | 
 | /// A single ID for a ecommerce group transaction. | 
 | const String _TRANSACTION_ID = 'transaction_id'; | 
 |  | 
 | /// Travel class. | 
 | const String _TRAVEL_CLASS = 'travel_class'; | 
 |  | 
 | /// A context-specific numeric value which is accumulated automatically for | 
 | /// each event type. | 
 | const String _VALUE = 'value'; | 
 |  | 
 | /// Name of virtual currency type. | 
 | const String _VIRTUAL_CURRENCY_NAME = 'virtual_currency_name'; |