Used to send id information to analytics; the most natural way to use this is to load it from your Settings.Default each time you run, even if you don't know these things yet because the user hasn't registered yet. Then even if they register while offline, eventually this informatino will be sent when they *are* online.
 public static UserInfo GetAnalyticsUserInfo()
 {
     UserInfo userInfo = new UserInfo()
         {
             FirstName = SIL.Windows.Forms.Registration.Registration.Default.FirstName,
             LastName = SIL.Windows.Forms.Registration.Registration.Default.Surname,
             Email = SIL.Windows.Forms.Registration.Registration.Default.Email,
             UILanguageCode = Settings.Default.UserInterfaceLanguage
         };
     userInfo.OtherProperties.Add("Organization", SIL.Windows.Forms.Registration.Registration.Default.Organization);
     userInfo.OtherProperties.Add("HowUsing", SIL.Windows.Forms.Registration.Registration.Default.HowUsing);
     return userInfo;
 }
		static void Main(string[] args)
		{
			if (args.Length == 0)
			{
				Console.WriteLine("Usage: SampleApp <segmentioApiSecret>");
			}

			var userInfo = new UserInfo()
				{
					FirstName = "John",
					LastName = "Smith",
					Email="*****@*****.**",
					UILanguageCode= "fr"
				};
			userInfo.OtherProperties.Add("HowIUseIt","This is a really long explanation of how I use this product to see how much you would be able to extract from Mixpanel.\r\nAnd a second line of it.");

			using (new Analytics(args[0], userInfo))
			{
				DesktopAnalytics.Analytics.Track("SomeEvent", new Dictionary<string, string>() {{"SomeValue", "62"}});
				Segment.Analytics.Client.Flush();
				Console.WriteLine("Sleeping for a 20 seconds to give it all a chance to send an event in the background..."); 
				Thread.Sleep(20000);
			}
		}
		static void Main(string[] args)
		{
			if (args.Length == 0)
			{
				Console.WriteLine("Usage: SampleApp <segmentioApiSecret>");
			}

			var userInfo = new UserInfo()
				{
					FirstName = "John",
					LastName = "Smith",
					Email="*****@*****.**",
					UILanguageCode= "fr"
				};
			userInfo.OtherProperties.Add("HowIUseIt","This is a really long explanation of how I use this product to see how much you would be able to extract from Mixpanel.\r\nAnd a second line of it.");

			var propertiesThatGoWithEveryEvent = new Dictionary<string, string> {{"channel", "beta"}};
			using (new Analytics(args[0], userInfo, propertiesThatGoWithEveryEvent))
			{
				Thread.Sleep(3000);
				//note that anything we set from here on didn't make it into the initial "Launch" event. Things we want to 
				//be in that event should go in the propertiesThatGoWithEveryEvent parameter of the constructor.

				DesktopAnalytics.Analytics.SetApplicationProperty("TimeSinceLaunch", "3 seconds");
				DesktopAnalytics.Analytics.Track("SomeEvent", new Dictionary<string, string>() {{"SomeValue", "62"}});
				Segment.Analytics.Client.Flush();
				Console.WriteLine("Sleeping for 20 seconds to give it all a chance to send an event in the background...");
				Thread.Sleep(20000);

				DesktopAnalytics.Analytics.SetApplicationProperty("TimeSinceLaunch", "23 seconds");
				DesktopAnalytics.Analytics.Track("SomeEvent", new Dictionary<string, string>() {{"SomeValue", "42"}});
				Segment.Analytics.Client.Flush();
				Console.WriteLine("Sleeping for another 20 seconds to give it all a chance to send an event in the background...");
				Thread.Sleep(20000);
			}
		}
		/// <summary>
		/// Initialized a singleton; after calling this, use Analytics.Track() for each event.
		/// </summary>
		/// <param name="apiSecret">The segment.com apiSecret</param>
		/// <param name="userInfo">Information about the user that you have previous collected</param>
		/// <param name="propertiesThatGoWithEveryEvent">A set of key-value pairs to send with *every* event</param>
		/// <param name="allowTracking">If false, this will not do any communication with segment.io</param>
		public Analytics(string apiSecret, UserInfo userInfo, Dictionary<string, string> propertiesThatGoWithEveryEvent, bool allowTracking = true)
		{
			if (_singleton != null)
			{
				throw new ApplicationException("You can only construct a single Analytics object.");
			}
			_singleton = this;
			_propertiesThatGoWithEveryEvent = propertiesThatGoWithEveryEvent;

			_userInfo = userInfo;

			AllowTracking = allowTracking;
			//UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the clien has given us a different url to us
			if (string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress))
				UrlThatReturnsExternalIpAddress = "http://icanhazip.com";//went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;


			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			const string UserConfigFileName = "user.config";

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{
				// Apparently a first-time install. Any chance we can migrate settings from another channel of this app?
				// We really want to use the same ID if possible to keep our statistics valid.

				// We need to get the company name and exe name of the main application, without introducing a dependency on
				// Windows.Forms, so we can't use the Windows.Forms.Application methods.
				var entryAssembly = Assembly.GetEntryAssembly(); // the main exe assembly
				var productExe = Path.GetFileNameWithoutExtension(entryAssembly.Location);
				AssemblyCompanyAttribute companyAttribute = AssemblyCompanyAttribute.GetCustomAttribute(entryAssembly, typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
				if (companyAttribute != null && !string.IsNullOrEmpty(productExe))
				{
					string companyName = companyAttribute.Company;
					var settingsLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
						companyName);
					// Coincidentally, 5 is a good length for Bloom...better heuristic later?
					// For example, we could
					// - look for the last capital letter, and truncate to there. BloomAlpha->Bloom; HearThisAlpha->HearThis; *HearThis->Hear; TEX->?TE; TEXAlpha->TEX; BloomBetaOne->*BloomBeta
					// - look for the first non-initial capital letter, and truncate from there on. BloomAlpha->Bloom, HearThisAlpha->*Hear; HearThis->*Hear; TEX->*T' TEXAlpha->TEX; BloomBetaOne->Bloom
					// - look for a non-initial capital letter following at least one LC letter. Similar except TEX->TEX, TEXAlpha->*TEXAlpha.
					// In general, truncating too much is better than too little; too much just makes us slow, while too little may make us miss useful results.
					// It's true that truncating too much (like TEX->TE) may cause us to fetch an analytics ID from the wrong program. But even this is harmless, AFAIK.
					var index = Math.Min(5, productExe.Length);
					var prefix = productExe.Substring(0, index);
					var pattern = prefix + "*";
					var possibleParentFolders = Directory.GetDirectories(settingsLocation, pattern);
					var possibleFolders = new List<string>();
					foreach (var folder in possibleParentFolders)
					{
						possibleFolders.AddRange(Directory.GetDirectories(folder).Where(f => File.Exists(Path.Combine(f, UserConfigFileName))));
					}

					possibleFolders.Sort((first, second) =>
					{
						if (first == second)
							return 0;
						var firstConfigPath = Path.Combine(first, UserConfigFileName);
						var secondConfigPath = Path.Combine(second, UserConfigFileName);
						// Reversing the arguments like this means that second comes before first if it has a LARGER mod time.
						// That is, we end up with the most recently modified user.config first.
						return new FileInfo(secondConfigPath).LastWriteTimeUtc.CompareTo(new FileInfo(firstConfigPath).LastWriteTimeUtc);
					});
					foreach (var folder in possibleFolders)
					{
						try
						{
							var doc = XDocument.Load(Path.Combine(folder, UserConfigFileName));
							var idSetting =
								doc.XPathSelectElement(
									"configuration/userSettings/DesktopAnalytics.AnalyticsSettings/setting[@name='IdForAnalytics']");
							if (idSetting == null)
								continue;
							string analyticsId = idSetting.Value;
							if (string.IsNullOrEmpty(analyticsId))
								continue;
							AnalyticsSettings.Default.IdForAnalytics = analyticsId;
							AnalyticsSettings.Default.FirstName = ExtractSetting(AnalyticsSettings.Default.FirstName, doc, "FirstName");
							AnalyticsSettings.Default.LastName = ExtractSetting(AnalyticsSettings.Default.LastName, doc, "LastName");
							AnalyticsSettings.Default.LastVersionLaunched = ExtractSetting(AnalyticsSettings.Default.LastVersionLaunched, doc, "LastVersionLaunched");
							AnalyticsSettings.Default.Email = ExtractSetting(AnalyticsSettings.Default.Email, doc, "Email");
							AnalyticsSettings.Default.Save();
							break;
						}
						catch (Exception)
						{
							// If anything goes wrong we just won't try to get our ID from this source.
						}
					}
				}
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

			UpdateSegmentIOInformationOnThisUser();
			ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can
			string versionNumberWithBuild = "";
			try
			{
				versionNumberWithBuild = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
			}
			catch (NullReferenceException)
			{
				try
				{
					// GetEntryAssembly is null for MAF plugins
					versionNumberWithBuild = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
				}
				catch (NullReferenceException)
				{
					// This probably can't happen, but if it does, just roll with it.
				}
			}
			string versionNumber = versionNumberWithBuild.Split('.').Take(2).Aggregate((a, b) => a + "." + b);
			SetApplicationProperty("Version", versionNumber);
			SetApplicationProperty("FullVersion", versionNumberWithBuild);
			SetApplicationProperty("UserName", GetUserNameForEvent());
			SetApplicationProperty("Browser", GetOperatingSystemLabel());


			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithApplicationProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != versionNumberWithBuild)
			{
				TrackWithApplicationProperties("Upgrade", new Properties
					{
						{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
					});
			}

			//we want to record the launch event independent of whether we also recorded a special first launch
			// But that is done after we retrieve (or fail to retrieve) our external ip address.
			// See http://issues.bloomlibrary.org/youtrack/issue/BL-4011.

			AnalyticsSettings.Default.LastVersionLaunched = versionNumberWithBuild;
			AnalyticsSettings.Default.Save();

		}
		public Analytics(string apiSecret, UserInfo userInfo, bool allowTracking = true)
			: this(apiSecret, userInfo, new Dictionary<string, string>(), allowTracking)
		{

		}
		/// <summary>
		/// Use this after showing a registration dialog, so that this stuff is sent right away, rather than the next time you start up Analytics
		/// </summary>
		public static void IdentifyUpdate(UserInfo userInfo)
		{
			_userInfo = userInfo;
			UpdateSegmentIOInformationOnThisUser();
		}
		/// <summary>
		/// Initialized a singleton; after calling this, use Analytics.Track() for each event.
		/// </summary>
		/// <param name="apiSecret">The segment.com apiSecret</param>
		/// <param name="userInfo">Information about the user that you have previous collected</param>
		/// <param name="propertiesThatGoWithEveryEvent">A set of key-value pairs to send with *every* event</param>
		/// <param name="allowTracking">If false, this will not do any communication with segment.io</param>
		public Analytics(string apiSecret, UserInfo userInfo, Dictionary<string, string> propertiesThatGoWithEveryEvent, bool allowTracking = true)
		{
			if (_singleton != null)
			{
				throw new ApplicationException("You can only construct a single Analytics object.");
			}
			_singleton = this;
			_propertiesThatGoWithEveryEvent = propertiesThatGoWithEveryEvent;

			_userInfo = userInfo;

			AllowTracking = allowTracking;
			//UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the client has given us a different url to us
			if (string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress))
				UrlThatReturnsExternalIpAddress = "http://icanhazip.com"; //went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;


			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{
				// Apparently a first-time install. Any chance we can migrate settings from another channel of this app?
				// We really want to use the same ID if possible to keep our statistics valid.
				try
				{
					AttemptToGetUserIdSettingsFromDifferentChannel();
				}
				catch (Exception)
				{
					// Oh, well, we tried.
				}
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

			UpdateSegmentIOInformationOnThisUser();
			ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can
			string versionNumberWithBuild = "";
			try
			{
				versionNumberWithBuild = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
			}
			catch (NullReferenceException)
			{
				try
				{
					// GetEntryAssembly is null for MAF plugins
					versionNumberWithBuild = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
				}
				catch (NullReferenceException)
				{
					// This probably can't happen, but if it does, just roll with it.
				}
			}
			string versionNumber = versionNumberWithBuild.Split('.').Take(2).Aggregate((a, b) => a + "." + b);
			SetApplicationProperty("Version", versionNumber);
			SetApplicationProperty("FullVersion", versionNumberWithBuild);
			SetApplicationProperty("UserName", GetUserNameForEvent());
			SetApplicationProperty("Browser", GetOperatingSystemLabel());


			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithApplicationProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != versionNumberWithBuild)
			{
				TrackWithApplicationProperties("Upgrade", new Properties
				{
					{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
				});
			}

			// We want to record the launch event independent of whether we also recorded a special first launch
			// But that is done after we retrieve (or fail to retrieve) our external ip address.
			// See http://issues.bloomlibrary.org/youtrack/issue/BL-4011.

			AnalyticsSettings.Default.LastVersionLaunched = versionNumberWithBuild;
			AnalyticsSettings.Default.Save();

		}
	    public Analytics(string apiSecret, UserInfo userInfo, bool allowTracking=true)
	    {
	        _userInfo = userInfo;

			AllowTracking = allowTracking;
	        //UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the clien has given us a different url to us
            if(string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress)) 
                UrlThatReturnsExternalIpAddress = "http://icanhazip.com";//went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;

			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

            UpdateSegmentIOInformationOnThisUser();
            ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can

		    try
		    {
				_applicationVersion = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
		    }
		    catch (NullReferenceException)
		    {
			    try
			    {
					// GetEntryAssembly is null for MAF plugins
					_applicationVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
			    }
				catch (NullReferenceException)
			    {
				    // This probably can't happen, but if it does, just roll with it.
			    }
		    }

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithDefaultProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != _applicationVersion)
			{
				TrackWithDefaultProperties("Upgrade", new Properties
					{
						{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
					});
			}
			
			//we want to record the launch event independent of whether we also recorded a special first launch

			TrackWithDefaultProperties("Launch");
			
			AnalyticsSettings.Default.LastVersionLaunched = _applicationVersion;
			AnalyticsSettings.Default.Save();

		}