diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/MainActivity.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/MainActivity.cs index 58e94388..4e959173 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/MainActivity.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/MainActivity.cs @@ -1,5 +1,4 @@ -using System; - + using Android.App; using Android.Content.PM; using Android.Runtime; diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/O2NextGen.SmallTalk.Android.csproj b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/O2NextGen.SmallTalk.Android.csproj index 829991a9..e8387156 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/O2NextGen.SmallTalk.Android.csproj +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/O2NextGen.SmallTalk.Android.csproj @@ -93,4 +93,9 @@ + + + + + \ No newline at end of file diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Properties/AssemblyInfo.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Properties/AssemblyInfo.cs index be674359..93aa86c7 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Properties/AssemblyInfo.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Android.App; diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Resources/Resource.designer.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Resources/Resource.designer.cs index 7223b592..606a6ad9 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Resources/Resource.designer.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Android/Resources/Resource.designer.cs @@ -14,7 +14,7 @@ namespace O2NextGen.SmallTalk.Droid { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.0.11")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.2.0.155")] public partial class Resource { diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/App.xaml.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/App.xaml.cs index edb207bb..ba1521d1 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/App.xaml.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/App.xaml.cs @@ -1,18 +1,8 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.Foundation; -using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace O2NextGen.SmallTalk.UWP diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/MainPage.xaml.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/MainPage.xaml.cs index ac7dfaa2..b9ef38e4 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/MainPage.xaml.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/MainPage.xaml.cs @@ -1,19 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -namespace O2NextGen.SmallTalk.UWP +namespace O2NextGen.SmallTalk.UWP { public sealed partial class MainPage { diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/Properties/AssemblyInfo.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/Properties/AssemblyInfo.cs index cd81754d..0a5b1b5e 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/Properties/AssemblyInfo.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.UWP/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -26,4 +25,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: ComVisible(false)] \ No newline at end of file +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/AppDelegate.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/AppDelegate.cs index 55272962..76ffbd8d 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/AppDelegate.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/AppDelegate.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; - + using Foundation; using UIKit; diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Main.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Main.cs index 48d3bd57..cfa9d285 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Main.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Main.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using Foundation; -using UIKit; +using UIKit; namespace O2NextGen.SmallTalk.iOS { diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Properties/AssemblyInfo.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Properties/AssemblyInfo.cs index bc6d9c59..55e2ccee 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Properties/AssemblyInfo.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.iOS/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/App.xaml.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/App.xaml.cs index 3845a850..33ac2f14 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/App.xaml.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/App.xaml.cs @@ -16,7 +16,7 @@ private void InitApp() { //_settingsService = ViewModelLocator.Resolve(); //if (!_settingsService.UseMocks) - ViewModelLocator.UpdateDependencies(true); + ViewModelLocator.UpdateDependencies(false); } } } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Converters/OwnerConverter.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Converters/OwnerConverter.cs index 29a15331..46ea2426 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Converters/OwnerConverter.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Converters/OwnerConverter.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Text; using Xamarin.Forms; namespace O2NextGen.SmallTalk.Core.Converters diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Exceptions/ServiceAuthenticationException.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Exceptions/ServiceAuthenticationException.cs new file mode 100644 index 00000000..9852437c --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Exceptions/ServiceAuthenticationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace O2NextGen.SmallTalk.Core.Exceptions +{ + public class ServiceAuthenticationException : Exception + { + public string Content { get; } + + public ServiceAuthenticationException() + { + } + + public ServiceAuthenticationException(string content) + { + Content = content; + } + } +} \ No newline at end of file diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Extensions/ObservableExtension.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Extensions/ObservableExtension.cs new file mode 100644 index 00000000..d1a05462 --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Extensions/ObservableExtension.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace O2NextGen.SmallTalk.Core.Extensions +{ + public static class ObservableExtension + { + public static ObservableCollection ToObservableCollection(this IEnumerable source) + { + ObservableCollection collection = new ObservableCollection(); + + foreach (T item in source) + { + collection.Add(item); + } + + return collection; + } + } +} diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/GlobalSetting.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/GlobalSetting.cs new file mode 100644 index 00000000..d8af2d53 --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/GlobalSetting.cs @@ -0,0 +1,9 @@ +namespace O2NextGen.SmallTalk.Core +{ + public class GlobalSetting + { + public string GatewayChatEndpoint { get; set; } = "https://api-smalltalk.o2bus.com"; + public static GlobalSetting Instance { get; } = new GlobalSetting(); + public string HubConnectionURL { get; set; } = "http://localhost:5000/chathub"; + } +} diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Helpers/UriHelper.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Helpers/UriHelper.cs new file mode 100644 index 00000000..ae839d4f --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Helpers/UriHelper.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; + +namespace O2NextGen.SmallTalk.Core.Helpers +{ + public static class UriHelper + { + public static string CombineUri(params string[] uriParts) + { + string uri = string.Empty; + if (uriParts != null && uriParts.Count() > 0) + { + char[] trims = new char[] { '\\', '/' }; + uri = (uriParts[0] ?? string.Empty).TrimEnd(trims); + for (int i = 1; i < uriParts.Count(); i++) + { + uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims)); + } + } + return uri; + } + + internal static object CombineUri(object gatewayChatEndpoint, string v) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Core.csproj b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Core.csproj index 95e987bf..9796ffa8 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Core.csproj +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/O2NextGen.SmallTalk.Core.csproj @@ -11,6 +11,9 @@ + + + diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatService.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatService.cs index 51955800..d7299645 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatService.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatService.cs @@ -1,5 +1,9 @@ using O2NextGen.Sdk.NetCore.Models.smalltalk; +using O2NextGen.SmallTalk.Core.Extensions; +using O2NextGen.SmallTalk.Core.Helpers; +using O2NextGen.SmallTalk.Core.Services.RequestProvider; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; @@ -7,9 +11,26 @@ namespace O2NextGen.SmallTalk.Core.Services.Chat { public class ChatService : IChatService { - public Task AddMessageToSessionAsync(string message) + public ChatService(IRequestProvider requestProvider) { - throw new NotImplementedException(); + this._requestProvider = requestProvider; + } + private const string _apiUrlBase = "/api/chat"; + private readonly IRequestProvider _requestProvider; + + public async Task AddMessageToSessionAsync(string message) + { + var uri = UriHelper.CombineUri(GlobalSetting.Instance.GatewayChatEndpoint, $"{_apiUrlBase}/session/1/messages"); + + var addMessage = await _requestProvider.PostAsync(uri, new ChatMessage() + { + Id = 0, + Message = message, + RecipientId = 1, + SenderId = 2 + }); + + return addMessage; } public void GetByIdMessage(long sessionId, long id) @@ -17,19 +38,34 @@ public void GetByIdMessage(long sessionId, long id) throw new NotImplementedException(); } - public Task> GetMessageAsync() + public async Task> GetMessageAsync() { - throw new NotImplementedException(); + var uri = UriHelper.CombineUri(GlobalSetting.Instance.GatewayChatEndpoint, $"{_apiUrlBase}/session/1/messages"); + + var messages = await _requestProvider.GetAsync>(uri); + + if (messages != null) + return messages?.ToObservableCollection(); + else + return new ObservableCollection(); } - public void GetMessages(long sessionId) + public async Task GetMessagesAsync(long sessionId) { throw new NotImplementedException(); } - public Task GetSessionAsync() + public async Task GetSessionAsync() { - throw new NotImplementedException(); + var uri = UriHelper.CombineUri(GlobalSetting.Instance.GatewayChatEndpoint, $"{_apiUrlBase}/session/1"); + + var chatSession = await _requestProvider.GetAsync(uri); + + if (chatSession != null) + return chatSession; + else + return new ChatSession(); + //throw new NotImplementedException(); } public Task> GetSessionsAsync() @@ -41,5 +77,10 @@ public void Sessions(long sessionId) { throw new NotImplementedException(); } + + void IChatService.GetMessagesAsync(long sessionId) + { + throw new NotImplementedException(); + } } } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatServiceMock.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatServiceMock.cs index d08e8d0f..caf2d0e7 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatServiceMock.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/ChatServiceMock.cs @@ -13,12 +13,12 @@ public void GetByIdMessage(long sessionId, long id) throw new NotImplementedException(); } - public void GetMessages(long sessionId) + public void GetMessagesAsync(long sessionId) { throw new NotImplementedException(); } - private ObservableCollection MockSessions = new ObservableCollection + private ObservableCollection _mockSessions = new ObservableCollection { new ChatSession() { Id = 1, @@ -35,7 +35,6 @@ public void GetMessages(long sessionId) { Id = 2, Message = "Tests 2", - SenderId = 1, RecipientId = 2 }, new ChatMessage() @@ -80,7 +79,7 @@ public async Task> GetSessionsAsync() { await Task.Delay(10); - return MockSessions; + return _mockSessions; } public void Sessions(long sessionId) @@ -92,20 +91,22 @@ public async Task GetSessionAsync() { await Task.Delay(10); - return MockSessions[0]; + return _mockSessions[0]; } public async Task> GetMessageAsync() { await Task.Delay(10); - return new ObservableCollection(MockSessions[0].Messages); + return new ObservableCollection(_mockSessions[0].Messages); } - public async Task AddMessageToSessionAsync(string message) + public async Task AddMessageToSessionAsync(string message) { await Task.Delay(10); - long index = MockSessions.Count+1; - MockSessions[0].Messages.Add(new ChatMessage() { Id = index,Message=message,SenderId=1,RecipientId=2 }); + long index = _mockSessions.Count + 1; + var addMessage = new ChatMessage() { Id = index, Message = message, SenderId = 1, RecipientId = 2 }; + _mockSessions[0].Messages.Add(addMessage); + return addMessage; } } } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/IChatService.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/IChatService.cs index bf44cf68..802b71de 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/IChatService.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/Chat/IChatService.cs @@ -7,11 +7,11 @@ namespace O2NextGen.SmallTalk.Core.Services.Chat public interface IChatService { void GetByIdMessage(long sessionId, long id); - void GetMessages(long sessionId); + void GetMessagesAsync(long sessionId); void Sessions(long sessionId); Task> GetSessionsAsync(); Task GetSessionAsync(); Task> GetMessageAsync(); - Task AddMessageToSessionAsync(string message); + Task AddMessageToSessionAsync(string message); } } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/HttpRequestExceptionEx.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/HttpRequestExceptionEx.cs new file mode 100644 index 00000000..efa2a144 --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/HttpRequestExceptionEx.cs @@ -0,0 +1,24 @@ +using System; +using System.Net.Http; + +namespace O2NextGen.SmallTalk.Core.Services.RequestProvider +{ + public class HttpRequestExceptionEx : HttpRequestException + { + public System.Net.HttpStatusCode HttpCode { get; } + public HttpRequestExceptionEx(System.Net.HttpStatusCode code) : this(code, null, null) + { + } + + public HttpRequestExceptionEx(System.Net.HttpStatusCode code, string message) : this(code, message, null) + { + } + + public HttpRequestExceptionEx(System.Net.HttpStatusCode code, string message, Exception inner) : base(message, + inner) + { + HttpCode = code; + } + + } +} diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/IRequestProvider.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/IRequestProvider.cs new file mode 100644 index 00000000..128bc973 --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/IRequestProvider.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace O2NextGen.SmallTalk.Core.Services.RequestProvider +{ + public interface IRequestProvider + { + Task GetAsync(string uri, string token = ""); + + Task PostAsync(string uri, TResult data, string token = "", string header = ""); + + Task PostAsync(string uri, string data, string clientId, string clientSecret); + + Task PutAsync(string uri, TResult data, string token = "", string header = ""); + + Task DeleteAsync(string uri, string token = ""); + } +} \ No newline at end of file diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/RequestProvider.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/RequestProvider.cs new file mode 100644 index 00000000..21491803 --- /dev/null +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Services/RequestProvider/RequestProvider.cs @@ -0,0 +1,164 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using O2NextGen.SmallTalk.Core.Exceptions; +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace O2NextGen.SmallTalk.Core.Services.RequestProvider +{ + public class RequestProvider : IRequestProvider + { + private readonly JsonSerializerSettings _serializerSettings; + + public RequestProvider() + { + _serializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + NullValueHandling = NullValueHandling.Ignore + }; + _serializerSettings.Converters.Add(new StringEnumConverter()); + } + + public async Task GetAsync(string uri, string token = "") + { + HttpClient httpClient = CreateHttpClient(token); + HttpResponseMessage response = await httpClient.GetAsync(uri); + + await HandleResponse(response); + string serialized = await response.Content.ReadAsStringAsync(); + + TResult result = await Task.Run(() => + JsonConvert.DeserializeObject(serialized, _serializerSettings)); + + return result; + } + + public async Task PostAsync(string uri, TResult data, string token = "", string header = "") + { + HttpClient httpClient = CreateHttpClient(token); + + if (!string.IsNullOrEmpty(header)) + { + AddHeaderParameter(httpClient, header); + } + + var content = new StringContent(JsonConvert.SerializeObject(data)); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + HttpResponseMessage response = await httpClient.PostAsync(uri, content); + + await HandleResponse(response); + string serialized = await response.Content.ReadAsStringAsync(); + + TResult result = await Task.Run(() => + JsonConvert.DeserializeObject(serialized, _serializerSettings)); + + return result; + } + + public async Task PostAsync(string uri, string data, string clientId, string clientSecret) + { + HttpClient httpClient = CreateHttpClient(string.Empty); + + if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret)) + { + AddBasicAuthenticationHeader(httpClient, clientId, clientSecret); + } + + var content = new StringContent(data); + content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); + HttpResponseMessage response = await httpClient.PostAsync(uri, content); + + await HandleResponse(response); + string serialized = await response.Content.ReadAsStringAsync(); + + TResult result = await Task.Run(() => + JsonConvert.DeserializeObject(serialized, _serializerSettings)); + + return result; + } + + public async Task PutAsync(string uri, TResult data, string token = "", string header = "") + { + HttpClient httpClient = CreateHttpClient(token); + + if (!string.IsNullOrEmpty(header)) + { + AddHeaderParameter(httpClient, header); + } + + var content = new StringContent(JsonConvert.SerializeObject(data)); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + HttpResponseMessage response = await httpClient.PutAsync(uri, content); + + await HandleResponse(response); + string serialized = await response.Content.ReadAsStringAsync(); + + TResult result = await Task.Run(() => + JsonConvert.DeserializeObject(serialized, _serializerSettings)); + + return result; + } + + public async Task DeleteAsync(string uri, string token = "") + { + HttpClient httpClient = CreateHttpClient(token); + await httpClient.DeleteAsync(uri); + } + + private HttpClient CreateHttpClient(string token = "") + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + if (!string.IsNullOrEmpty(token)) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + return httpClient; + } + + private void AddHeaderParameter(HttpClient httpClient, string parameter) + { + if (httpClient == null) + return; + + if (string.IsNullOrEmpty(parameter)) + return; + + httpClient.DefaultRequestHeaders.Add(parameter, Guid.NewGuid().ToString()); + } + + private void AddBasicAuthenticationHeader(HttpClient httpClient, string clientId, string clientSecret) + { + if (httpClient == null) + return; + + if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret)) + return; + + httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret); + } + + private async Task HandleResponse(HttpResponseMessage response) + { + if (!response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + + if (response.StatusCode == HttpStatusCode.Forbidden || + response.StatusCode == HttpStatusCode.Unauthorized) + { + throw new ServiceAuthenticationException(content); + } + + throw new HttpRequestExceptionEx(response.StatusCode, content); + } + } + } +} diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/Base/ViewModelLocator.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/Base/ViewModelLocator.cs index 98b5f0fa..0574c7c8 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/Base/ViewModelLocator.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/Base/ViewModelLocator.cs @@ -1,4 +1,5 @@ using O2NextGen.SmallTalk.Core.Services.Chat; +using O2NextGen.SmallTalk.Core.Services.RequestProvider; using System; using System.Globalization; using System.Reflection; @@ -21,31 +22,38 @@ public static void SetAutoWireViewModel(BindableObject bindable, bool value) bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value); } - public static bool UseMockService { get; set; } = true; + public static bool UseMockService { get; set; } = false; static ViewModelLocator() { + //var settingsService = new SettingsService(); + var requestProvider = new RequestProvider(); + // Services - by default, TinyIoC will register interface registrations as singletons. // Xamarin.Forms.DependencyService.RegisterSingleton(new Services.Dependency.DependencyService()); + Xamarin.Forms.DependencyService.RegisterSingleton(requestProvider); Xamarin.Forms.DependencyService.RegisterSingleton(new ChatServiceMock()); // View models - by default, TinyIoC will register concrete classes as multi-instance. Xamarin.Forms.DependencyService.Register(); Xamarin.Forms.DependencyService.Register(); - + UseMockService = false; } public static void UpdateDependencies(bool useMockServices) { + var requestProvider = DependencyService.Get(); // Change injected dependencies if (useMockServices) { + Xamarin.Forms.DependencyService.RegisterSingleton(new ChatServiceMock()); UseMockService = true; } else { - Xamarin.Forms.DependencyService.RegisterSingleton(new ChatService()); + + Xamarin.Forms.DependencyService.RegisterSingleton(new ChatService(requestProvider)); UseMockService = false; } } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/ChatDetailViewModel.cs b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/ChatDetailViewModel.cs index 79ef9507..313161c7 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/ChatDetailViewModel.cs +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/ViewModels/ChatDetailViewModel.cs @@ -1,4 +1,5 @@ -using O2NextGen.Sdk.NetCore.Models.smalltalk; +using Microsoft.AspNetCore.SignalR.Client; +using O2NextGen.Sdk.NetCore.Models.smalltalk; using O2NextGen.SmallTalk.Core.Services.Chat; using O2NextGen.SmallTalk.Core.ViewModels.Base; using System; @@ -17,10 +18,13 @@ public class ChatDetailViewModel : ViewModelBase private ChatSession session; private ObservableCollection messages; private string message; + #endregion #region Commands public ICommand SendMsgCommand { get; private set; } + + private readonly HubConnection hubConnection; #endregion #region Props @@ -34,6 +38,18 @@ public ChatDetailViewModel() this.MultipleInitialization = true; _chatService = DependencyService.Get(); SendMsgCommand = new Command(async (item) => await SendMsgAsync()); + + hubConnection = new HubConnectionBuilder() + .WithUrl(GlobalSetting.Instance.HubConnectionURL) + .Build(); + + hubConnection.On("OnUpdateMessage", async () => + { + Console.WriteLine("TestMessage"); + await RelaodData(); + }); + + } public ChatSession Session @@ -69,22 +85,24 @@ public string Message public override async Task InitializeAsync(IDictionary query) { - await RelaodData(); - + + await hubConnection.StartAsync(); + await hubConnection.InvokeAsync("NewUserAsync","Denis"); + } private async Task SendMsgAsync() { await AddMessageAsync(); - await RelaodData(); Message = string.Empty; RaisePropertyChanged(() => Messages); + await RelaodData(); } private async Task RelaodData() { IsBusy = true; - Session = await _chatService.GetSessionAsync(); + //Session = await _chatService.GetSessionAsync(); Messages = await _chatService.GetMessageAsync(); IsBusy = false; } diff --git a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Views/ChatDetailView.xaml b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Views/ChatDetailView.xaml index e791f78e..e59d17b9 100644 --- a/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Views/ChatDetailView.xaml +++ b/src/MobileApps/Small-Talk/O2NextGen.SmallTalk/O2NextGen.SmallTalk/Views/ChatDetailView.xaml @@ -26,8 +26,7 @@ + Grid.Row="1"> GetMessagesAsync(long sessionId, CancellationTo return Ok(resultWithMessages.ToViewModel()); } + //[HttpGet] + //[Route("session/{sessionId}")] + //public async Task GetSessionsAsync(long sessionId, CancellationToken ct) + //{ + // var resultWithMessages = await _chatManager.GetSession(sessionId, ct); + // return Ok(resultWithMessages.ToViewModel()); + //} + [HttpPost] [HttpPut] [Route("session/{sessionId}/messages")] diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.Business/Services/IChatManager.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.Business/Services/IChatManager.cs index e5e5731c..874db808 100644 --- a/src/Services/smalltalk/O2NextGen.SmallTalk.Business/Services/IChatManager.cs +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.Business/Services/IChatManager.cs @@ -12,5 +12,6 @@ public interface IChatManager Task RemoveMessageAsync(long sessionId, long id, CancellationToken ct); Task UpdateMessageAsync(long sessionId, ChatMessageModel chatMessageModel, CancellationToken ct); Task GetMessageByIdAsync(long sessionId, long id, CancellationToken ct); + Task GetSession(long sessionId, CancellationToken ct); } } diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.Impl/Services/InMemorySessionManager.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.Impl/Services/InMemorySessionManager.cs index 5c5db9c4..19341dcb 100644 --- a/src/Services/smalltalk/O2NextGen.SmallTalk.Impl/Services/InMemorySessionManager.cs +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.Impl/Services/InMemorySessionManager.cs @@ -82,13 +82,13 @@ public InMemorySessionManager() #endregion public async Task> GetAllAsync(CancellationToken ct) { - await Task.Delay(3000, ct); + await Task.Delay(1, ct); return await Task.FromResult>(Sessions.AsReadOnly()); } public async Task AddSessionAsync(ChatSessionModel chatSession, CancellationToken ct) { - await Task.Delay(3000, ct); + await Task.Delay(1, ct); chatSession.Id = ++_currentId; Sessions.Add(chatSession); return await Task.FromResult(chatSession); @@ -108,14 +108,14 @@ public async Task GetSessionAsync(long sessionId, Cancellation public async Task> GetMessages(long idSession, CancellationToken ct) { - await Task.Delay(3000, ct); + await Task.Delay(1, ct); var messages = Sessions.Single(_ => _.Id == idSession).Messages; return await Task.FromResult>(messages.AsReadOnly()); } public async Task GetMessageByIdAsync(long idSession, long id, CancellationToken ct) { - await Task.Delay(3000, ct); + await Task.Delay(1, ct); var message = Sessions.Single(_ => _.Id == idSession).Messages.Single(_ => _.Id == id); return await Task.FromResult(message); } diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Controllers/ChatController.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Controllers/ChatController.cs new file mode 100644 index 00000000..5b7fe224 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Controllers/ChatController.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Mvc; +using O2NextGen.SmallTalk.SignalrHub.Hubs; + +namespace O2NextGen.SmallTalk.SignalrHub.Controllers +{ + [Route("api/chat")] + [ApiController] + public class ChatController : ControllerBase + { + private readonly IChatHub chatHub; + + public ChatController(IChatHub chatHub) + { + this.chatHub = chatHub; + } + [HttpGet] + public void Test() + { + chatHub.UpdateMessages(); + } + } +} diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Dockerfile b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Dockerfile new file mode 100644 index 00000000..c009a181 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Dockerfile @@ -0,0 +1,22 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:2.1 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:2.1 AS build +WORKDIR /src +COPY ["O2NextGen.SmallTalk.SignalrHub/O2NextGen.SmallTalk.SignalrHub.csproj", "O2NextGen.SmallTalk.SignalrHub/"] +RUN dotnet restore "O2NextGen.SmallTalk.SignalrHub/O2NextGen.SmallTalk.SignalrHub.csproj" +COPY . . +WORKDIR "/src/O2NextGen.SmallTalk.SignalrHub" +RUN dotnet build "O2NextGen.SmallTalk.SignalrHub.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "O2NextGen.SmallTalk.SignalrHub.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "O2NextGen.SmallTalk.SignalrHub.dll"] \ No newline at end of file diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Hubs/ChatHub.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Hubs/ChatHub.cs new file mode 100644 index 00000000..44ce7f09 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Hubs/ChatHub.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.SignalR; +using System; +using System.Threading.Tasks; + +namespace O2NextGen.SmallTalk.SignalrHub.Hubs +{ + public interface IChatHub { + Task UpdateMessages(); + } + public class ChatHub : Hub, IChatHub + { + public async Task NewUserAsync(string username) + { + await Groups.AddToGroupAsync(Context.ConnectionId, username); + await base.OnConnectedAsync(); + } + + public async Task UpdateMessages() + { + await Clients.All.SendAsync("OnUpdateMessage"); + } + + //public override async Task OnConnectedAsync() + //{ + // await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); + // await base.OnConnectedAsync(); + //} + + //public override async Task OnDisconnectedAsync(Exception ex) + //{ + // await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name); + // await base.OnDisconnectedAsync(ex); + //} + } +} diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/O2NextGen.SmallTalk.SignalrHub.csproj b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/O2NextGen.SmallTalk.SignalrHub.csproj new file mode 100644 index 00000000..06b1256c --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/O2NextGen.SmallTalk.SignalrHub.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.1 + bc1a7d3c-820d-4431-a0c7-d51b4a253b6d + Linux + + + + + + + + + + + + + + + diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Program.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Program.cs new file mode 100644 index 00000000..93e339b4 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Program.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace O2NextGen.SmallTalk.SignalrHub +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Properties/launchSettings.json b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Properties/launchSettings.json new file mode 100644 index 00000000..e4caa173 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:65388", + "sslPort": 44398 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "O2NextGen.SmallTalk.SignalrHub": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Startup.cs b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Startup.cs new file mode 100644 index 00000000..ff720698 --- /dev/null +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.SignalrHub/Startup.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using O2NextGen.SmallTalk.SignalrHub.Hubs; + +namespace O2NextGen.SmallTalk.SignalrHub +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .AllowAnyMethod() + .AllowAnyHeader() + .SetIsOriginAllowed((host) => true) + .AllowCredentials()); + }); + services.AddSingleton(); + services.AddSignalR(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + //else + //{ + // app.UseHsts(); + //} + app.UseCors("CorsPolicy"); + + //app.UseRouting(); + + //app.UseAuthentication(); + //app.UseAuthorization(); + app.UseSignalR((routes) => + { + routes.MapHub("/chathub"); + }); + app.UseMvc(); + //app.UseEndpoints(endpoints => + //{ + // endpoints.MapControllers(); + // endpoints.MapHub("/signalrtc"); + // endpoints.MapHub("/o2hub"); + //}); + //app.UseEndpoints(endpoints => + //{ + // endpoints.MapHub("/hub/chathub", + // options => options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransports.All); + //}; + // app.Run(async (context) => + //{ + // await context.Response.WriteAsync("Hello World!"); + //}); + } + } +} diff --git a/src/Services/smalltalk/O2NextGen.SmallTalk.sln b/src/Services/smalltalk/O2NextGen.SmallTalk.sln index 4589eddc..6b029700 100644 --- a/src/Services/smalltalk/O2NextGen.SmallTalk.sln +++ b/src/Services/smalltalk/O2NextGen.SmallTalk.sln @@ -27,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDKs", "SDKs", "{B03A4DA5-C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "O2NextGen.Sdk.NetCore.Models", "..\..\SDKs\O2NG.Sdk.NetCore\O2NG.Sdk.NetCore.Models\O2NextGen.Sdk.NetCore.Models.csproj", "{011B24B8-B1CA-4585-9862-916BE9A090AF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "O2NextGen.SmallTalk.SignalrHub", "O2NextGen.SmallTalk.SignalrHub\O2NextGen.SmallTalk.SignalrHub.csproj", "{03A09C6B-B835-4748-839E-33E395D8197C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,6 +75,10 @@ Global {011B24B8-B1CA-4585-9862-916BE9A090AF}.Debug|Any CPU.Build.0 = Debug|Any CPU {011B24B8-B1CA-4585-9862-916BE9A090AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {011B24B8-B1CA-4585-9862-916BE9A090AF}.Release|Any CPU.Build.0 = Release|Any CPU + {03A09C6B-B835-4748-839E-33E395D8197C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03A09C6B-B835-4748-839E-33E395D8197C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03A09C6B-B835-4748-839E-33E395D8197C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03A09C6B-B835-4748-839E-33E395D8197C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE