Single synchronizer for calendar and mail.

This commit is contained in:
Burak Kaan Köse
2024-11-30 12:47:24 +01:00
parent 2bc5be2105
commit 14e10c038c
25 changed files with 179 additions and 78 deletions

View File

@@ -7,6 +7,7 @@ using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Authentication;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.ViewModels;
@@ -96,7 +97,7 @@ namespace Wino.Calendar.ViewModels
};
var tokenInformationResponse = await WinoServerConnectionManager
.GetResponseAsync<TokenInformation, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
createdAccount,
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
@@ -106,11 +107,11 @@ namespace Wino.Calendar.ViewModels
tokenInformationResponse.ThrowIfFailed();
var tokenInformation = tokenInformationResponse.Data;
createdAccount.Address = tokenInformation.Address;
tokenInformation.AccountId = createdAccount.Id;
//var tokenInformation = tokenInformationResponse.Data;
//createdAccount.Address = tokenInformation.Address;
//tokenInformation.AccountId = createdAccount.Id;
await AccountService.CreateAccountAsync(createdAccount, tokenInformation, null);
await AccountService.CreateAccountAsync(createdAccount, null);
// Sync profile information if supported.
if (createdAccount.IsProfileInfoSyncSupported)

View File

@@ -114,6 +114,7 @@ namespace Wino.Calendar.ViewModels
/// </summary>
private DateTime GetDisplayTypeSwitchDate()
{
var settings = PreferencesService.GetCurrentCalendarSettings();
switch (StatePersistenceService.CalendarDisplayType)
{
case CalendarDisplayType.Day:
@@ -121,10 +122,12 @@ namespace Wino.Calendar.ViewModels
return HighlightedDateRange.StartDate;
case CalendarDisplayType.Week:
// TODO: From settings
if (HighlightedDateRange.IsInRange(DateTime.Now)) return DateTime.Now.Date.GetWeekStartDateForDate(DayOfWeek.Monday);
if (HighlightedDateRange == null || HighlightedDateRange.IsInRange(DateTime.Now))
{
return DateTime.Now.Date.GetWeekStartDateForDate(settings.FirstDayOfWeek);
}
return HighlightedDateRange.StartDate.GetWeekStartDateForDate(DayOfWeek.Monday);
return HighlightedDateRange.StartDate.GetWeekStartDateForDate(settings.FirstDayOfWeek);
case CalendarDisplayType.WorkWeek:
break;
case CalendarDisplayType.Month:

View File

@@ -7,7 +7,6 @@ using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Calendar.Models.CalendarTypeStrategies;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
@@ -380,40 +379,40 @@ namespace Wino.Calendar.ViewModels
var beforeAllDay = new CalendarItem(calendarItem.StartTime.Date.AddHours(0), calendarItem.StartTime.Date.AddMinutes(30))
{
Name = "kj"
Title = "kj"
};
var allday = new CalendarItem(calendarItem.StartTime.Date.AddHours(1), calendarItem.StartTime.Date.AddHours(10).AddMinutes(59))
{
Name = "All day"
Title = "All day"
};
var test = new CalendarItem(calendarItem.StartTime.Date.AddHours(4), calendarItem.StartTime.Date.AddHours(4).AddMinutes(30))
{
Name = "test"
Title = "test"
};
var hour = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8))
{
Name = "1 h"
Title = "1 h"
};
var hourandhalf = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
{
Name = "1.5 h"
Title = "1.5 h"
};
var halfhour1 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(7).AddMinutes(30))
{
Name = "30 min"
Title = "30 min"
};
var halfhour2 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7).AddMinutes(30), calendarItem.StartTime.Date.AddHours(8))
{
Name = "30 min"
Title = "30 min"
};
var halfhour3 = new CalendarItem(calendarItem.StartTime.Date.AddHours(8), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
{
Name = "30 min"
Title = "30 min"
};
foreach (var day in eventDays)

View File

@@ -27,6 +27,9 @@
<ResourceDictionary Source="Styles/WinoCalendarTypeSelectorControl.xaml" />
<styles:CalendarItemControlResources />
<!-- Last item must always be the default theme. -->
<ResourceDictionary Source="ms-appx:///Wino.Core.UWP/AppThemes/Mica.xaml" />
</controls:XamlControlsResources.MergedDictionaries>
</controls:XamlControlsResources>
</Application.Resources>

View File

@@ -64,6 +64,7 @@ namespace Wino.Calendar
services.AddSingleton<ICalendarDialogService, DialogService>();
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
services.AddTransient<IProviderService, ProviderService>();
services.AddSingleton<IAuthenticatorConfig, CalendarAuthenticatorConfig>();
}
private void RegisterViewModels(IServiceCollection services)

View File

