├── component ├── icons │ ├── icon_128x128.png │ └── icon_256x256.png ├── screenshots │ ├── diagram.png │ ├── guidance.png │ ├── libraries.png │ ├── data-storage.png │ ├── integrations.png │ └── one-integration.png ├── samples │ ├── Analytics.iOS.Samples │ │ └── Analytics.iOS.Samples │ │ │ ├── Resources │ │ │ └── Default-568h@2x.png │ │ │ ├── Entitlements.plist │ │ │ ├── Main.cs │ │ │ ├── Analytics.OS.SamplesViewController.designer.cs │ │ │ ├── Info.plist │ │ │ ├── Analytics.iOS.SamplesViewController.cs │ │ │ ├── AppDelegate.cs │ │ │ ├── Analytics.iOS.Samples.sln │ │ │ ├── MainStoryboard.storyboard │ │ │ └── Analytics.iOS.Samples.csproj │ └── Analytics.Android.Samples │ │ └── Analytics.Android.Samples │ │ ├── Resources │ │ ├── drawable │ │ │ └── Icon.png │ │ ├── values │ │ │ └── Strings.xml │ │ ├── layout │ │ │ └── Main.axml │ │ ├── AboutResources.txt │ │ └── Resource.designer.cs │ │ ├── Properties │ │ ├── AndroidManifest.xml │ │ └── AssemblyInfo.cs │ │ ├── Assets │ │ └── AboutAssets.txt │ │ ├── MainActivity.cs │ │ ├── Analytics.Android.Samples.sln │ │ └── Analytics.Android.Samples.csproj ├── GettingStarted.md ├── License.md ├── component.yaml └── Details.md ├── Analytics.Xamarin.Pcl ├── Model │ ├── Traits.cs │ ├── Properties.cs │ ├── Alias.cs │ ├── Context.cs │ ├── Identify.cs │ ├── Dict.cs │ ├── Batch.cs │ ├── Group.cs │ ├── Track.cs │ ├── Page.cs │ ├── Screen.cs │ ├── BaseAction.cs │ ├── Providers.cs │ └── Options.cs ├── Delegates │ ├── SucceededActionHandler.cs │ └── FailedActionHandler.cs ├── Flush │ ├── IBatchFactory.cs │ ├── SimpleBatchFactory.cs │ ├── IFlushHandler.cs │ ├── BlockingQueue.cs │ ├── BlockingFlushHandler.cs │ └── AsyncFlushHandler.cs ├── Constants.cs ├── Stats │ └── Statistics.cs ├── Exception │ ├── NotInitializedException.cs │ └── BadParameter.cs ├── Defaults.cs ├── packages.config ├── Request │ ├── IRequestHandler.cs │ └── BlockingRequestHandler.cs ├── Properties │ └── AssemblyInfo.cs ├── Analytics.cs ├── Logger.cs ├── Config.cs ├── Analytics.Xamarin.Pcl.csproj └── Client.cs ├── Tests ├── Constants.cs ├── packages.config ├── app.config ├── ClientTests.cs ├── ActionTests.cs ├── Actions.cs ├── FlushTests.cs └── Tests.csproj ├── Analytics.Xamarin.Android ├── Resources │ ├── Values │ │ └── Strings.xml │ ├── Resource.Designer.cs │ └── AboutResources.txt ├── packages.config ├── app.config ├── Properties │ └── AssemblyInfo.cs └── Analytics.Xamarin.Android.csproj ├── Makefile ├── Analytics.Xamarin.Net ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── Analytics.Xamarin.Net.csproj ├── Analytics.Xamarin.iOS ├── packages.config ├── app.config ├── Properties │ └── AssemblyInfo.cs └── Analytics.Xamarin.iOS.csproj ├── Analytics.Xamarin.UWP ├── packages.config ├── Properties │ ├── AssemblyInfo.cs │ └── Analytics.Xamarin.UWP.rd.xml ├── Analytics.Xamarin.UWP.nuget.targets ├── Analytics.Xamarin.UWP.csproj └── app.config ├── History.md ├── .gitignore ├── Analytics.Xamarin.nuspec ├── Readme.md └── Analytics.Xamarin.sln /component/icons/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/icons/icon_128x128.png -------------------------------------------------------------------------------- /component/icons/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/icons/icon_256x256.png -------------------------------------------------------------------------------- /component/screenshots/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/diagram.png -------------------------------------------------------------------------------- /component/screenshots/guidance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/guidance.png -------------------------------------------------------------------------------- /component/screenshots/libraries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/libraries.png -------------------------------------------------------------------------------- /component/screenshots/data-storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/data-storage.png -------------------------------------------------------------------------------- /component/screenshots/integrations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/integrations.png -------------------------------------------------------------------------------- /component/screenshots/one-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/screenshots/one-integration.png -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Model/Traits.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Segment.Model 5 | { 6 | public class Traits : Dict 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Model/Properties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Segment.Model 5 | { 6 | public class Properties : Dict 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Delegates/SucceededActionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Segment.Model; 3 | 4 | namespace Segment.Delegates 5 | { 6 | public delegate void SucceededActionHandler(BaseAction action); 7 | } -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Delegates/FailedActionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Segment.Model; 3 | 4 | namespace Segment.Delegates 5 | { 6 | public delegate void FailedActionHandler(BaseAction action, System.Exception e); 7 | } -------------------------------------------------------------------------------- /Tests/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Segment.Test 4 | { 5 | public class Constants 6 | { 7 | // project segmentio/dotnet-test 8 | public static string WRITE_KEY = "r7bxis28wy"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /component/samples/Analytics.iOS.Samples/Analytics.iOS.Samples/Resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/samples/Analytics.iOS.Samples/Analytics.iOS.Samples/Resources/Default-568h@2x.png -------------------------------------------------------------------------------- /Analytics.Xamarin.Android/Resources/Values/Strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World, Click Me! 4 | $projectname$ 5 | 6 | -------------------------------------------------------------------------------- /component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Resources/drawable/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/HEAD/component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Resources/drawable/Icon.png -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Flush/IBatchFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Segment.Model; 5 | 6 | namespace Segment.Flush 7 | { 8 | internal interface IBatchFactory 9 | { 10 | Batch Create(List actions); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Segment 4 | { 5 | public class Constants 6 | { 7 | /// 8 | /// The maximum amount of messages to send per batch 9 | /// 10 | public static int BatchIncrement = 20; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /component/samples/Analytics.iOS.Samples/Analytics.iOS.Samples/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Resources/values/Strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World, Click Me! 4 | Analytics.Android.Samples 5 | 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MDTOOL ?= /Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool 2 | COMPONENT ?= bin/xamarin-component.exe 3 | 4 | build: 5 | $(MDTOOL) build -c:Release ./Analytics.Xamarin.sln 6 | 7 | clean: 8 | $(MDTOOL) build -t:Clean ./ReactiveUI_XSAll.sln 9 | 10 | package: 11 | @mono $(COMPONENT) package component 12 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Stats/Statistics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Segment.Stats 7 | { 8 | public class Statistics 9 | { 10 | public int Submitted { get; set; } 11 | public int Succeeded { get; set; } 12 | public int Failed { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Exception/NotInitializedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Segment.Exception 7 | { 8 | public class NotInitializedException : System.Exception 9 | { 10 | public NotInitializedException() : base("Please initialize Segment.io first before using.") { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Net/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Defaults.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Segment 7 | { 8 | public class Defaults 9 | { 10 | public static string Host = "https://api.segment.io"; 11 | 12 | public static TimeSpan Timeout = TimeSpan.FromSeconds(5); 13 | 14 | public static int MaxQueueCapacity = 10000; 15 | 16 | public static bool Async = true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Analytics.Xamarin.iOS/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Android/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Exception/BadParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Segment.Exception 6 | { 7 | public class APIException : System.Exception 8 | { 9 | public string Code { get; set; } 10 | public string message { get; set; } 11 | 12 | public APIException(string code, string message) : base(message) 13 | { 14 | this.Code = code; 15 | this.message = message; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Analytics.Xamarin.UWP/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Flush/SimpleBatchFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Segment.Model; 5 | 6 | namespace Segment.Flush 7 | { 8 | internal class SimpleBatchFactory : IBatchFactory 9 | { 10 | private string _writeKey; 11 | 12 | internal SimpleBatchFactory(string writeKey) 13 | { 14 | this._writeKey = writeKey; 15 | } 16 | 17 | public Batch Create(List actions) 18 | { 19 | return new Batch(_writeKey, actions); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Resources/layout/Main.axml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Analytics.Xamarin.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Analytics.Xamarin 5 | 1.0.0 6 | Analytics.Xamarin 7 | Segment Team 8 | https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/master/README.md 9 | https://segment.com 10 | https://segment.io/favicon.ico 11 | false 12 | The .NET API for analytics, by Segment. Install and immediately send data to 150+ analytics and business intelligence tools with the flip of a switch. 13 | The Segment Xamarin Portable Client Library (PCL), the cleanest and best way to integrate analytics into your Xamarin application. Check out docs at: https://segment.io/docs/libraries/net/ and code at https: 14 | Check out the changelog at: https://raw.githubusercontent.com/segmentio/Analytics.Xamarin/master/History.md 15 | en-US 16 | analytics, Segment 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Analytics.Xamarin.iOS/Analytics.Xamarin.iOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {33432EE5-146F-4C0B-B9A8-D5387D758779} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | Analytics.Xamarin.iOS 12 | Resources 13 | Analytics.Xamarin.iOS 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug 22 | DEBUG; 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | full 29 | true 30 | bin\Release 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll 38 | True 39 | 40 | 41 | 42 | ..\packages\Microsoft.Net.Http.2.2.29\lib\Xamarin.iOS10\System.Net.Http.Extensions.dll 43 | True 44 | 45 | 46 | ..\packages\Microsoft.Net.Http.2.2.29\lib\Xamarin.iOS10\System.Net.Http.Primitives.dll 47 | True 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {f074c87f-5524-4ff6-a0a9-74dc6bd5faa7} 62 | Analytics.Xamarin.Pcl 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /component/samples/Analytics.Android.Samples/Analytics.Android.Samples/Analytics.Android.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 7 | {F9F5A5C6-72CC-411B-BF33-2745059DA577} 8 | Library 9 | Analytics.Android.Samples 10 | Assets 11 | Resources 12 | Resource 13 | Resources\Resource.designer.cs 14 | True 15 | False 16 | Analytics.Android.Samples 17 | Properties\AndroidManifest.xml 18 | v4.0.3 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug 25 | DEBUG; 26 | prompt 27 | 4 28 | None 29 | false 30 | 31 | 32 | full 33 | true 34 | bin\Release 35 | prompt 36 | 4 37 | false 38 | false 39 | armeabi;armeabi-v7a;x86 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.dll 48 | 49 | 50 | ..\..\..\..\Analytics\bin\Release\Analytics.dll 51 | 52 | 53 | ..\..\..\..\Analytics\bin\Release\Newtonsoft.Json.dll 54 | 55 | 56 | ..\..\..\..\Analytics\bin\Release\System.IO.dll 57 | 58 | 59 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.Extensions.dll 60 | 61 | 62 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.Primitives.dll 63 | 64 | 65 | ..\..\..\..\Analytics\bin\Release\System.Runtime.dll 66 | 67 | 68 | ..\..\..\..\Analytics\bin\Release\System.Threading.Tasks.dll 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Analytics.Xamarin 2 | ============= 3 | 4 | Analytics.Xamarin is a [Xamarin](http://xamarin.com) portable class library [(PCL)](http://developer.xamarin.com/guides/cross-platform/application_fundamentals/pcl/) that makes the [Segment](https://segment.com) analytics API available for the following platforms: 5 | 6 | ### ⚠️ Community ⚠️ 7 | This library is community supported. Segment does not manage or update this library. We suggest forking the repo if changes are needed for your project. 8 | 9 | We highly recommend using our upgraded [Analytics-CSharp](https://github.com/segmentio/Analytics-CSharp) to benefit from feature enhancements and ongoing support. 10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |

You can't fix what you can't measure

18 |
19 | 20 | Analytics helps you measure your users, product, and business. It unlocks insights into your app's funnel, core business metrics, and whether you have product-market fit. 21 | 22 | ## How to get started 23 | 1. **Collect analytics data** from your app(s). 24 | - The top 200 Segment companies collect data from 5+ source types (web, mobile, server, CRM, etc.). 25 | 2. **Send the data to analytics tools** (for example, Google Analytics, Amplitude, Mixpanel). 26 | - Over 250+ Segment companies send data to eight categories of destinations such as analytics tools, warehouses, email marketing and remarketing systems, session recording, and more. 27 | 3. **Explore your data** by creating metrics (for example, new signups, retention cohorts, and revenue generation). 28 | - The best Segment companies use retention cohorts to measure product market fit. Netflix has 70% paid retention after 12 months, 30% after 7 years. 29 | 30 | [Segment](https://segment.com) collects analytics data and allows you to send it to more than 250 apps (such as Google Analytics, Mixpanel, Optimizely, Facebook Ads, Slack, Sentry) just by flipping a switch. You only need one Segment code snippet, and you can turn integrations on and off at will, with no additional code. [Sign up with Segment today](https://app.segment.com/signup). 31 | 32 | ### Why? 33 | 1. **Power all your analytics apps with the same data**. Instead of writing code to integrate all of your tools individually, send data to Segment, once. 34 | 35 | 2. **Install tracking for the last time**. We're the last integration you'll ever need to write. You only need to instrument Segment once. Reduce all of your tracking code and advertising tags into a single set of API calls. 36 | 37 | 3. **Send data from anywhere**. Send Segment data from any device, and we'll transform and send it on to any tool. 38 | 39 | 4. **Query your data in SQL**. Slice, dice, and analyze your data in detail with Segment SQL. We'll transform and load your customer behavioral data directly from your apps into Amazon Redshift, Google BigQuery, or Postgres. Save weeks of engineering time by not having to invent your own data warehouse and ETL pipeline. 40 | 41 | For example, you can capture data on any app: 42 | ```js 43 | analytics.track('Order Completed', { price: 99.84 }) 44 | ``` 45 | Then, query the resulting data in SQL: 46 | ```sql 47 | select * from app.order_completed 48 | order by price desc 49 | ``` 50 | 51 | ### 🚀 Startup Program 52 |
53 | 54 |
55 | If you are part of a new startup (<$5M raised, <2 years since founding), we just launched a new startup program for you. You can get a Segment Team plan (up to $25,000 value in Segment credits) for free up to 2 years — apply here! 56 | 57 | ## Documentation 58 | 59 | Documentation is available at [https://segment.com/docs/libraries/xamarin/](https://segment.com/docs/libraries/xamarin/). 60 | 61 | ## License 62 | 63 | ``` 64 | WWWWWW||WWWWWW 65 | W W W||W W W 66 | || 67 | ( OO )__________ 68 | / | \ 69 | /o o| MIT \ 70 | \___/||_||__||_|| * 71 | || || || || 72 | _||_|| _||_|| 73 | (__|__|(__|__| 74 | ``` 75 | 76 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/segmentio/Analytics.Xamarin/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 77 | 78 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {DF5F3A0E-A598-45CB-802F-A06967974362} 9 | Library 10 | Tests 11 | Tests 12 | 13 | 14 | v4.5.1 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug 22 | DEBUG; 23 | prompt 24 | 4 25 | false 26 | false 27 | 28 | 29 | full 30 | true 31 | bin\Release 32 | prompt 33 | 4 34 | false 35 | false 36 | 37 | 38 | 39 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 40 | True 41 | 42 | 43 | ..\packages\NUnit.3.6.0\lib\net45\nunit.framework.dll 44 | True 45 | 46 | 47 | 48 | 49 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll 50 | True 51 | 52 | 53 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll 54 | True 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {f074c87f-5524-4ff6-a0a9-74dc6bd5faa7} 76 | Analytics.Xamarin.Pcl 77 | 78 | 79 | 80 | 81 | 82 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Net/Analytics.Xamarin.Net.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {230084FF-FC68-469A-AC69-31CBE0FE82CA} 8 | Library 9 | Properties 10 | Analytics.Xamarin.Net 11 | Analytics.Xamarin.Net 12 | v4.5.2 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 37 | True 38 | 39 | 40 | 41 | 42 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll 43 | True 44 | 45 | 46 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll 47 | True 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {f074c87f-5524-4ff6-a0a9-74dc6bd5faa7} 63 | Analytics.Xamarin.Pcl 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Request/BlockingRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Runtime.Serialization.Json; 7 | using System.IO; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | using Newtonsoft.Json; 12 | 13 | using Segment.Model; 14 | using Segment.Exception; 15 | using Segment.Delegates; 16 | 17 | namespace Segment.Request 18 | { 19 | class WebProxy : System.Net.IWebProxy 20 | { 21 | private string _proxy; 22 | 23 | public WebProxy(string proxy) 24 | { 25 | _proxy = proxy; 26 | GetProxy(new Uri(proxy)); // ** What does this do? 27 | } 28 | 29 | public System.Net.ICredentials Credentials 30 | { 31 | get; set; 32 | } 33 | 34 | public Uri GetProxy(Uri destination) 35 | { 36 | if (!String.IsNullOrWhiteSpace(destination.ToString())) 37 | return destination; 38 | else 39 | return new Uri(""); 40 | } 41 | 42 | public bool IsBypassed(Uri host) 43 | { 44 | if (!String.IsNullOrWhiteSpace(host.ToString())) 45 | return true; 46 | else 47 | return false; 48 | } 49 | } 50 | internal class BlockingRequestHandler : IRequestHandler 51 | { 52 | /// 53 | /// Occurs when an action fails. 54 | /// 55 | public event FailedActionHandler Failed; 56 | 57 | /// 58 | /// Occurs when an action succeeds. 59 | /// 60 | public event SucceededActionHandler Succeeded; 61 | 62 | /// 63 | /// Segment client 64 | /// 65 | private readonly Client _client; 66 | 67 | /// 68 | /// Http client 69 | /// 70 | private readonly HttpClient _httpClient; 71 | 72 | 73 | 74 | internal BlockingRequestHandler(Client client, TimeSpan timeout) 75 | { 76 | this._client = client; 77 | 78 | if (!string.IsNullOrEmpty(_client.Config.Proxy)) 79 | { 80 | var handler = new HttpClientHandler 81 | { 82 | Proxy = new WebProxy(_client.Config.Proxy), 83 | UseProxy = true 84 | }; 85 | this._httpClient = new HttpClient(handler); 86 | } 87 | else 88 | this._httpClient = new HttpClient(); 89 | 90 | this._httpClient.Timeout = timeout; 91 | // do not use the expect 100-continue behavior 92 | this._httpClient.DefaultRequestHeaders.ExpectContinue = false; 93 | } 94 | 95 | public bool SendBatch(Batch batch) 96 | { 97 | Dict props = new Dict { 98 | { "batch id", batch.MessageId }, 99 | { "batch size", batch.batch.Count } 100 | }; 101 | bool sentBatch = false; 102 | try 103 | { 104 | // set the current request time 105 | batch.SentAt = DateTime.Now.ToString("o"); 106 | string json = JsonConvert.SerializeObject(batch); 107 | props["json size"] = json.Length; 108 | 109 | Uri uri = new Uri(_client.Config.Host + "/v1/import"); 110 | 111 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri); 112 | 113 | // basic auth: https://segment.io/docs/tracking-api/reference/#authentication 114 | request.Headers.Add("Authorization", BasicAuthHeader(batch.WriteKey, "")); 115 | request.Content = new StringContent(json, Encoding.UTF8, "application/json"); 116 | 117 | Logger.Info("Sending analytics request to Segment.io ..", props); 118 | 119 | var start = DateTime.Now; 120 | 121 | var response = _httpClient.SendAsync(request).Result; 122 | 123 | var duration = DateTime.Now - start; 124 | props["success"] = response.IsSuccessStatusCode; 125 | props["duration (ms)"] = duration.TotalMilliseconds; 126 | 127 | if (response.IsSuccessStatusCode) 128 | { 129 | Succeed(batch); 130 | Logger.Info("Request successful", props); 131 | sentBatch = true; 132 | } 133 | else 134 | { 135 | string reason = string.Format("Status Code {0} ", response.StatusCode); 136 | reason += response.Content.ToString(); 137 | props["reason"] = reason; 138 | Logger.Error("Request failed", props); 139 | Fail(batch, new APIException("Unexpected Status Code", reason)); 140 | } 141 | } 142 | catch (System.Exception e) 143 | { 144 | props["reason"] = e.Message; 145 | Logger.Error("Request failed", props); 146 | Fail(batch, e); 147 | } 148 | return sentBatch; 149 | } 150 | 151 | private void Fail(Batch batch, System.Exception e) 152 | { 153 | foreach (BaseAction action in batch.batch) 154 | { 155 | if (Failed != null) 156 | { 157 | Failed(action, e); 158 | } 159 | } 160 | } 161 | 162 | 163 | private void Succeed(Batch batch) 164 | { 165 | foreach (BaseAction action in batch.batch) 166 | { 167 | if (Succeeded != null) 168 | { 169 | Succeeded(action); 170 | } 171 | } 172 | } 173 | 174 | private string BasicAuthHeader(string user, string pass) 175 | { 176 | string val = user + ":" + pass; 177 | return "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(val)); 178 | } 179 | 180 | public void Dispose() 181 | { 182 | _client.Dispose(); 183 | } 184 | } 185 | } 186 | 187 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Android/Analytics.Xamarin.Android.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2} 9 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | Properties 12 | Analytics.Xamarin.Android 13 | Analytics.Xamarin.Android 14 | 512 15 | Resources\Resource.Designer.cs 16 | Off 17 | v11.0 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll 43 | True 44 | 45 | 46 | 47 | 48 | 49 | ..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Extensions.dll 50 | True 51 | 52 | 53 | ..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Primitives.dll 54 | True 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {f074c87f-5524-4ff6-a0a9-74dc6bd5faa7} 74 | Analytics.Xamarin.Pcl 75 | 76 | 77 | 78 | 79 | 80 | 81 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 82 | 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Flush/AsyncFlushHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Runtime.Serialization.Json; 5 | using System.IO; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using Segment; 10 | using Segment.Request; 11 | using Segment.Model; 12 | using Segment.Exception; 13 | 14 | namespace Segment.Flush 15 | { 16 | internal class AsyncFlushHandler : IFlushHandler 17 | { 18 | /// 19 | /// Internal message queue 20 | /// 21 | private BlockingQueue _queue; 22 | 23 | /// 24 | /// Creates a series of actions into a batch that we can send to the server 25 | /// 26 | private IBatchFactory _batchFactory; 27 | /// 28 | /// Performs the actual HTTP request to our server 29 | /// 30 | private IRequestHandler _requestHandler; 31 | 32 | /// 33 | /// Cancellation token for the flushing task. 34 | /// 35 | private CancellationTokenSource _continue; 36 | 37 | /// 38 | /// Marks that the current queue is empty and no flush is happening. 39 | /// Flush will wait for this to be signaled. 40 | /// 41 | private ManualResetEvent _idle; 42 | 43 | /// 44 | /// The max size of the queue to allow 45 | /// This condition prevents high performance condition causing 46 | /// this library to eat memory. 47 | /// 48 | internal int MaxQueueSize { get; set; } 49 | 50 | internal AsyncFlushHandler(IBatchFactory batchFactory, 51 | IRequestHandler requestHandler, 52 | int maxQueueSize) 53 | { 54 | _queue = new BlockingQueue(); 55 | 56 | this._batchFactory = batchFactory; 57 | this._requestHandler = requestHandler; 58 | 59 | this.MaxQueueSize = maxQueueSize; 60 | 61 | // set that the queue is currently empty 62 | _idle = new ManualResetEvent(true); 63 | 64 | _continue = new CancellationTokenSource(); 65 | 66 | // start the long running flushing task 67 | Task.Factory.StartNew(() => Loop(), _continue.Token); 68 | } 69 | 70 | public void Process(BaseAction action) 71 | { 72 | int size = _queue.Count; 73 | 74 | if (size > MaxQueueSize) 75 | { 76 | Logger.Warn("Dropped message because queue is too full.", new Dict 77 | { 78 | { "message id", action.MessageId }, 79 | { "queue size", _queue.Count }, 80 | { "max queue size", MaxQueueSize } 81 | }); 82 | } 83 | else 84 | { 85 | _queue.Enqueue(action); 86 | } 87 | } 88 | 89 | /// 90 | /// Blocks until all the messages are flushed 91 | /// 92 | public void Flush() 93 | { 94 | // if the queue has items and the flushing thread is still on WAIT for the blocking 95 | // queue, then the idle event could still be triggered. in that case, we want to reset it 96 | if (_queue.Count > 0) _idle.Reset(); 97 | 98 | Logger.Debug("Blocking flush waiting until the queue if fully empty .."); 99 | 100 | _idle.WaitOne(); 101 | 102 | Logger.Debug("Blocking flush completed."); 103 | } 104 | 105 | /// 106 | /// Loops on the flushing thread and processes the message queue 107 | /// 108 | private void Loop() 109 | { 110 | Logger.Debug("Starting async flush thread .."); 111 | 112 | List current = new List(); 113 | 114 | // keep looping while flushing thread is active 115 | while (!_continue.Token.IsCancellationRequested) 116 | { 117 | do 118 | { 119 | 120 | // the only time we're actually not flushing 121 | // is if the condition that the queue is empty here 122 | if (_queue.Count == 0) 123 | { 124 | _idle.Set(); 125 | 126 | Logger.Debug("Queue is empty, flushing is finished."); 127 | } 128 | 129 | // blocks and waits for a dequeue 130 | BaseAction action = _queue.Dequeue(); 131 | 132 | if (action == null) 133 | { 134 | // the queue was disposed, so we're done with this batch 135 | break; 136 | 137 | } 138 | else 139 | { 140 | // we are no longer idle since there's messages to be processed 141 | _idle.Reset(); 142 | 143 | // add this action to the current batch 144 | current.Add(action); 145 | 146 | Logger.Debug("Dequeued action in async loop.", new Dict{ 147 | { "message id", action.MessageId }, 148 | { "queue size", _queue.Count } 149 | }); 150 | } 151 | } 152 | // if we can easily see that there's still stuff in the queue 153 | // we'd prefer to add more to the current batch to send more 154 | // at once. But only if we're not disposed yet (_continue is true). 155 | while (!_continue.Token.IsCancellationRequested && _queue.Count > 0 && current.Count <= Constants.BatchIncrement); 156 | 157 | if (current.Count > 0) 158 | { 159 | // we have a batch that we're trying to send 160 | Batch batch = _batchFactory.Create(current); 161 | 162 | Logger.Debug("Created flush batch.", new Dict { 163 | { "batch size", current.Count } 164 | }); 165 | 166 | // send the batch async 167 | Task res = Task.Run(() => _requestHandler.SendBatch(batch)); 168 | // clear the current only of the batch was sent successfully 169 | if (res.Result) 170 | { 171 | current = new List(); 172 | } 173 | } 174 | } 175 | } 176 | 177 | /// 178 | /// Disposes of the flushing thread and the message queue 179 | /// 180 | /// Call when you are finished using the . The 181 | /// method leaves the in an unusable state. 182 | /// After calling , you must release all references to the 183 | /// so the garbage collector can reclaim the memory that the 184 | /// was occupying. 185 | public void Dispose() 186 | { 187 | // tell the flushing thread to stop 188 | _continue.Cancel(); 189 | 190 | // tell the queue to stop blocking if it is currently doing so 191 | _queue.Dispose(); 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Analytics.Xamarin.Pcl.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7} 9 | Library 10 | Properties 11 | Analytics.Xamarin.Pcl 12 | Analytics.Xamarin.Pcl 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile44 17 | v4.6 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | Designer 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll 83 | True 84 | 85 | 86 | ..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net45+win8\System.Net.Http.Extensions.dll 87 | True 88 | 89 | 90 | ..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net45+win8\System.Net.Http.Primitives.dll 91 | True 92 | 93 | 94 | 95 | 96 | 97 | 98 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 99 | 100 | 101 | 102 | 109 | -------------------------------------------------------------------------------- /component/samples/Analytics.iOS.Samples/Analytics.iOS.Samples/Analytics.iOS.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | {6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 7 | {9CC85766-F2D2-486E-8DA8-478D90BD5231} 8 | Exe 9 | Analytics.OS.Samples 10 | Resources 11 | AnalyticsOSSamples 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\iPhoneSimulator\Debug 18 | DEBUG;__MOBILE__;__IOS__; 19 | prompt 20 | 4 21 | false 22 | None 23 | Entitlements.plist 24 | true 25 | 26 | 27 | full 28 | true 29 | bin\iPhoneSimulator\Release 30 | __MOBILE__;__IOS__; 31 | prompt 32 | 4 33 | None 34 | false 35 | Entitlements.plist 36 | 37 | 38 | true 39 | full 40 | false 41 | bin\iPhone\Debug 42 | DEBUG;__MOBILE__;__IOS__; 43 | prompt 44 | 4 45 | false 46 | Entitlements.plist 47 | true 48 | iPhone Developer 49 | 50 | 51 | full 52 | true 53 | bin\iPhone\Release 54 | __MOBILE__;__IOS__; 55 | prompt 56 | 4 57 | Entitlements.plist 58 | false 59 | iPhone Developer 60 | 61 | 62 | full 63 | true 64 | bin\iPhone\Ad-Hoc 65 | __MOBILE__;__IOS__; 66 | prompt 67 | 4 68 | false 69 | Entitlements.plist 70 | true 71 | Automatic:AdHoc 72 | iPhone Distribution 73 | 74 | 75 | full 76 | true 77 | bin\iPhone\AppStore 78 | __MOBILE__;__IOS__; 79 | prompt 80 | 4 81 | false 82 | Entitlements.plist 83 | Automatic:AppStore 84 | iPhone Distribution 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | ..\..\..\..\Analytics\bin\Release\Newtonsoft.Json.dll 93 | 94 | 95 | ..\..\..\..\Analytics\bin\Release\System.IO.dll 96 | 97 | 98 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.Extensions.dll 99 | 100 | 101 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.Primitives.dll 102 | 103 | 104 | ..\..\..\..\Analytics\bin\Release\System.Net.Http.dll 105 | 106 | 107 | ..\..\..\..\Analytics\bin\Release\System.Runtime.dll 108 | 109 | 110 | ..\..\..\..\Analytics\bin\Release\System.Threading.Tasks.dll 111 | 112 | 113 | ..\..\..\..\Analytics\bin\Release\Analytics.dll 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Analytics.iOS.SamplesViewController.cs 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Analytics.Xamarin.UWP/Analytics.Xamarin.UWP.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5020A79A-3FC9-42F3-9975-E73003391BF8} 8 | Library 9 | Properties 10 | Analytics.Xamarin.UWP 11 | Analytics.Xamarin.UWP 12 | en-US 13 | UAP 14 | 10.0.19041.0 15 | 10.0.19041.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot 20 | 21 | 22 | AnyCPU 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 28 | prompt 29 | 4 30 | 31 | 32 | AnyCPU 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE;NETFX_CORE;WINDOWS_UWP 37 | prompt 38 | 4 39 | 40 | 41 | x86 42 | true 43 | bin\x86\Debug\ 44 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 45 | ;2008 46 | full 47 | x86 48 | false 49 | prompt 50 | 51 | 52 | x86 53 | bin\x86\Release\ 54 | TRACE;NETFX_CORE;WINDOWS_UWP 55 | true 56 | ;2008 57 | pdbonly 58 | x86 59 | false 60 | prompt 61 | 62 | 63 | ARM 64 | true 65 | bin\ARM\Debug\ 66 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 67 | ;2008 68 | full 69 | ARM 70 | false 71 | prompt 72 | 73 | 74 | ARM 75 | bin\ARM\Release\ 76 | TRACE;NETFX_CORE;WINDOWS_UWP 77 | true 78 | ;2008 79 | pdbonly 80 | ARM 81 | false 82 | prompt 83 | 84 | 85 | x64 86 | true 87 | bin\x64\Debug\ 88 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 89 | ;2008 90 | full 91 | x64 92 | false 93 | prompt 94 | 95 | 96 | x64 97 | bin\x64\Release\ 98 | TRACE;NETFX_CORE;WINDOWS_UWP 99 | true 100 | ;2008 101 | pdbonly 102 | x64 103 | false 104 | prompt 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | {f074c87f-5524-4ff6-a0a9-74dc6bd5faa7} 118 | Analytics.Xamarin.Pcl 119 | 120 | 121 | 122 | 123 | 1.1.10 124 | 125 | 126 | 1.0.21 127 | 128 | 129 | 2.2.29 130 | 131 | 132 | 5.2.4 133 | 134 | 135 | 13.0.1 136 | 137 | 138 | 139 | 14.0 140 | 141 | 142 | 149 | -------------------------------------------------------------------------------- /Analytics.Xamarin.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{DF5F3A0E-A598-45CB-802F-A06967974362}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{66782BD5-F803-4916-B756-63BFE1549B93}" 9 | ProjectSection(SolutionItems) = preProject 10 | Analytics.Xamarin.nuspec = Analytics.Xamarin.nuspec 11 | buildNuget.ps1 = buildNuget.ps1 12 | EndProjectSection 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analytics.Xamarin.Pcl", "Analytics.Xamarin.Pcl\Analytics.Xamarin.Pcl.csproj", "{F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analytics.Xamarin.Android", "Analytics.Xamarin.Android\Analytics.Xamarin.Android.csproj", "{5FCCF15F-BF1F-4871-B684-E02A4DD418E2}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analytics.Xamarin.iOS", "Analytics.Xamarin.iOS\Analytics.Xamarin.iOS.csproj", "{33432EE5-146F-4C0B-B9A8-D5387D758779}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analytics.Xamarin.UWP", "Analytics.Xamarin.UWP\Analytics.Xamarin.UWP.csproj", "{5020A79A-3FC9-42F3-9975-E73003391BF8}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analytics.Xamarin.Net", "Analytics.Xamarin.Net\Analytics.Xamarin.Net.csproj", "{230084FF-FC68-469A-AC69-31CBE0FE82CA}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Debug|ARM = Debug|ARM 28 | Debug|x64 = Debug|x64 29 | Debug|x86 = Debug|x86 30 | Release|Any CPU = Release|Any CPU 31 | Release|ARM = Release|ARM 32 | Release|x64 = Release|x64 33 | Release|x86 = Release|x86 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|ARM.ActiveCfg = Debug|Any CPU 39 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|ARM.Build.0 = Debug|Any CPU 40 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|x64.ActiveCfg = Debug|Any CPU 41 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|x64.Build.0 = Debug|Any CPU 42 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|x86.ActiveCfg = Debug|Any CPU 43 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Debug|x86.Build.0 = Debug|Any CPU 44 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|ARM.ActiveCfg = Release|Any CPU 47 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|ARM.Build.0 = Release|Any CPU 48 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|x64.ActiveCfg = Release|Any CPU 49 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|x64.Build.0 = Release|Any CPU 50 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|x86.ActiveCfg = Release|Any CPU 51 | {DF5F3A0E-A598-45CB-802F-A06967974362}.Release|x86.Build.0 = Release|Any CPU 52 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|ARM.ActiveCfg = Debug|Any CPU 55 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|ARM.Build.0 = Debug|Any CPU 56 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|x64.ActiveCfg = Debug|Any CPU 57 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|x64.Build.0 = Debug|Any CPU 58 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|x86.ActiveCfg = Debug|Any CPU 59 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Debug|x86.Build.0 = Debug|Any CPU 60 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|ARM.ActiveCfg = Release|Any CPU 63 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|ARM.Build.0 = Release|Any CPU 64 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|x64.ActiveCfg = Release|Any CPU 65 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|x64.Build.0 = Release|Any CPU 66 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|x86.ActiveCfg = Release|Any CPU 67 | {F074C87F-5524-4FF6-A0A9-74DC6BD5FAA7}.Release|x86.Build.0 = Release|Any CPU 68 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|ARM.ActiveCfg = Debug|Any CPU 71 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|ARM.Build.0 = Debug|Any CPU 72 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|x64.ActiveCfg = Debug|Any CPU 73 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|x64.Build.0 = Debug|Any CPU 74 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|x86.ActiveCfg = Debug|Any CPU 75 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Debug|x86.Build.0 = Debug|Any CPU 76 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|Any CPU.Build.0 = Release|Any CPU 78 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|ARM.ActiveCfg = Release|Any CPU 79 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|ARM.Build.0 = Release|Any CPU 80 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|x64.ActiveCfg = Release|Any CPU 81 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|x64.Build.0 = Release|Any CPU 82 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|x86.ActiveCfg = Release|Any CPU 83 | {5FCCF15F-BF1F-4871-B684-E02A4DD418E2}.Release|x86.Build.0 = Release|Any CPU 84 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|Any CPU.Build.0 = Debug|Any CPU 86 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|ARM.ActiveCfg = Debug|Any CPU 87 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|ARM.Build.0 = Debug|Any CPU 88 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|x64.ActiveCfg = Debug|Any CPU 89 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|x64.Build.0 = Debug|Any CPU 90 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|x86.ActiveCfg = Debug|Any CPU 91 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Debug|x86.Build.0 = Debug|Any CPU 92 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|Any CPU.ActiveCfg = Release|Any CPU 93 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|Any CPU.Build.0 = Release|Any CPU 94 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|ARM.ActiveCfg = Release|Any CPU 95 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|ARM.Build.0 = Release|Any CPU 96 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|x64.ActiveCfg = Release|Any CPU 97 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|x64.Build.0 = Release|Any CPU 98 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|x86.ActiveCfg = Release|Any CPU 99 | {33432EE5-146F-4C0B-B9A8-D5387D758779}.Release|x86.Build.0 = Release|Any CPU 100 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 101 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU 102 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|ARM.ActiveCfg = Debug|ARM 103 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|ARM.Build.0 = Debug|ARM 104 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|x64.ActiveCfg = Debug|x64 105 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|x64.Build.0 = Debug|x64 106 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|x86.ActiveCfg = Debug|x86 107 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Debug|x86.Build.0 = Debug|x86 108 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU 109 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|Any CPU.Build.0 = Release|Any CPU 110 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|ARM.ActiveCfg = Release|ARM 111 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|ARM.Build.0 = Release|ARM 112 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|x64.ActiveCfg = Release|x64 113 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|x64.Build.0 = Release|x64 114 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|x86.ActiveCfg = Release|x86 115 | {5020A79A-3FC9-42F3-9975-E73003391BF8}.Release|x86.Build.0 = Release|x86 116 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 117 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|Any CPU.Build.0 = Debug|Any CPU 118 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|ARM.ActiveCfg = Debug|Any CPU 119 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|ARM.Build.0 = Debug|Any CPU 120 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|x64.ActiveCfg = Debug|Any CPU 121 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|x64.Build.0 = Debug|Any CPU 122 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|x86.ActiveCfg = Debug|Any CPU 123 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Debug|x86.Build.0 = Debug|Any CPU 124 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|Any CPU.ActiveCfg = Release|Any CPU 125 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|Any CPU.Build.0 = Release|Any CPU 126 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|ARM.ActiveCfg = Release|Any CPU 127 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|ARM.Build.0 = Release|Any CPU 128 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|x64.ActiveCfg = Release|Any CPU 129 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|x64.Build.0 = Release|Any CPU 130 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|x86.ActiveCfg = Release|Any CPU 131 | {230084FF-FC68-469A-AC69-31CBE0FE82CA}.Release|x86.Build.0 = Release|Any CPU 132 | EndGlobalSection 133 | GlobalSection(SolutionProperties) = preSolution 134 | HideSolutionNode = FALSE 135 | EndGlobalSection 136 | GlobalSection(MonoDevelopProperties) = preSolution 137 | StartupItem = Tests\Tests.csproj 138 | EndGlobalSection 139 | EndGlobal 140 | -------------------------------------------------------------------------------- /Analytics.Xamarin.UWP/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /Analytics.Xamarin.Pcl/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Text; 5 | 6 | using Segment.Flush; 7 | using Segment.Request; 8 | using Segment.Exception; 9 | using Segment.Model; 10 | using Segment.Stats; 11 | using Segment.Delegates; 12 | 13 | namespace Segment 14 | { 15 | /// 16 | /// A Segment.io .NET client 17 | /// 18 | public class Client : IDisposable 19 | { 20 | private IFlushHandler _flushHandler; 21 | private string _writeKey; 22 | private Config _config; 23 | 24 | public Statistics Statistics { get; set; } 25 | 26 | #region Events 27 | 28 | public event FailedActionHandler Failed; 29 | public event SucceededActionHandler Succeeded; 30 | 31 | #endregion 32 | 33 | #region Initialization 34 | 35 | /// 36 | /// Creates a new REST client with a specified API writeKey and default config 37 | /// 38 | /// 39 | public Client(string writeKey) : this(writeKey, new Config()) { } 40 | 41 | /// 42 | /// Creates a new REST client with a specified API writeKey and default config 43 | /// 44 | /// 45 | /// 46 | public Client(string writeKey, Config config) 47 | { 48 | if (String.IsNullOrEmpty(writeKey)) 49 | throw new InvalidOperationException("Please supply a valid writeKey to initialize."); 50 | 51 | this.Statistics = new Statistics(); 52 | 53 | this._writeKey = writeKey; 54 | this._config = config; 55 | 56 | IRequestHandler requestHandler = new BlockingRequestHandler(this, config.Timeout); 57 | IBatchFactory batchFactory = new SimpleBatchFactory(this._writeKey); 58 | 59 | requestHandler.Succeeded += (action) => 60 | { 61 | this.Statistics.Succeeded += 1; 62 | if (Succeeded != null) Succeeded(action); 63 | }; 64 | 65 | requestHandler.Failed += (action, e) => 66 | { 67 | this.Statistics.Failed += 1; 68 | if (Failed != null) Failed(action, e); 69 | }; 70 | 71 | if (config.Async) 72 | _flushHandler = new AsyncFlushHandler(batchFactory, requestHandler, config.MaxQueueSize); 73 | else 74 | _flushHandler = new BlockingFlushHandler(batchFactory, requestHandler); 75 | } 76 | 77 | #endregion 78 | 79 | #region Properties 80 | 81 | public string WriteKey 82 | { 83 | get 84 | { 85 | return _writeKey; 86 | } 87 | } 88 | 89 | 90 | public Config Config 91 | { 92 | get 93 | { 94 | return _config; 95 | } 96 | } 97 | 98 | #endregion 99 | 100 | #region Public Methods 101 | 102 | #region Identify 103 | 104 | /// 105 | /// Identifying a visitor ties all of their actions to an ID you 106 | /// recognize and records visitor traits you can segment by. 107 | /// 108 | /// 109 | /// The visitor's identifier after they log in, or you know 110 | /// who they are. By 111 | /// explicitly identifying a user, you tie all of their actions to their identity. 112 | /// 113 | /// A dictionary with keys like "email", "name", “subscriptionPlan” or 114 | /// "friendCount”. You can segment your users by any trait you record. 115 | /// Pass in values in key-value format. String key, then its value 116 | /// { String, Integer, Boolean, Double, or Date are acceptable types for a value. } 117 | /// 118 | public void Identify(string userId, IDictionary traits) 119 | { 120 | Identify(userId, traits, null); 121 | } 122 | 123 | /// 124 | /// Identifying a visitor ties all of their actions to an ID you 125 | /// recognize and records visitor traits you can segment by. 126 | /// 127 | /// 128 | /// The visitor's identifier after they log in, or you know 129 | /// who they are. By 130 | /// explicitly identifying a user, you tie all of their actions to their identity. 131 | /// 132 | /// A dictionary with keys like "email", "name", “subscriptionPlan” or 133 | /// "friendCount”. You can segment your users by any trait you record. 134 | /// Pass in values in key-value format. String key, then its value 135 | /// { String, Integer, Boolean, Double, or Date are acceptable types for a value. } 136 | /// 137 | /// Options allowing you to set timestamp, anonymousId, target integrations, 138 | /// and the context of th emessage. 139 | /// 140 | public void Identify(string userId, IDictionary traits, Options options) 141 | { 142 | ensureId(userId, options); 143 | 144 | Enqueue(new Identify(userId, traits, options)); 145 | } 146 | 147 | #endregion 148 | 149 | #region Group 150 | 151 | /// 152 | /// The `group` method lets you associate a user with a group. Be it a company, 153 | /// organization, account, project, team or whatever other crazy name you came up 154 | /// with for the same concept! It also lets you record custom traits about the 155 | /// group, like industry or number of employees. 156 | /// 157 | /// 158 | /// The visitor's database identifier after they log in, or you know 159 | /// who they are. By explicitly grouping a user, you tie all of their actions to their group. 160 | /// 161 | /// The group's database identifier after they log in, or you know 162 | /// who they are. 163 | /// 164 | /// Options allowing you to set timestamp, anonymousId, target integrations, 165 | /// and the context of th emessage. 166 | /// 167 | public void Group(string userId, string groupId, Options options) 168 | { 169 | Group(userId, groupId, null, options); 170 | } 171 | 172 | /// 173 | /// The `group` method lets you associate a user with a group. Be it a company, 174 | /// organization, account, project, team or whatever other crazy name you came up 175 | /// with for the same concept! It also lets you record custom traits about the 176 | /// group, like industry or number of employees. 177 | /// 178 | /// 179 | /// The visitor's database identifier after they log in, or you know 180 | /// who they are. By explicitly grouping a user, you tie all of their actions to their group. 181 | /// 182 | /// The group's database identifier after they log in, or you know 183 | /// who they are. 184 | /// 185 | /// A dictionary with group keys like "name", “subscriptionPlan”. 186 | /// You can segment your users by any trait you record. Pass in values in key-value format. 187 | /// String key, then its value { String, Integer, Boolean, Double, or Date are acceptable types for a value. } 188 | /// 189 | public void Group(string userId, string groupId, IDictionary traits) 190 | { 191 | Group(userId, groupId, traits, null); 192 | } 193 | 194 | /// 195 | /// The `group` method lets you associate a user with a group. Be it a company, 196 | /// organization, account, project, team or whatever other crazy name you came up 197 | /// with for the same concept! It also lets you record custom traits about the 198 | /// group, like industry or number of employees. 199 | /// 200 | /// 201 | /// The visitor's database identifier after they log in, or you know 202 | /// who they are. By explicitly grouping a user, you tie all of their actions to their group. 203 | /// 204 | /// The group's database identifier after they log in, or you know 205 | /// who they are. 206 | /// 207 | /// A dictionary with group keys like "name", “subscriptionPlan”. 208 | /// You can segment your users by any trait you record. Pass in values in key-value format. 209 | /// String key, then its value { String, Integer, Boolean, Double, or Date are acceptable types for a value. } 210 | /// 211 | /// Options allowing you to set timestamp, anonymousId, target integrations, 212 | /// and the context of th emessage. 213 | /// 214 | public void Group(string userId, string groupId, IDictionary traits, Options options) 215 | { 216 | ensureId(userId, options); 217 | 218 | if (String.IsNullOrEmpty(groupId)) 219 | throw new InvalidOperationException("Please supply a valid groupId to call #Group."); 220 | 221 | Enqueue(new Group(userId, groupId, traits, options)); 222 | } 223 | 224 | #endregion 225 | 226 | #region Track 227 | 228 | /// 229 | /// Whenever a user triggers an event on your site, you’ll want to track it. 230 | /// 231 | /// 232 | /// The visitor's identifier after they log in, or you know 233 | /// who they are. 234 | /// 235 | /// The event name you are tracking. It is recommended 236 | /// that it is in human readable form. For example, "Bought T-Shirt" 237 | /// or "Started an exercise" 238 | /// 239 | public void Track(string userId, string eventName) 240 | { 241 | Track(userId, eventName, null, null); 242 | } 243 | 244 | /// 245 | /// Whenever a user triggers an event on your site, you’ll want to track it. 246 | /// 247 | /// 248 | /// The visitor's identifier after they log in, or you know 249 | /// who they are. 250 | /// 251 | /// The event name you are tracking. It is recommended 252 | /// that it is in human readable form. For example, "Bought T-Shirt" 253 | /// or "Started an exercise" 254 | /// 255 | /// A dictionary with items that describe the event 256 | /// in more detail. This argument is optional, but highly recommended — 257 | /// you’ll find these properties extremely useful later. 258 | /// 259 | public void Track(string userId, string eventName, IDictionary properties) 260 | { 261 | Track(userId, eventName, properties, null); 262 | } 263 | 264 | /// 265 | /// Whenever a user triggers an event on your site, you’ll want to track it 266 | /// so that you can analyze and segment by those events later. 267 | /// 268 | /// 269 | /// The visitor's identifier after they log in, or you know 270 | /// who they are. By 271 | /// explicitly identifying a user, you tie all of their actions to their identity. 272 | /// This makes it possible for you to run things like segment-based email campaigns. 273 | /// 274 | /// The event name you are tracking. It is recommended 275 | /// that it is in human readable form. For example, "Bought T-Shirt" 276 | /// or "Started an exercise" 277 | /// 278 | /// Options allowing you to set timestamp, anonymousId, target integrations, 279 | /// and the context of th emessage. 280 | /// 281 | /// 282 | public void Track(string userId, string eventName, Options options) 283 | { 284 | Track(userId, eventName, null, options); 285 | } 286 | 287 | /// 288 | /// Whenever a user triggers an event on your site, you’ll want to track it 289 | /// so that you can analyze and segment by those events later. 290 | /// 291 | /// 292 | /// The visitor's identifier after they log in, or you know 293 | /// who they are. By 294 | /// explicitly identifying a user, you tie all of their actions to their identity. 295 | /// This makes it possible for you to run things like segment-based email campaigns. 296 | /// 297 | /// The event name you are tracking. It is recommended 298 | /// that it is in human readable form. For example, "Bought T-Shirt" 299 | /// or "Started an exercise" 300 | /// 301 | /// A dictionary with items that describe the event 302 | /// in more detail. This argument is optional, but highly recommended — 303 | /// you’ll find these properties extremely useful later. 304 | /// 305 | /// Options allowing you to set timestamp, anonymousId, target integrations, 306 | /// and the context of th emessage. 307 | /// 308 | /// 309 | public void Track(string userId, string eventName, IDictionary properties, Options options) 310 | { 311 | ensureId(userId, options); 312 | 313 | if (String.IsNullOrEmpty(eventName)) 314 | throw new InvalidOperationException("Please supply a valid event to Track."); 315 | 316 | Enqueue(new Track(userId, eventName, properties, options)); 317 | } 318 | 319 | #endregion 320 | 321 | #region Alias 322 | 323 | /// 324 | /// Aliases an anonymous user into an identified user. 325 | /// 326 | /// 327 | /// The anonymous user's id before they are logged in. 328 | /// 329 | /// the identified user's id after they're logged in. 330 | /// 331 | public void Alias(string previousId, string userId) 332 | { 333 | Alias(previousId, userId, null); 334 | } 335 | 336 | /// 337 | /// Aliases an anonymous user into an identified user. 338 | /// 339 | /// 340 | /// The anonymous user's id before they are logged in. 341 | /// 342 | /// the identified user's id after they're logged in. 343 | /// 344 | /// Options allowing you to set timestamp, anonymousId, target integrations, 345 | /// and the context of th emessage. 346 | /// 347 | public void Alias(string previousId, string userId, Options options) 348 | { 349 | if (String.IsNullOrEmpty(previousId)) 350 | throw new InvalidOperationException("Please supply a valid 'previousId' to Alias."); 351 | 352 | if (String.IsNullOrEmpty(userId)) 353 | throw new InvalidOperationException("Please supply a valid 'to' to Alias."); 354 | 355 | Enqueue(new Alias(previousId, userId, options)); 356 | } 357 | 358 | #endregion 359 | 360 | #region Page 361 | 362 | /// 363 | /// The `page` method let your record whenever a user sees a webpage on 364 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 365 | /// 366 | /// 367 | /// The visitor's identifier after they log in, or you know 368 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 369 | /// This makes it possible for you to run things like segment-based email campaigns. 370 | /// 371 | /// The name of the webpage, like "Signup", "Login" 372 | /// 373 | public void Page(string userId, string name) 374 | { 375 | Page(userId, name, null, null, null); 376 | } 377 | 378 | /// 379 | /// The `page` method let your record whenever a user sees a webpage on 380 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 381 | /// 382 | /// 383 | /// The visitor's identifier after they log in, or you know 384 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 385 | /// This makes it possible for you to run things like segment-based email campaigns. 386 | /// 387 | /// The name of the webpage, like "Signup", "Login" 388 | /// 389 | /// Options allowing you to set timestamp, anonymousId, target integrations, 390 | /// and the context of th emessage. 391 | /// 392 | public void Page(string userId, string name, Options options) 393 | { 394 | Page(userId, name, null, null, options); 395 | } 396 | 397 | /// 398 | /// The `page` method let your record whenever a user sees a webpage on 399 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 400 | /// 401 | /// 402 | /// The visitor's identifier after they log in, or you know 403 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 404 | /// This makes it possible for you to run things like segment-based email campaigns. 405 | /// 406 | /// The name of the webpage, like "Signup", "Login" 407 | /// 408 | /// The (optional) category of the webpage, like "Authentication", "Sports" 409 | /// 410 | public void Page(string userId, string name, string category) 411 | { 412 | Page(userId, name, category, null, null); 413 | } 414 | 415 | /// 416 | /// The `page` method let your record whenever a user sees a webpage on 417 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 418 | /// 419 | /// 420 | /// The visitor's identifier after they log in, or you know 421 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 422 | /// This makes it possible for you to run things like segment-based email campaigns. 423 | /// 424 | /// The name of the webpage, like "Signup", "Login" 425 | /// 426 | /// A dictionary with items that describe the page 427 | /// in more detail. This argument is optional, but highly recommended — 428 | /// you’ll find these properties extremely useful later. 429 | /// 430 | public void Page(string userId, string name, IDictionary properties) 431 | { 432 | Page(userId, name, null, properties, null); 433 | } 434 | 435 | /// 436 | /// The `page` method let your record whenever a user sees a webpage on 437 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 438 | /// 439 | /// 440 | /// The visitor's identifier after they log in, or you know 441 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 442 | /// This makes it possible for you to run things like segment-based email campaigns. 443 | /// 444 | /// The name of the webpage, like "Signup", "Login" 445 | /// 446 | /// A dictionary with items that describe the page 447 | /// in more detail. This argument is optional, but highly recommended — 448 | /// you’ll find these properties extremely useful later. 449 | /// 450 | /// Options allowing you to set timestamp, anonymousId, target integrations, 451 | /// and the context of th emessage. 452 | /// 453 | public void Page(string userId, string name, IDictionary properties, Options options) 454 | { 455 | Page(userId, name, null, properties, options); 456 | } 457 | 458 | /// 459 | /// The `page` method let your record whenever a user sees a webpage on 460 | /// your website, and attach a `name`, `category` or `properties` to the webpage load. 461 | /// 462 | /// 463 | /// The visitor's identifier after they log in, or you know 464 | /// who they are. By explicitly identifying a user, you tie all of their actions to their identity. 465 | /// This makes it possible for you to run things like segment-based email campaigns. 466 | /// 467 | /// The name of the webpage, like "Signup", "Login" 468 | /// 469 | /// The (optional) category of the mobile screen, like "Authentication", "Sports" 470 | /// 471 | /// A dictionary with items that describe the page 472 | /// in more detail. This argument is optional, but highly recommended — 473 | /// you’ll find these properties extremely useful later. 474 | /// 475 | /// Options allowing you to set timestamp, anonymousId, target integrations, 476 | /// and the context of th emessage. 477 | /// 478 | public void Page(string userId, string name, string category, IDictionary properties, Options options) 479 | { 480 | ensureId(userId, options); 481 | 482 | if (String.IsNullOrEmpty(name)) 483 | throw new InvalidOperationException("Please supply a valid name to #Page."); 484 | 485 | Enqueue(new Page(userId, name, category, properties, options)); 486 | } 487 | 488 | #endregion 489 | 490 | #region Screen 491 | 492 | /// 493 | /// The `screen` method let your record whenever a user sees a mobile screen on 494 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 495 | /// 496 | /// 497 | /// The visitor's identifier after they log in, or you know 498 | /// who they are. By 499 | /// explicitly identifying a user, you tie all of their actions to their identity. 500 | /// This makes it possible for you to run things like segment-based email campaigns. 501 | /// 502 | /// The name of the mobile screen, like "Signup", "Login" 503 | /// 504 | public void Screen(string userId, string name) 505 | { 506 | Screen(userId, name, null, null, null); 507 | } 508 | 509 | /// 510 | /// The `screen` method let your record whenever a user sees a mobile screen on 511 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 512 | /// 513 | /// 514 | /// The visitor's identifier after they log in, or you know 515 | /// who they are. By 516 | /// explicitly identifying a user, you tie all of their actions to their identity. 517 | /// This makes it possible for you to run things like segment-based email campaigns. 518 | /// 519 | /// The name of the mobile screen, like "Signup", "Login" 520 | /// 521 | /// Options allowing you to set timestamp, anonymousId, target integrations, 522 | /// and the context of th emessage. 523 | /// 524 | public void Screen(string userId, string name, Options options) 525 | { 526 | Screen(userId, name, null, null, options); 527 | } 528 | 529 | /// 530 | /// The `screen` method let your record whenever a user sees a mobile screen on 531 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 532 | /// 533 | /// 534 | /// The visitor's identifier after they log in, or you know 535 | /// who they are. By 536 | /// explicitly identifying a user, you tie all of their actions to their identity. 537 | /// This makes it possible for you to run things like segment-based email campaigns. 538 | /// 539 | /// The name of the mobile screen, like "Signup", "Login" 540 | /// 541 | /// The (optional) category of the mobile screen, like "Authentication", "Sports" 542 | /// 543 | public void Screen(string userId, string name, string category) 544 | { 545 | Screen(userId, name, category, null, null); 546 | } 547 | 548 | /// 549 | /// The `screen` method let your record whenever a user sees a mobile screen on 550 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 551 | /// 552 | /// 553 | /// The visitor's identifier after they log in, or you know 554 | /// who they are. By 555 | /// explicitly identifying a user, you tie all of their actions to their identity. 556 | /// This makes it possible for you to run things like segment-based email campaigns. 557 | /// 558 | /// The name of the mobile screen, like "Signup", "Login" 559 | /// 560 | /// A dictionary with items that describe the screen 561 | /// in more detail. This argument is optional, but highly recommended — 562 | /// you’ll find these properties extremely useful later. 563 | /// 564 | public void Screen(string userId, string name, IDictionary properties) 565 | { 566 | Screen(userId, name, null, properties, null); 567 | } 568 | 569 | /// 570 | /// The `screen` method let your record whenever a user sees a mobile screen on 571 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 572 | /// 573 | /// 574 | /// The visitor's identifier after they log in, or you know 575 | /// who they are. By 576 | /// explicitly identifying a user, you tie all of their actions to their identity. 577 | /// This makes it possible for you to run things like segment-based email campaigns. 578 | /// 579 | /// The name of the mobile screen, like "Signup", "Login" 580 | /// 581 | /// A dictionary with items that describe the screen 582 | /// in more detail. This argument is optional, but highly recommended — 583 | /// you’ll find these properties extremely useful later. 584 | /// 585 | /// Options allowing you to set timestamp, anonymousId, target integrations, 586 | /// and the context of th emessage. 587 | /// 588 | public void Screen(string userId, string name, IDictionary properties, Options options) 589 | { 590 | Screen(userId, name, null, properties, options); 591 | } 592 | 593 | /// 594 | /// The `screen` method let your record whenever a user sees a mobile screen on 595 | /// your mobile app, and attach a `name`, `category` or `properties` to the screen. 596 | /// 597 | /// 598 | /// The visitor's identifier after they log in, or you know 599 | /// who they are. By 600 | /// explicitly identifying a user, you tie all of their actions to their identity. 601 | /// This makes it possible for you to run things like segment-based email campaigns. 602 | /// 603 | /// The name of the mobile screen, like "Signup", "Login" 604 | /// 605 | /// The (optional) category of the mobile screen, like "Authentication", "Sports" 606 | /// 607 | /// A dictionary with items that describe the screen 608 | /// in more detail. This argument is optional, but highly recommended — 609 | /// you’ll find these properties extremely useful later. 610 | /// 611 | /// Options allowing you to set timestamp, anonymousId, target integrations, 612 | /// and the context of th emessage. 613 | /// 614 | public void Screen(string userId, string name, string category, IDictionary properties, Options options) 615 | { 616 | ensureId(userId, options); 617 | 618 | if (String.IsNullOrEmpty(name)) 619 | throw new InvalidOperationException("Please supply a valid name to #Screen."); 620 | 621 | Enqueue(new Screen(userId, name, category, properties, options)); 622 | } 623 | 624 | #endregion 625 | 626 | #region Other 627 | 628 | /// 629 | /// Blocks until all messages are flushed 630 | /// 631 | public void Flush() 632 | { 633 | _flushHandler.Flush(); 634 | } 635 | 636 | /// 637 | /// Disposes of the flushing thread and the message queue. Note, this does not call Flush() first. 638 | /// 639 | /// Call when you are finished using the . The 640 | /// method leaves the in an unusable state. After calling 641 | /// , you must release all references to the so the garbage 642 | /// collector can reclaim the memory that the was occupying. 643 | public void Dispose() 644 | { 645 | _flushHandler.Dispose(); 646 | } 647 | 648 | #endregion 649 | 650 | #endregion 651 | 652 | #region Private Methods 653 | 654 | private void Enqueue(BaseAction action) 655 | { 656 | _flushHandler.Process(action); 657 | 658 | this.Statistics.Submitted += 1; 659 | } 660 | 661 | protected void ensureId(String userId, Options options) 662 | { 663 | if (String.IsNullOrEmpty(userId) && String.IsNullOrEmpty(options?.AnonymousId)) 664 | throw new InvalidOperationException("Please supply a valid id (either userId or anonymousId."); 665 | } 666 | 667 | #endregion 668 | 669 | #region Event API 670 | 671 | internal void RaiseSuccess(BaseAction action) 672 | { 673 | if (Succeeded != null) Succeeded(action); 674 | } 675 | 676 | internal void RaiseFailure(BaseAction action, System.Exception e) 677 | { 678 | if (Failed != null) Failed(action, e); 679 | } 680 | 681 | #endregion 682 | } 683 | } 684 | --------------------------------------------------------------------------------