@@ -36,7 +36,7 @@ namespace Wino.Calendar.Controls
public override string ToString()
{
return Item?.Name ?? "NA";
return Item?.Title ?? "NA";
}
}
}

View File

@@ -75,7 +75,7 @@ namespace Wino.Calendar.Controls
// No need to handle actions. Each action requires a full measurement update.
private void EventCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => ResetMeasurements();
private double GetChildTopMargin(DateTime childStart, double availableHeight)
private double GetChildTopMargin(DateTimeOffset childStart, double availableHeight)
{
double totalMinutes = 1440;
double minutesFromStart = (childStart - DayModel.RepresentingDate).TotalMinutes;
@@ -90,7 +90,7 @@ namespace Wino.Calendar.Controls
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
=> availableWidth * calendarItemMeasurement.Left;
private double GetChildHeight(DateTime childStart, DateTime childEnd)
private double GetChildHeight(DateTimeOffset childStart, DateTimeOffset childEnd)
{
double totalMinutes = 1440;
double availableHeight = DayModel.CalendarRenderOptions.CalendarSettings.HourHeight * 24;
@@ -146,7 +146,7 @@ namespace Wino.Calendar.Controls
child.Measure(new Size(childWidth, childHeight));
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, childWidth - extraRightMargin, childHeight);
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, Math.Max(childWidth - extraRightMargin, 1), childHeight);
child.Arrange(arrangementRect);
}

View File

@@ -0,0 +1,30 @@
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.Services
{
public class CalendarAuthenticatorConfig : IAuthenticatorConfig
{
public string OutlookAuthenticatorClientId => "b19c2035-d740-49ff-b297-de6ec561b208";
public string[] OutlookScope => new string[]
{
"Calendars.Read",
"Calendars.Read.Shared",
"offline_access",
"Calendars.ReadBasic",
"Calendars.ReadWrite",
"Calendars.ReadWrite.Shared",
"User.Read"
};
public string GmailAuthenticatorClientId => "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com";
public string[] GmailScope => new string[]
{
"https://mail.google.com/",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/calendar.settings.readonly"
};
}
}

View File

@@ -18,7 +18,7 @@
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{x:Bind Item.Name, Mode=OneWay}" />
Text="{x:Bind Item.Title, Mode=OneWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>

View File

@@ -152,6 +152,7 @@
</Compile>
<Compile Include="Models\CalendarItemMeasurement.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\CalendarAuthenticatorConfig.cs" />
<Compile Include="Services\DialogService.cs" />
<Compile Include="Services\NavigationService.cs" />
<Compile Include="Services\ProviderService.cs" />

View File

@@ -1,10 +1,12 @@
using System;
using Itenso.TimePeriod;
using SQLite;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Domain.Entities.Calendar
{
public class CalendarItem
public class CalendarItem : ICalendarItem
{
[PrimaryKey]
public Guid Id { get; set; }
@@ -20,5 +22,8 @@ namespace Wino.Core.Domain.Entities.Calendar
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
public Guid CalendarId { get; set; }
[Ignore]
public TimeRange Period => new TimeRange(StartTime.Date, EndTime.Date);
}
}

View File

@@ -5,10 +5,10 @@ namespace Wino.Core.Domain.Interfaces
{
public interface ICalendarItem
{
string Name { get; }
string Title { get; }
Guid Id { get; }
DateTime StartTime { get; }
DateTime EndTime { get; }
DateTimeOffset StartTime { get; }
DateTimeOffset EndTime { get; }
TimeRange Period { get; }
}
}

View File

@@ -6,7 +6,7 @@ namespace Wino.Core.Domain.Models.Calendar
{
public class CalendarItem : ICalendarItem
{
public string Name { get; set; }
public string Title { get; set; }
public CalendarItem(DateTime startTime, DateTime endTime)
{
StartTime = startTime;
@@ -14,8 +14,8 @@ namespace Wino.Core.Domain.Models.Calendar
Period = new TimeRange(startTime, endTime);
}
public DateTime StartTime { get; }
public DateTime EndTime { get; }
public DateTimeOffset StartTime { get; }
public DateTimeOffset EndTime { get; }
public Guid Id { get; } = Guid.NewGuid();

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.MailItem;
@@ -18,16 +19,32 @@ namespace Wino.Core.Domain.Models.Synchronization
[JsonIgnore]
public IEnumerable<IMailItem> DownloadedMessages { get; set; } = [];
/// <summary>
/// Gets the new downloaded events from synchronization.
/// Server will create notifications for these events.
/// It's ignored in serialization. Client should not react to this.
/// </summary>
[JsonIgnore]
public IEnumerable<ICalendarItem> DownloadedEvents { get; set; } = [];
public ProfileInformation ProfileInformation { get; set; }
public SynchronizationCompletedState CompletedState { get; set; }
public static SynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
public static SynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages, ProfileInformation profileInformation = null)
// Mail synchronization
public static SynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages)
=> new()
{
DownloadedMessages = downloadedMessages,
CompletedState = SynchronizationCompletedState.Success
};
// Profile synchronization
public static SynchronizationResult Completed(ProfileInformation profileInformation)
=> new()
{
ProfileInformation = profileInformation,
CompletedState = SynchronizationCompletedState.Success
};

View File

@@ -31,8 +31,8 @@
<SolidColorBrush x:Key="CommandBarBorderBrushOpen" Color="Transparent" />
<Thickness x:Key="CommandBarBorderThicknessOpen">0</Thickness>
<x:Double x:Key="AppBarButtonContentHeight">19</x:Double>
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">19</x:Double>
<!--<x:Double x:Key="AppBarButtonContentHeight">19</x:Double>
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">19</x:Double>-->
<Thickness x:Key="ImapSetupDialogSubPagePadding">24,24,24,24</Thickness>
<!-- Border style for each page's root border for separation of zones. -->

View File

@@ -1,12 +0,0 @@
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Synchronizers
{
public abstract class BaseCalendarSynchronizer<TBaseRequest, TMessageType> : BaseSynchronizer<TBaseRequest>, IBaseCalendarSynchronizer
{
protected BaseCalendarSynchronizer(MailAccount account) : base(account)
{
}
}
}

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Synchronizers.Calendar
{
public class OutlookCalendarSynchronizer : BaseSynchronizer<RequestInformation>
{
public OutlookCalendarSynchronizer(MailAccount account) : base(account)
{
}
public override Task ExecuteNativeRequestsAsync(List<IRequestBundle<RequestInformation>> batchedRequests, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Http;
@@ -34,7 +35,7 @@ using Wino.Messaging.UI;
namespace Wino.Core.Synchronizers.Mail
{
public class GmailSynchronizer : BaseMailSynchronizer<IClientServiceRequest, Message>, IHttpClientFactory
public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message, Event>, IHttpClientFactory
{
public override uint BatchModificationSize => 1000;
public override uint InitialMessageDownloadCountPerFolder => 1200;
@@ -67,6 +68,7 @@ namespace Wino.Core.Synchronizers.Mail
_gmailService = new GmailService(initializer);
_peopleService = new PeopleServiceService(initializer);
_authenticator = authenticator;
_gmailChangeProcessor = gmailChangeProcessor;
}

View File

@@ -30,7 +30,7 @@ using Wino.Messaging.UI;
namespace Wino.Core.Synchronizers.Mail
{
public class ImapSynchronizer : BaseMailSynchronizer<ImapRequest, ImapMessageCreationPackage>
public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreationPackage, object>
{
private CancellationTokenSource idleDoneToken;
private CancellationTokenSource cancelInboxListeningToken = new CancellationTokenSource();

View File

@@ -38,7 +38,7 @@ using Wino.Core.Requests.Mail;
namespace Wino.Core.Synchronizers.Mail
{
public class OutlookSynchronizer : BaseMailSynchronizer<RequestInformation, Message>
public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message, Event>
{
public override uint BatchModificationSize => 20;
public override uint InitialMessageDownloadCountPerFolder => 250;
@@ -572,8 +572,6 @@ namespace Wino.Core.Synchronizers.Mail
#region Mail Integration
public override bool DelaySendOperationSynchronization() => true;
public override List<IRequestBundle<RequestInformation>> Move(BatchMoveRequest request)
@@ -925,7 +923,6 @@ namespace Wino.Core.Synchronizers.Mail
}
}
private async Task<MimeMessage> DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default)
{
var mimeContentStream = await _graphClient.Me.Messages[messageId].Content.GetAsync(null, cancellationToken).ConfigureAwait(false);

View File

@@ -23,11 +23,11 @@ using Wino.Messaging.UI;
namespace Wino.Core.Synchronizers
{
public abstract class BaseMailSynchronizer<TBaseRequest, TMessageType> : BaseSynchronizer<TBaseRequest>, IBaseMailSynchronizer
public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType> : BaseSynchronizer<TBaseRequest>, IBaseMailSynchronizer
{
protected ILogger Logger = Log.ForContext<BaseMailSynchronizer<TBaseRequest, TMessageType>>();
protected ILogger Logger = Log.ForContext<WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType>>();
protected BaseMailSynchronizer(MailAccount account) : base(account)
protected WinoSynchronizer(MailAccount account) : base(account)
{
}
@@ -186,7 +186,7 @@ namespace Wino.Core.Synchronizers
return SynchronizationResult.Failed;
}
return SynchronizationResult.Completed(null, newProfileInformation);
return SynchronizationResult.Completed(newProfileInformation);
}
// Alias sync.
@@ -300,6 +300,8 @@ namespace Wino.Core.Synchronizers
return options;
}
#region Mail/Folder Operations
public virtual bool DelaySendOperationSynchronization() => false;
public virtual List<IRequestBundle<TBaseRequest>> Move(BatchMoveRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> ChangeFlag(BatchChangeFlagRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
@@ -314,6 +316,14 @@ namespace Wino.Core.Synchronizers
public virtual List<IRequestBundle<TBaseRequest>> EmptyFolder(EmptyFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> MarkFolderAsRead(MarkFolderAsReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
#endregion
#region Calendar Operations
#endregion
/// <summary>
/// Downloads a single missing message from synchronizer and saves it to given FileId from IMailItem.
/// </summary>

View File

@@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.68.0.3592" />
<PackageReference Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
<PackageReference Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.70" />

View File

@@ -9,6 +9,7 @@ using System.Windows;
using H.NotifyIcon;
using Microsoft.Extensions.DependencyInjection;
using Windows.Storage;
using Wino.Calendar.Services;
using Wino.Core;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
@@ -92,10 +93,9 @@ namespace Wino.Server
}
else
{
// TODO: Calendar config will be added here.
services.AddSingleton<IAuthenticatorConfig, CalendarAuthenticatorConfig>();
}
return services.BuildServiceProvider();
}

View File

@@ -19,6 +19,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\Wino.Mail\Services\MailAuthenticatorConfiguration.cs" Link="Services\MailAuthenticatorConfiguration.cs" />
<Compile Include="..\Wino.Calendar\Services\CalendarAuthenticatorConfig.cs" Link="Services\CalendarAuthenticatorConfig.cs" />
<Compile Include="..\Wino.Core.UWP\Services\ConfigurationService.cs" Link="Services\ConfigurationService.cs" />
<Compile Include="..\Wino.Core.UWP\Services\NativeAppService.cs" Link="Services\NativeAppService.cs" />
<Compile Include="..\Wino.Core.UWP\Services\PreferencesService.cs" Link="Services\PreferencesService.cs" />

View File

@@ -34,6 +34,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Calendar.ViewModels",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Authentication", "Wino.Authentication\Wino.Authentication.csproj", "{A4DBA01A-F315-49E0-8428-BB99D32B20F9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Calendar", "Wino.Calendar\Wino.Calendar.csproj", "{600F4979-DB7E-409D-B7DA-B60BE4C55C35}"
EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Wino.Calendar.Packaging", "Wino.Calendar.Packaging\Wino.Calendar.Packaging.wapproj", "{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -328,6 +332,66 @@ Global
{A4DBA01A-F315-49E0-8428-BB99D32B20F9}.Release|x64.Build.0 = Release|Any CPU
{A4DBA01A-F315-49E0-8428-BB99D32B20F9}.Release|x86.ActiveCfg = Release|Any CPU
{A4DBA01A-F315-49E0-8428-BB99D32B20F9}.Release|x86.Build.0 = Release|Any CPU
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.ActiveCfg = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Build.0 = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Deploy.0 = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.ActiveCfg = Debug|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.Build.0 = Debug|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM.Deploy.0 = Debug|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.ActiveCfg = Debug|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Build.0 = Debug|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Deploy.0 = Debug|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.ActiveCfg = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Build.0 = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Deploy.0 = Debug|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.ActiveCfg = Debug|x86
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Build.0 = Debug|x86
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Deploy.0 = Debug|x86
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.ActiveCfg = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Build.0 = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Deploy.0 = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.ActiveCfg = Release|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.Build.0 = Release|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM.Deploy.0 = Release|ARM
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.ActiveCfg = Release|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Build.0 = Release|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Deploy.0 = Release|ARM64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.ActiveCfg = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Build.0 = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Deploy.0 = Release|x64
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.ActiveCfg = Release|x86
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Build.0 = Release|x86
{600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Deploy.0 = Release|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.ActiveCfg = Debug|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.Build.0 = Debug|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM.Deploy.0 = Debug|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.ActiveCfg = Debug|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.Build.0 = Debug|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|ARM64.Deploy.0 = Debug|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.ActiveCfg = Debug|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.Build.0 = Debug|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x64.Deploy.0 = Debug|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.ActiveCfg = Debug|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.Build.0 = Debug|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Debug|x86.Deploy.0 = Debug|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.Build.0 = Release|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|Any CPU.Deploy.0 = Release|Any CPU
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.ActiveCfg = Release|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.Build.0 = Release|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM.Deploy.0 = Release|ARM
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.ActiveCfg = Release|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.Build.0 = Release|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|ARM64.Deploy.0 = Release|ARM64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.ActiveCfg = Release|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.Build.0 = Release|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x64.Deploy.0 = Release|x64
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.ActiveCfg = Release|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.Build.0 = Release|x86
{7485B18C-F5AB-4ABE-BA7F-05B6623C67C8}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE