Merge branch 'main' into fix_tcb_winui2.6
This commit is contained in:
Коммит
ebcdf29373
|
@ -17,7 +17,7 @@
|
|||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
|
@ -46,9 +46,9 @@
|
|||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<RunSettings>
|
||||
<MSTest>
|
||||
<MaxCpuCount>0</MaxCpuCount>
|
||||
<MaxCpuCount>0</MaxCpuCount>
|
||||
<Parallelize>
|
||||
<Workers>0</Workers>
|
||||
<Scope>ClassLevel</Scope>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Parsers.Markdown;
|
||||
using Microsoft.Toolkit.Parsers.Markdown.Blocks;
|
||||
using Microsoft.Toolkit.Parsers.Markdown.Inlines;
|
||||
|
@ -407,19 +408,19 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls
|
|||
{
|
||||
get
|
||||
{
|
||||
return storage.Read<string>(DesiredLangKey);
|
||||
return settingsStorage.Read<string>(DesiredLangKey);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
storage.Save(DesiredLangKey, value);
|
||||
settingsStorage.Save(DesiredLangKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Local Storage Helper.
|
||||
/// The local app data storage helper for storing settings.
|
||||
/// </summary>
|
||||
private LocalObjectStorageHelper storage = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private readonly ApplicationDataStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// DocFX note types and styling info, keyed by identifier.
|
||||
|
|
|
@ -20,6 +20,7 @@ using System.Threading.Tasks;
|
|||
// TODO Reintroduce graph controls
|
||||
// using Microsoft.Toolkit.Graph.Converters;
|
||||
// using Microsoft.Toolkit.Graph.Providers;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Input.GazeInteraction;
|
||||
using Microsoft.Toolkit.Uwp.SampleApp.Models;
|
||||
|
@ -45,7 +46,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
|
||||
public static async void EnsureCacheLatest()
|
||||
{
|
||||
var settingsStorage = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
var settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
var onlineDocsSHA = await GetDocsSHA();
|
||||
var cacheSHA = settingsStorage.Read<string>(_cacheSHAKey);
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp
|
||||
|
@ -21,7 +22,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);
|
||||
|
||||
private static LinkedList<Sample> _recentSamples;
|
||||
private static LocalObjectStorageHelper _localObjectStorageHelper = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private static ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
public static async Task<SampleCategory> GetCategoryBySample(Sample sample)
|
||||
{
|
||||
|
@ -98,7 +99,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
if (_recentSamples == null)
|
||||
{
|
||||
_recentSamples = new LinkedList<Sample>();
|
||||
var savedSamples = _localObjectStorageHelper.Read<string>(_recentSamplesStorageKey);
|
||||
var savedSamples = _settingsStorage.Read<string>(_recentSamplesStorageKey);
|
||||
|
||||
if (savedSamples != null)
|
||||
{
|
||||
|
@ -144,7 +145,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
}
|
||||
|
||||
var str = string.Join(";", _recentSamples.Take(10).Select(s => s.Name).ToArray());
|
||||
_localObjectStorageHelper.Save<string>(_recentSamplesStorageKey, str);
|
||||
_settingsStorage.Save<string>(_recentSamplesStorageKey, str);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,19 +74,20 @@
|
|||
</controls:ListDetailsView.NoSelectionContentTemplate>
|
||||
<controls:ListDetailsView.ListCommandBar>
|
||||
<CommandBar>
|
||||
<!-- Button functionality to be implemented by developer -->
|
||||
<AppBarButton Icon="Back" Label="Back"/>
|
||||
<AppBarButton Icon="Forward" Label="Forward"/>
|
||||
|
||||
<CommandBar.Content>
|
||||
<TextBlock Margin="12,14">
|
||||
<Run Text="{Binding Emails.Count}" />
|
||||
<Run Text="Items" />
|
||||
</TextBlock>
|
||||
</CommandBar.Content>
|
||||
</CommandBar>
|
||||
<CommandBar.Content>
|
||||
<TextBlock Margin="12,14">
|
||||
<Run Text="{Binding Emails.Count}" />
|
||||
<Run Text="Items" />
|
||||
</TextBlock>
|
||||
</CommandBar.Content>
|
||||
</CommandBar>
|
||||
</controls:ListDetailsView.ListCommandBar>
|
||||
<controls:ListDetailsView.DetailsCommandBar>
|
||||
<CommandBar>
|
||||
<!-- Button functionality to be implemented by developer -->
|
||||
<AppBarButton Icon="MailReply" Label="Reply" />
|
||||
<AppBarButton Icon="MailReplyAll" Label="Reply All" />
|
||||
<AppBarButton Icon="MailForward" Label="Forward" />
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
var localObjectStorageHelper = new LocalObjectStorageHelper();
|
||||
var roamingObjectStorageHelper = new RoamingObjectStorageHelper();
|
||||
ApplicationDataStorageHelper appDataStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer());
|
||||
|
||||
// Read and Save with simple objects
|
||||
string keySimpleObject = "simple";
|
||||
string result = localObjectStorageHelper.Read<string>(keySimpleObject);
|
||||
localObjectStorageHelper.Save(keySimpleObject, 47);
|
||||
string result = appDataStorageHelper.Read<string>(keySimpleObject);
|
||||
appDataStorageHelper.Save(keySimpleObject, 47);
|
||||
|
||||
// Read and Save with complex/large objects
|
||||
string keyLargeObject = "large";
|
||||
var result = localObjectStorageHelper.ReadFileAsync<MyLargeObject>(keyLargeObject);
|
||||
string complexObjectKey = "complexObject";
|
||||
var complexObject = await appDataStorageHelper.ReadFileAsync<MyLargeObject>(complexObjectKey);
|
||||
|
||||
var o = new MyLargeObject
|
||||
var myComplexObject = new MyComplexObject()
|
||||
{
|
||||
...
|
||||
};
|
||||
localObjectStorageHelper.SaveFileAsync(keySimpleObject, o);
|
||||
await appDataStorageHelper.SaveFileAsync(complexObjectKey, myComplexObject);
|
||||
|
||||
// Complex object
|
||||
public class MyLargeObject
|
||||
public class MyComplexObject
|
||||
{
|
||||
public string MyContent { get; set; }
|
||||
public List<string> MyContents { get; set; }
|
||||
public List<MyLargeObject> MyObjects { get; set; }
|
||||
public List<MyComplexObject> MyObjects { get; set; }
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
|
@ -9,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
{
|
||||
public sealed partial class ObjectStoragePage
|
||||
{
|
||||
private readonly IObjectStorageHelper localStorageHelper = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
public ObjectStoragePage()
|
||||
{
|
||||
|
@ -24,9 +25,9 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
}
|
||||
|
||||
// Read from local storage
|
||||
if (localStorageHelper.KeyExists(KeyTextBox.Text))
|
||||
if (_settingsStorage.KeyExists(KeyTextBox.Text))
|
||||
{
|
||||
ContentTextBox.Text = localStorageHelper.Read<string>(KeyTextBox.Text);
|
||||
ContentTextBox.Text = _settingsStorage.Read<string>(KeyTextBox.Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +44,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
}
|
||||
|
||||
// Save into local storage
|
||||
localStorageHelper.Save(KeyTextBox.Text, ContentTextBox.Text);
|
||||
_settingsStorage.Save(KeyTextBox.Text, ContentTextBox.Text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ using Windows.UI.Xaml;
|
|||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using NavigationView = Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
|
@ -42,12 +43,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
private AppViewBackButtonVisibility? _previousSystemBackButtonVisibility;
|
||||
private bool _previousNavigationViewBackEnabled;
|
||||
|
||||
// Int used because the underlying type is an enum, but we don't have access to the enum
|
||||
private int _previousNavigationViewBackVisibilty;
|
||||
private NavigationView.NavigationViewBackButtonVisible _previousNavigationViewBackVisibilty;
|
||||
private NavigationView.NavigationView _navigationView;
|
||||
private ContentPresenter _detailsPresenter;
|
||||
private VisualStateGroup _selectionStateGroup;
|
||||
private Button _inlineBackButton;
|
||||
private object _navigationView;
|
||||
private Frame _frame;
|
||||
|
||||
/// <summary>
|
||||
|
@ -199,7 +199,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
_frame.Navigating -= OnFrameNavigating;
|
||||
}
|
||||
|
||||
_navigationView = this.FindAscendants().FirstOrDefault(p => p.GetType().FullName == "Microsoft.UI.Xaml.Controls.NavigationView");
|
||||
_navigationView = this.FindAscendant<NavigationView.NavigationView>();
|
||||
_frame = this.FindAscendant<Frame>();
|
||||
if (_frame != null)
|
||||
{
|
||||
|
@ -328,8 +328,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// </summary>
|
||||
private void SetBackButtonVisibility(ListDetailsViewState? previousState = null)
|
||||
{
|
||||
const int backButtonVisible = 1;
|
||||
|
||||
if (DesignMode.DesignModeEnabled)
|
||||
{
|
||||
return;
|
||||
|
@ -358,7 +356,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
else
|
||||
{
|
||||
SetNavigationViewBackButtonState(backButtonVisible, true);
|
||||
SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible.Visible, true);
|
||||
}
|
||||
}
|
||||
else if (BackButtonBehavior != BackButtonBehavior.Manual)
|
||||
|
@ -441,27 +439,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
VisualStateManager.GoToState(this, SelectedItem == null ? noSelectionState : hasSelectionState, animate);
|
||||
}
|
||||
|
||||
private void SetNavigationViewBackButtonState(int visible, bool enabled)
|
||||
private void SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible visibility, bool enabled)
|
||||
{
|
||||
if (_navigationView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var navType = _navigationView.GetType();
|
||||
var visibleProperty = navType.GetProperty("IsBackButtonVisible");
|
||||
if (visibleProperty != null)
|
||||
{
|
||||
_previousNavigationViewBackVisibilty = (int)visibleProperty.GetValue(_navigationView);
|
||||
visibleProperty.SetValue(_navigationView, visible);
|
||||
}
|
||||
_previousNavigationViewBackVisibilty = _navigationView.IsBackButtonVisible;
|
||||
_navigationView.IsBackButtonVisible = visibility;
|
||||
|
||||
var enabledProperty = navType.GetProperty("IsBackEnabled");
|
||||
if (enabledProperty != null)
|
||||
{
|
||||
_previousNavigationViewBackEnabled = (bool)enabledProperty.GetValue(_navigationView);
|
||||
enabledProperty.SetValue(_navigationView, enabled);
|
||||
}
|
||||
_previousNavigationViewBackEnabled = _navigationView.IsBackEnabled;
|
||||
_navigationView.IsBackEnabled = enabled;
|
||||
}
|
||||
|
||||
private void SetDetailsContent()
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// An extension of ApplicationDataStorageHelper with additional features for interop with the LocalCacheFolder.
|
||||
/// </summary>
|
||||
public partial class ApplicationDataStorageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the local cache folder.
|
||||
/// </summary>
|
||||
public StorageFolder CacheFolder => AppData.LocalCacheFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object from a file in the LocalCacheFolder.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that contains the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>Waiting task until completion with the object in the file.</returns>
|
||||
public Task<T> ReadCacheFileAsync<T>(string filePath, T @default = default)
|
||||
{
|
||||
return ReadFileAsync<T>(CacheFolder, filePath, @default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the listings for a folder and the item types in the LocalCacheFolder.
|
||||
/// </summary>
|
||||
/// <param name="folderPath">The path to the target folder.</param>
|
||||
/// <returns>A list of file types and names in the target folder.</returns>
|
||||
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadCacheFolderAsync(string folderPath)
|
||||
{
|
||||
return ReadFolderAsync(CacheFolder, folderPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves an object inside a file in the LocalCacheFolder.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that will contain the object.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
public Task CreateCacheFileAsync<T>(string filePath, T value)
|
||||
{
|
||||
return SaveFileAsync<T>(CacheFolder, filePath, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure a folder exists at the folder path specified in the LocalCacheFolder.
|
||||
/// </summary>
|
||||
/// <param name="folderPath">The path and name of the target folder.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
public Task CreateCacheFolderAsync(string folderPath)
|
||||
{
|
||||
return CreateFolderAsync(CacheFolder, folderPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file or folder item in the LocalCacheFolder.
|
||||
/// </summary>
|
||||
/// <param name="itemPath">The path to the item for deletion.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
public Task DeleteCacheItemAsync(string itemPath)
|
||||
{
|
||||
return DeleteItemAsync(CacheFolder, itemPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Windows.Storage;
|
||||
using Windows.System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints.
|
||||
/// </summary>
|
||||
public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationDataStorageHelper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appData">The data store to interact with.</param>
|
||||
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="Toolkit.Helpers.SystemSerializer"/>.</param>
|
||||
public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer? objectSerializer = null)
|
||||
{
|
||||
this.AppData = appData ?? throw new ArgumentNullException(nameof(appData));
|
||||
this.Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings container.
|
||||
/// </summary>
|
||||
public ApplicationDataContainer Settings => this.AppData.LocalSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the storage folder.
|
||||
/// </summary>
|
||||
public StorageFolder Folder => this.AppData.LocalFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the storage host.
|
||||
/// </summary>
|
||||
protected ApplicationData AppData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serializer for converting stored values.
|
||||
/// </summary>
|
||||
protected Toolkit.Helpers.IObjectSerializer Serializer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a new instance using ApplicationData.Current and the provided serializer.
|
||||
/// </summary>
|
||||
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="Toolkit.Helpers.SystemSerializer"/>.</param>
|
||||
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
|
||||
public static ApplicationDataStorageHelper GetCurrent(Toolkit.Helpers.IObjectSerializer? objectSerializer = null)
|
||||
{
|
||||
var appData = ApplicationData.Current;
|
||||
return new ApplicationDataStorageHelper(appData, objectSerializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a new instance using the ApplicationData for the provided user and serializer.
|
||||
/// </summary>
|
||||
/// <param name="user">App data user owner.</param>
|
||||
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="Toolkit.Helpers.SystemSerializer"/>.</param>
|
||||
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
|
||||
public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user, Toolkit.Helpers.IObjectSerializer? objectSerializer = null)
|
||||
{
|
||||
var appData = await ApplicationData.GetForUserAsync(user);
|
||||
return new ApplicationDataStorageHelper(appData, objectSerializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a setting already exists.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the setting (that contains object).</param>
|
||||
/// <returns>True if a value exists.</returns>
|
||||
public bool KeyExists(string key)
|
||||
{
|
||||
return this.Settings.Values.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single item by its key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>The TValue object.</returns>
|
||||
public T? Read<T>(string key, T? @default = default)
|
||||
{
|
||||
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
|
||||
{
|
||||
return this.Serializer.Deserialize<T>(valueString);
|
||||
}
|
||||
|
||||
return @default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryRead<T>(string key, out T? value)
|
||||
{
|
||||
if (this.Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
|
||||
{
|
||||
value = this.Serializer.Deserialize<T>(valueString);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save<T>(string key, T value)
|
||||
{
|
||||
this.Settings.Values[key] = this.Serializer.Serialize(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryDelete(string key)
|
||||
{
|
||||
return this.Settings.Values.Remove(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
this.Settings.Values.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a setting already exists in composite.
|
||||
/// </summary>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the setting (that contains object).</param>
|
||||
/// <returns>True if a value exists.</returns>
|
||||
public bool KeyExists(string compositeKey, string key)
|
||||
{
|
||||
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
|
||||
{
|
||||
return composite.ContainsKey(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve a single item by its key in composite.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="value">The value of the object retrieved.</param>
|
||||
/// <returns>The T object.</returns>
|
||||
public bool TryRead<T>(string compositeKey, string key, out T? value)
|
||||
{
|
||||
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
|
||||
{
|
||||
string compositeValue = (string)composite[key];
|
||||
if (compositeValue != null)
|
||||
{
|
||||
value = this.Serializer.Deserialize<T>(compositeValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single item by its key in composite.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>The T object.</returns>
|
||||
public T? Read<T>(string compositeKey, string key, T? @default = default)
|
||||
{
|
||||
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
|
||||
{
|
||||
if (composite.TryGetValue(key, out object valueObj) && valueObj is string value)
|
||||
{
|
||||
return this.Serializer.Deserialize<T>(value);
|
||||
}
|
||||
}
|
||||
|
||||
return @default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a group of items by its key in a composite.
|
||||
/// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application
|
||||
/// and for groups of settings which need to be treated in an atomic way.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="values">Objects to save.</param>
|
||||
public void Save<T>(string compositeKey, IDictionary<string, T> values)
|
||||
{
|
||||
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, T> setting in values)
|
||||
{
|
||||
if (composite.ContainsKey(setting.Key))
|
||||
{
|
||||
composite[setting.Key] = this.Serializer.Serialize(setting.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
composite = new ApplicationDataCompositeValue();
|
||||
foreach (KeyValuePair<string, T> setting in values)
|
||||
{
|
||||
composite.Add(setting.Key, this.Serializer.Serialize(setting.Value));
|
||||
}
|
||||
|
||||
this.Settings.Values[compositeKey] = composite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a single item by its key in composite.
|
||||
/// </summary>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <returns>A boolean indicator of success.</returns>
|
||||
public bool TryDelete(string compositeKey, string key)
|
||||
{
|
||||
if (this.TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
|
||||
{
|
||||
return composite.Remove(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T?> ReadFileAsync<T>(string filePath, T? @default = default)
|
||||
{
|
||||
return this.ReadFileAsync<T>(this.Folder, filePath, @default);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath)
|
||||
{
|
||||
return this.ReadFolderAsync(this.Folder, folderPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateFileAsync<T>(string filePath, T value)
|
||||
{
|
||||
return this.SaveFileAsync<T>(this.Folder, filePath, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateFolderAsync(string folderPath)
|
||||
{
|
||||
return this.CreateFolderAsync(this.Folder, folderPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task DeleteItemAsync(string itemPath)
|
||||
{
|
||||
return this.DeleteItemAsync(this.Folder, itemPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves an object inside a file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that will contain the object.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
public Task<StorageFile> SaveFileAsync<T>(string filePath, T value)
|
||||
{
|
||||
return this.SaveFileAsync(this.Folder, filePath, value);
|
||||
}
|
||||
|
||||
private async Task<T?> ReadFileAsync<T>(StorageFolder folder, string filePath, T? @default = default)
|
||||
{
|
||||
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath);
|
||||
return (value != null) ? this.Serializer.Deserialize<T>(value) : @default;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<(DirectoryItemType, string)>> ReadFolderAsync(StorageFolder folder, string folderPath)
|
||||
{
|
||||
var targetFolder = await folder.GetFolderAsync(folderPath);
|
||||
var items = await targetFolder.GetItemsAsync();
|
||||
|
||||
return items.Select((item) =>
|
||||
{
|
||||
var itemType = item.IsOfType(StorageItemTypes.File) ? DirectoryItemType.File
|
||||
: item.IsOfType(StorageItemTypes.Folder) ? DirectoryItemType.Folder
|
||||
: DirectoryItemType.None;
|
||||
|
||||
return (itemType, item.Name);
|
||||
});
|
||||
}
|
||||
|
||||
private Task<StorageFile> SaveFileAsync<T>(StorageFolder folder, string filePath, T value)
|
||||
{
|
||||
return StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting);
|
||||
}
|
||||
|
||||
private async Task CreateFolderAsync(StorageFolder folder, string folderPath)
|
||||
{
|
||||
await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists);
|
||||
}
|
||||
|
||||
private async Task DeleteItemAsync(StorageFolder folder, string itemPath)
|
||||
{
|
||||
var item = await folder.GetItemAsync(itemPath);
|
||||
await item.DeleteAsync();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// <summary>
|
||||
/// Shared implementation of ObjectStorageHelper.
|
||||
/// </summary>
|
||||
[Obsolete("BaseObjectStorageHelper is deprecated and has been superceded by ApplicationDataStorageHelper.")]
|
||||
public abstract class BaseObjectStorageHelper : IObjectStorageHelper
|
||||
{
|
||||
private readonly IObjectSerializer serializer;
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic serialization service.
|
||||
/// </summary>
|
||||
[Obsolete("IObjectSerializer has been migrated to the Microsoft.Toolkit (CommunityToolkit.Common) package.")]
|
||||
public interface IObjectSerializer
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -25,4 +28,4 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// <returns>The deserialized object.</returns>
|
||||
T Deserialize<T>(object value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
|
@ -11,48 +12,49 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// <summary>
|
||||
/// Service used to store data.
|
||||
/// </summary>
|
||||
[Obsolete("IObjectStorageHelper is deprecated. Please use ISettingsStorageHelper and IFileStorageHelper interfaces instead.")]
|
||||
public interface IObjectStorageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether a setting already exists.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the setting (that contains object)</param>
|
||||
/// <returns>True if a value exists</returns>
|
||||
/// <param name="key">Key of the setting (that contains object).</param>
|
||||
/// <returns>True if a value exists.</returns>
|
||||
bool KeyExists(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a setting already exists in composite.
|
||||
/// </summary>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings)</param>
|
||||
/// <param name="key">Key of the setting (that contains object)</param>
|
||||
/// <returns>True if a value exists</returns>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the setting (that contains object).</param>
|
||||
/// <returns>True if a value exists.</returns>
|
||||
bool KeyExists(string compositeKey, string key);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single item by its key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved</typeparam>
|
||||
/// <param name="key">Key of the object</param>
|
||||
/// <param name="default">Default value of the object</param>
|
||||
/// <returns>The T object</returns>
|
||||
T Read<T>(string key, T @default = default(T));
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>The T object.</returns>
|
||||
T Read<T>(string key, T @default = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single item by its key in composite.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings)</param>
|
||||
/// <param name="key">Key of the object</param>
|
||||
/// <param name="default">Default value of the object</param>
|
||||
/// <returns>The T object</returns>
|
||||
T Read<T>(string compositeKey, string key, T @default = default(T));
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>The T object.</returns>
|
||||
T Read<T>(string compositeKey, string key, T @default = default);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a single item by its key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved</typeparam>
|
||||
/// <param name="key">Key of the value saved</param>
|
||||
/// <param name="value">Object to save</param>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="key">Key of the value saved.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
void Save<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,34 +63,34 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// (refers to <see cref="SaveFileAsync{T}(string, T)"/> for complex/large objects) and for groups of settings which
|
||||
/// need to be treated in an atomic way.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings)</param>
|
||||
/// <param name="values">Objects to save</param>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
|
||||
/// <param name="values">Objects to save.</param>
|
||||
void Save<T>(string compositeKey, IDictionary<string, T> values);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a file already exists.
|
||||
/// </summary>
|
||||
/// <param name="filePath">Key of the file (that contains object)</param>
|
||||
/// <returns>True if a value exists</returns>
|
||||
/// <param name="filePath">Key of the file (that contains object).</param>
|
||||
/// <returns>True if a value exists.</returns>
|
||||
Task<bool> FileExistsAsync(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object from a file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved</typeparam>
|
||||
/// <param name="filePath">Path to the file that contains the object</param>
|
||||
/// <param name="default">Default value of the object</param>
|
||||
/// <returns>Waiting task until completion with the object in the file</returns>
|
||||
Task<T> ReadFileAsync<T>(string filePath, T @default = default(T));
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that contains the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>Waiting task until completion with the object in the file.</returns>
|
||||
Task<T> ReadFileAsync<T>(string filePath, T @default = default);
|
||||
|
||||
/// <summary>
|
||||
/// Saves an object inside a file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved</typeparam>
|
||||
/// <param name="filePath">Path to the file that will contain the object</param>
|
||||
/// <param name="value">Object to save</param>
|
||||
/// <returns>Waiting task until completion</returns>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that will contain the object.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
Task<StorageFile> SaveFileAsync<T>(string filePath, T value);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Helpers
|
||||
|
@ -9,20 +10,21 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// <summary>
|
||||
/// Store data in the Local environment (only on the current device).
|
||||
/// </summary>
|
||||
[Obsolete("LocalObjectStorageHelper is deprecated and has been superceded by the ApplicationDataStorageHelper. To upgrade, simply swap any LocalObjectStorageHelper instances with ApplicationDataStorageHelper.GetCurrent(serializer). The underlying interfaces are nearly identical but now with even more features available, such as deletion and access to user specific data stores!")]
|
||||
public class LocalObjectStorageHelper : BaseObjectStorageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalObjectStorageHelper"/> class,
|
||||
/// which can read and write data using the provided <see cref="IObjectSerializer"/>;
|
||||
/// In 6.1 and older the default Serializer was based on Newtonsoft.Json.
|
||||
/// To implement an <see cref="IObjectSerializer"/> based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration
|
||||
/// To implement an <see cref="IObjectSerializer"/> based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration.
|
||||
/// </summary>
|
||||
/// <param name="objectSerializer">The serializer to use.</param>
|
||||
public LocalObjectStorageHelper(IObjectSerializer objectSerializer)
|
||||
: base(objectSerializer)
|
||||
{
|
||||
Settings = ApplicationData.Current.LocalSettings;
|
||||
Folder = ApplicationData.Current.LocalFolder;
|
||||
this.Settings = ApplicationData.Current.LocalSettings;
|
||||
this.Folder = ApplicationData.Current.LocalFolder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// A bare-bones serializer which knows how to deal with primitive types and strings only. It will store them directly based on the <see cref="ApplicationDataContainer"/> API.
|
||||
/// It is recommended for more complex scenarios to implement your own <see cref="IObjectSerializer"/> based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration
|
||||
/// </summary>
|
||||
[Obsolete("SystemSerializer has been migrated to the Microsoft.Toolkit (CommunityToolkit.Common) package.")]
|
||||
public class SystemSerializer : IObjectSerializer
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -46,4 +47,4 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using System;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.Security.ExchangeActiveSyncProvisioning;
|
||||
|
@ -23,9 +24,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
public sealed class SystemInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="LocalObjectStorageHelper"/> instance used to save and retrieve application settings.
|
||||
/// The <see cref="ApplicationDataStorageHelper"/> instance used to save and retrieve application settings.
|
||||
/// </summary>
|
||||
private readonly LocalObjectStorageHelper _localObjectStorageHelper = new(new SystemSerializer());
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// The starting time of the current application session (since app launch or last move to foreground).
|
||||
|
@ -216,7 +217,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
if (LaunchCount > 0)
|
||||
{
|
||||
var subSessionLength = DateTime.UtcNow.Subtract(_sessionStart).Ticks;
|
||||
var uptimeSoFar = _localObjectStorageHelper.Read<long>(nameof(AppUptime));
|
||||
var uptimeSoFar = _settingsStorage.Read<long>(nameof(AppUptime));
|
||||
|
||||
return new(uptimeSoFar + subSessionLength);
|
||||
}
|
||||
|
@ -232,9 +233,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
/// <param name="duration">The amount to time to add</param>
|
||||
public void AddToAppUptime(TimeSpan duration)
|
||||
{
|
||||
var uptimeSoFar = _localObjectStorageHelper.Read<long>(nameof(AppUptime));
|
||||
var uptimeSoFar = _settingsStorage.Read<long>(nameof(AppUptime));
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(AppUptime), uptimeSoFar + duration.Ticks);
|
||||
_settingsStorage.Save(nameof(AppUptime), uptimeSoFar + duration.Ticks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -245,8 +246,8 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
LastResetTime = DateTime.UtcNow;
|
||||
LaunchCount = 0;
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(LastResetTime), LastResetTime.ToFileTimeUtc());
|
||||
_localObjectStorageHelper.Save(nameof(LaunchCount), LaunchCount);
|
||||
_settingsStorage.Save(nameof(LastResetTime), LastResetTime.ToFileTimeUtc());
|
||||
_settingsStorage.Save(nameof(LaunchCount), LaunchCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -258,8 +259,8 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
{
|
||||
if (args.PreviousExecutionState is ApplicationExecutionState.ClosedByUser or ApplicationExecutionState.NotRunning)
|
||||
{
|
||||
LaunchCount = _localObjectStorageHelper.Read<long>(nameof(LaunchCount)) + 1;
|
||||
TotalLaunchCount = _localObjectStorageHelper.Read<long>(nameof(TotalLaunchCount)) + 1;
|
||||
LaunchCount = _settingsStorage.Read<long>(nameof(LaunchCount)) + 1;
|
||||
TotalLaunchCount = _settingsStorage.Read<long>(nameof(TotalLaunchCount)) + 1;
|
||||
|
||||
// In case we upgraded the properties, make TotalLaunchCount is correct
|
||||
if (TotalLaunchCount < LaunchCount)
|
||||
|
@ -267,21 +268,21 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
TotalLaunchCount = LaunchCount;
|
||||
}
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(LaunchCount), LaunchCount);
|
||||
_localObjectStorageHelper.Save(nameof(TotalLaunchCount), TotalLaunchCount);
|
||||
_settingsStorage.Save(nameof(LaunchCount), LaunchCount);
|
||||
_settingsStorage.Save(nameof(TotalLaunchCount), TotalLaunchCount);
|
||||
|
||||
LaunchTime = DateTime.UtcNow;
|
||||
|
||||
var lastLaunch = _localObjectStorageHelper.Read<long>(nameof(LastLaunchTime));
|
||||
var lastLaunch = _settingsStorage.Read<long>(nameof(LastLaunchTime));
|
||||
|
||||
LastLaunchTime = lastLaunch != 0
|
||||
? DateTime.FromFileTimeUtc(lastLaunch)
|
||||
: LaunchTime;
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(LastLaunchTime), LaunchTime.ToFileTimeUtc());
|
||||
_localObjectStorageHelper.Save(nameof(AppUptime), 0L);
|
||||
_settingsStorage.Save(nameof(LastLaunchTime), LaunchTime.ToFileTimeUtc());
|
||||
_settingsStorage.Save(nameof(AppUptime), 0L);
|
||||
|
||||
var lastResetTime = _localObjectStorageHelper.Read<long>(nameof(LastResetTime));
|
||||
var lastResetTime = _settingsStorage.Read<long>(nameof(LastResetTime));
|
||||
|
||||
LastResetTime = lastResetTime != 0
|
||||
? DateTime.FromFileTimeUtc(lastResetTime)
|
||||
|
@ -321,20 +322,20 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
else
|
||||
{
|
||||
var subSessionLength = DateTime.UtcNow.Subtract(_sessionStart).Ticks;
|
||||
var uptimeSoFar = _localObjectStorageHelper.Read<long>(nameof(AppUptime));
|
||||
var uptimeSoFar = _settingsStorage.Read<long>(nameof(AppUptime));
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(AppUptime), uptimeSoFar + subSessionLength);
|
||||
_settingsStorage.Save(nameof(AppUptime), uptimeSoFar + subSessionLength);
|
||||
}
|
||||
}
|
||||
|
||||
private bool DetectIfFirstUse()
|
||||
{
|
||||
if (_localObjectStorageHelper.KeyExists(nameof(IsFirstRun)))
|
||||
if (_settingsStorage.KeyExists(nameof(IsFirstRun)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(IsFirstRun), true);
|
||||
_settingsStorage.Save(nameof(IsFirstRun), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -347,13 +348,13 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
// is ever called. That is, this is either the first time the app has been launched, or the first
|
||||
// time a previously existing app has run this method (or has run it after a new update of the app).
|
||||
// In this case, save the current version and report the same version as previous version installed.
|
||||
if (!_localObjectStorageHelper.KeyExists(nameof(currentVersion)))
|
||||
if (!_settingsStorage.KeyExists(nameof(currentVersion)))
|
||||
{
|
||||
_localObjectStorageHelper.Save(nameof(currentVersion), currentVersion);
|
||||
_settingsStorage.Save(nameof(currentVersion), currentVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousVersion = _localObjectStorageHelper.Read<string>(nameof(currentVersion));
|
||||
var previousVersion = _settingsStorage.Read<string>(nameof(currentVersion));
|
||||
|
||||
// There are two possible cases if the "currentVersion" key exists:
|
||||
// 1) The previous version is different than the current one. This means that the application
|
||||
|
@ -363,7 +364,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
// In this case we have nothing to do and just return the previous version installed to be the same.
|
||||
if (currentVersion != previousVersion)
|
||||
{
|
||||
_localObjectStorageHelper.Save(nameof(currentVersion), currentVersion);
|
||||
_settingsStorage.Save(nameof(currentVersion), currentVersion);
|
||||
|
||||
return (true, previousVersion.ToPackageVersion());
|
||||
}
|
||||
|
@ -374,28 +375,28 @@ namespace Microsoft.Toolkit.Uwp.Helpers
|
|||
|
||||
private DateTime DetectFirstUseTime()
|
||||
{
|
||||
if (_localObjectStorageHelper.KeyExists(nameof(FirstUseTime)))
|
||||
if (_settingsStorage.KeyExists(nameof(FirstUseTime)))
|
||||
{
|
||||
var firstUse = _localObjectStorageHelper.Read<long>(nameof(FirstUseTime));
|
||||
var firstUse = _settingsStorage.Read<long>(nameof(FirstUseTime));
|
||||
|
||||
return DateTime.FromFileTimeUtc(firstUse);
|
||||
}
|
||||
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(FirstUseTime), utcNow.ToFileTimeUtc());
|
||||
_settingsStorage.Save(nameof(FirstUseTime), utcNow.ToFileTimeUtc());
|
||||
|
||||
return utcNow;
|
||||
}
|
||||
|
||||
private PackageVersion DetectFirstVersionInstalled()
|
||||
{
|
||||
if (_localObjectStorageHelper.KeyExists(nameof(FirstVersionInstalled)))
|
||||
if (_settingsStorage.KeyExists(nameof(FirstVersionInstalled)))
|
||||
{
|
||||
return _localObjectStorageHelper.Read<string>(nameof(FirstVersionInstalled)).ToPackageVersion();
|
||||
return _settingsStorage.Read<string>(nameof(FirstVersionInstalled)).ToPackageVersion();
|
||||
}
|
||||
|
||||
_localObjectStorageHelper.Save(nameof(FirstVersionInstalled), ApplicationVersion.ToFormattedString());
|
||||
_settingsStorage.Save(nameof(FirstVersionInstalled), ApplicationVersion.ToFormattedString());
|
||||
|
||||
return ApplicationVersion;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
|
||||
namespace Microsoft.Toolkit.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Helpers methods for working with <see cref="ISettingsStorageHelper{TKey}"/> implementations.
|
||||
/// </summary>
|
||||
public static class ISettingsStorageHelperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to read the provided key and return the value.
|
||||
/// If the key is not found, the fallback value will be used instead.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of key used to lookup the object.</typeparam>
|
||||
/// <typeparam name="TValue">The type of object value expected.</typeparam>
|
||||
/// <param name="storageHelper">The storage helper instance fo read from.</param>
|
||||
/// <param name="key">The key of the target object.</param>
|
||||
/// <param name="fallback">An alternative value returned if the read fails.</param>
|
||||
/// <returns>The value of the target object, or the fallback value.</returns>
|
||||
public static TValue? GetValueOrDefault<TKey, TValue>(this ISettingsStorageHelper<TKey> storageHelper, TKey key, TValue? fallback = default)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (storageHelper.TryRead<TValue>(key, out TValue? storedValue))
|
||||
{
|
||||
return storedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the key in the storage helper instance and get the value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of key used to lookup the object.</typeparam>
|
||||
/// <typeparam name="TValue">The type of object value expected.</typeparam>
|
||||
/// <param name="storageHelper">The storage helper instance fo read from.</param>
|
||||
/// <param name="key">The key of the target object.</param>
|
||||
/// <returns>The value of the target object</returns>
|
||||
/// <exception cref="KeyNotFoundException">Throws when the key is not found in storage.</exception>
|
||||
public static TValue? Read<TKey, TValue>(this ISettingsStorageHelper<TKey> storageHelper, TKey key)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (storageHelper.TryRead<TValue>(key, out TValue? value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowKeyNotFoundException(key);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a key from storage.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of key used to lookup the object.</typeparam>
|
||||
/// <param name="storageHelper">The storage helper instance to delete from.</param>
|
||||
/// <param name="key">The key of the target object.</param>
|
||||
/// <exception cref="KeyNotFoundException">Throws when the key is not found in storage.</exception>
|
||||
public static void Delete<TKey>(this ISettingsStorageHelper<TKey> storageHelper, TKey key)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (!storageHelper.TryDelete(key))
|
||||
{
|
||||
ThrowKeyNotFoundException(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowKeyNotFoundException<TKey>(TKey key)
|
||||
{
|
||||
throw new KeyNotFoundException($"The given key '{key}' was not present");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the types of items available in a directory.
|
||||
/// </summary>
|
||||
public enum DirectoryItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// The item is neither a file or a folder.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a file type item.
|
||||
/// </summary>
|
||||
File,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a folder type item.
|
||||
/// </summary>
|
||||
Folder
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Service interface used to store data in a directory/file-system via files and folders.
|
||||
///
|
||||
/// This interface is meant to help abstract file storage operations across platforms in a library,
|
||||
/// but the actual behavior will be up to the implementer. Such as, we don't provide a sense of a current directory,
|
||||
/// so an implementor should consider using full paths to support any file operations. Otherwise, a "directory aware"
|
||||
/// implementation could be achieved with a current directory field and traversal functions, in which case relative paths would be applicable.
|
||||
/// </summary>
|
||||
public interface IFileStorageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves an object from a file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object retrieved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that contains the object.</param>
|
||||
/// <param name="default">Default value of the object.</param>
|
||||
/// <returns>Waiting task until completion with the object in the file.</returns>
|
||||
Task<T?> ReadFileAsync<T>(string filePath, T? @default = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the listings for a folder and the item types.
|
||||
/// </summary>
|
||||
/// <param name="folderPath">The path to the target folder.</param>
|
||||
/// <returns>A list of item types and names in the target folder.</returns>
|
||||
Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath);
|
||||
|
||||
/// <summary>
|
||||
/// Saves an object inside a file.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object saved.</typeparam>
|
||||
/// <param name="filePath">Path to the file that will contain the object.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
Task CreateFileAsync<T>(string filePath, T value);
|
||||
|
||||
/// <summary>
|
||||
/// Ensure a folder exists at the folder path specified.
|
||||
/// </summary>
|
||||
/// <param name="folderPath">The path and name of the target folder.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
Task CreateFolderAsync(string folderPath);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file or folder item.
|
||||
/// </summary>
|
||||
/// <param name="itemPath">The path to the item for deletion.</param>
|
||||
/// <returns>Waiting task until completion.</returns>
|
||||
Task DeleteItemAsync(string itemPath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic serialization service.
|
||||
/// </summary>
|
||||
public interface IObjectSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize an object into a string. It is recommended to use strings as the final format for objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object to serialize.</typeparam>
|
||||
/// <param name="value">The object to serialize.</param>
|
||||
/// <returns>The serialized object.</returns>
|
||||
string? Serialize<T>(T value);
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize string into an object of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the deserialized object.</typeparam>
|
||||
/// <param name="value">The string to deserialize.</param>
|
||||
/// <returns>The deserialized object.</returns>
|
||||
T Deserialize<T>(string value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Service interface used to store data using key value pairs.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of keys to use for accessing values.</typeparam>
|
||||
public interface ISettingsStorageHelper<in TKey>
|
||||
where TKey : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a single item by its key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Type of object retrieved.</typeparam>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <param name="value">The <see typeparamref="TValue"/> object for <see typeparamref="TKey"/> key.</param>
|
||||
/// <returns>A boolean indicator of success.</returns>
|
||||
bool TryRead<TValue>(TKey key, out TValue? value);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a single item by its key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">Type of object saved.</typeparam>
|
||||
/// <param name="key">Key of the value saved.</param>
|
||||
/// <param name="value">Object to save.</param>
|
||||
void Save<TValue>(TKey key, TValue value);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a single item by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the object.</param>
|
||||
/// <returns>A boolean indicator of success.</returns>
|
||||
bool TryDelete(TKey key);
|
||||
|
||||
/// <summary>
|
||||
/// Clear all keys and values from the settings store.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A bare-bones serializer which knows how to deal with primitive types and strings only.
|
||||
/// It is recommended for more complex scenarios to implement your own <see cref="IObjectSerializer"/> based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration
|
||||
/// </summary>
|
||||
public class SystemSerializer : IObjectSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Take a primitive value from storage and return it as the requested type using the <see cref="Convert.ChangeType(object, Type)"/> API.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to convert value to.</typeparam>
|
||||
/// <param name="value">Value from storage to convert.</param>
|
||||
/// <returns>Deserialized value or default value.</returns>
|
||||
public T Deserialize<T>(string value)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
|
||||
if (typeInfo.IsPrimitive || type == typeof(string))
|
||||
{
|
||||
return (T)Convert.ChangeType(value, type);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("This serializer can only handle primitive types and strings. Please implement your own IObjectSerializer for more complex scenarios.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value so that it can be serialized directly.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to serialize from.</typeparam>
|
||||
/// <param name="value">Value to serialize.</param>
|
||||
/// <returns>String representation of value.</returns>
|
||||
public string? Serialize<T>(T value)
|
||||
{
|
||||
return value?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,9 +21,10 @@
|
|||
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- .NET Standard 1.4 doesn't have the [Pure] attribute -->
|
||||
<!-- .NET Standard 1.4 doesn't have the [Pure] attribute or ValueTuple -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.4'">
|
||||
<PackageReference Include="System.Diagnostics.Contracts" Version="4.3.0" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -7,11 +7,13 @@ using System.Reflection;
|
|||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace UnitTests.UWP.Helpers
|
||||
namespace UnitTests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json.
|
||||
/// Based on <see cref="Microsoft.Toolkit.Uwp.Helpers.IObjectSerializer"/>.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
internal class JsonObjectSerializer : IObjectSerializer
|
||||
{
|
||||
public T Deserialize<T>(object value)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace UnitTests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json.
|
||||
/// Based on <see cref="Microsoft.Toolkit.Helpers.IObjectSerializer"/>.
|
||||
/// </summary>
|
||||
internal class JsonObjectSerializer2 : IObjectSerializer
|
||||
{
|
||||
public T Deserialize<T>(string value)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
|
||||
// Note: If you're creating a new app, you could just use the serializer directly.
|
||||
// This if/return combo is to maintain compatibility with 6.1.1
|
||||
if (typeInfo.IsPrimitive || type == typeof(string))
|
||||
{
|
||||
return (T)Convert.ChangeType(value, type);
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
|
||||
public string Serialize<T>(T value)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
|
||||
// Note: If you're creating a new app, you could just use the serializer directly.
|
||||
// This if/return combo is to maintain compatibility with 6.1.1
|
||||
if (typeInfo.IsPrimitive || type == typeof(string))
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,11 +6,13 @@ using System;
|
|||
using System.Text.Json;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
|
||||
namespace UnitTests.UWP.Helpers
|
||||
namespace UnitTests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Example class of writing a new <see cref="IObjectSerializer"/> that uses System.Text.Json.
|
||||
/// Based on <see cref="Microsoft.Toolkit.Uwp.Helpers.IObjectSerializer"/>.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
internal class SystemTextJsonSerializer : IObjectSerializer
|
||||
{
|
||||
public T Deserialize<T>(object value) => JsonSerializer.Deserialize<T>(value as string);
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
|
||||
namespace UnitTests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Example class of writing a new <see cref="IObjectSerializer"/> that uses System.Text.Json.
|
||||
/// Based on <see cref="Microsoft.Toolkit.Helpers.IObjectSerializer"/>.
|
||||
/// </summary>
|
||||
internal class SystemTextJsonSerializer2 : IObjectSerializer
|
||||
{
|
||||
public T Deserialize<T>(string value) => JsonSerializer.Deserialize<T>(value);
|
||||
|
||||
public string Serialize<T>(T value) => JsonSerializer.Serialize(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnitTests.Helpers
|
||||
{
|
||||
[TestClass]
|
||||
public class Test_ApplicationDataStorageHelper
|
||||
{
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent();
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer2());
|
||||
private readonly ApplicationDataStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer2());
|
||||
|
||||
/// <summary>
|
||||
/// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1.
|
||||
/// </summary>
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_CheckNewtonsoftVersion()
|
||||
{
|
||||
var version = typeof(Newtonsoft.Json.JsonSerializer).Assembly.GetName().Version;
|
||||
Assert.AreEqual(10, version.Major);
|
||||
Assert.AreEqual(0, version.Minor);
|
||||
Assert.AreEqual(0, version.Revision); // Apparently the file revision was not updated for the updated package
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_LegacyIntTest()
|
||||
{
|
||||
string key = "LifeUniverseAndEverything";
|
||||
|
||||
int input = 42;
|
||||
|
||||
// Use our previous Json layer to store value
|
||||
_settingsStorage_JsonCompat.Save(key, input);
|
||||
|
||||
// But try and read from our new system to see if it works
|
||||
int output = _settingsStorage_System.Read(key, 0);
|
||||
|
||||
Assert.AreEqual(input, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If we try and deserialize a complex type with the <see cref="Microsoft.Toolkit.Helpers.SystemSerializer"/>, we do a check ourselves and will throw our own exception.
|
||||
/// </summary>
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Test_StorageHelper_LegacyDateTestFailure()
|
||||
{
|
||||
string key = "ChristmasDay";
|
||||
|
||||
DateTime input = new DateTime(2017, 12, 25);
|
||||
|
||||
_settingsStorage_JsonCompat.Save(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
_ = _settingsStorage_System.Read(key, DateTime.Today);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Microsoft.Toolkit.Helpers.SystemSerializer"/> doesn't support complex types, since it just passes through directly.
|
||||
/// We'll get the argument exception from the <see cref="Windows.Storage.ApplicationDataContainer"/> API.
|
||||
/// </summary>
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Test_StorageHelper_DateTestFailure()
|
||||
{
|
||||
string key = "Today";
|
||||
|
||||
_settingsStorage_System.Save(key, DateTime.Today);
|
||||
_settingsStorage_System.TryRead<DateTime>(key, out _);
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_LegacyInternalClassTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
||||
UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 };
|
||||
|
||||
// simulate previous version by generating json and manually inserting it as string
|
||||
_settingsStorage_JsonCompat.Save(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
UI.Person output = _settingsStorage_JsonCompat.Read<UI.Person>(key, null);
|
||||
|
||||
Assert.IsNotNull(output);
|
||||
Assert.AreEqual(input.Name, output.Name);
|
||||
Assert.AreEqual(input.Age, output.Age);
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_LegacyPublicClassTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
||||
// Here's we're serializing a different class which has the same properties as our other class below.
|
||||
UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 };
|
||||
|
||||
// simulate previous version by generating json and manually inserting it as string
|
||||
_settingsStorage_JsonCompat.Save(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
Person output = _settingsStorage_JsonCompat.Read<Person>(key, null);
|
||||
|
||||
Assert.IsNotNull(output);
|
||||
Assert.AreEqual(input.Name, output.Name);
|
||||
Assert.AreEqual(input.Age, output.Age);
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_IntTest()
|
||||
{
|
||||
string key = "NewLifeUniverseAndEverything";
|
||||
|
||||
int input = 42;
|
||||
|
||||
_settingsStorage_System.Save<int>(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
int output = _settingsStorage_System.Read<int>(key, 0);
|
||||
|
||||
Assert.AreEqual(input, output);
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_NewDateTest()
|
||||
{
|
||||
string key = "NewChristmasDay";
|
||||
|
||||
DateTime input = new DateTime(2017, 12, 25);
|
||||
|
||||
_settingsStorage_JsonNew.Save(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
DateTime output = _settingsStorage_JsonNew.Read(key, DateTime.Today);
|
||||
|
||||
Assert.AreEqual(input, output);
|
||||
}
|
||||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
public void Test_StorageHelper_NewPersonTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
||||
Person input = new Person() { Name = "Joe Bloggs", Age = 42 };
|
||||
|
||||
_settingsStorage_JsonNew.Save(key, input);
|
||||
|
||||
// now read it as int to valid that the change works
|
||||
Person output = _settingsStorage_JsonNew.Read<Person>(key, null);
|
||||
|
||||
Assert.IsNotNull(output);
|
||||
Assert.AreEqual(input.Name, output.Name);
|
||||
Assert.AreEqual(input.Age, output.Age);
|
||||
}
|
||||
|
||||
public class Person
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Age { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ using System;
|
|||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
using UnitTests.UWP.Helpers;
|
||||
using UnitTests.Helpers;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace UnitTests.Helpers
|
||||
|
@ -14,10 +14,12 @@ namespace UnitTests.Helpers
|
|||
[TestClass]
|
||||
public class Test_StorageHelper
|
||||
{
|
||||
private LocalObjectStorageHelper _localStorageHelperSystem = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
private LocalObjectStorageHelper _localStorageHelperJsonCompat = new LocalObjectStorageHelper(new JsonObjectSerializer());
|
||||
|
||||
private LocalObjectStorageHelper _localStorageHelperJsonNew = new LocalObjectStorageHelper(new SystemTextJsonSerializer());
|
||||
[Obsolete]
|
||||
private readonly LocalObjectStorageHelper _localStorageHelperSystem = new LocalObjectStorageHelper(new SystemSerializer());
|
||||
[Obsolete]
|
||||
private readonly LocalObjectStorageHelper _localStorageHelperJsonCompat = new LocalObjectStorageHelper(new JsonObjectSerializer());
|
||||
[Obsolete]
|
||||
private readonly LocalObjectStorageHelper _localStorageHelperJsonNew = new LocalObjectStorageHelper(new SystemTextJsonSerializer());
|
||||
|
||||
/// <summary>
|
||||
/// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1.
|
||||
|
@ -34,6 +36,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_LegacyIntTest()
|
||||
{
|
||||
string key = "LifeUniverseAndEverything";
|
||||
|
@ -54,6 +57,7 @@ namespace UnitTests.Helpers
|
|||
/// </summary>
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Test_StorageHelper_LegacyDateTestFailure()
|
||||
{
|
||||
|
@ -73,6 +77,7 @@ namespace UnitTests.Helpers
|
|||
/// </summary>
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_DateTestFailure()
|
||||
{
|
||||
Exception expectedException = null;
|
||||
|
@ -93,6 +98,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_LegacyInternalClassTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
@ -112,6 +118,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_LegacyPublicClassTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
@ -132,6 +139,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_IntTest()
|
||||
{
|
||||
string key = "NewLifeUniverseAndEverything";
|
||||
|
@ -148,6 +156,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_NewDateTest()
|
||||
{
|
||||
string key = "NewChristmasDay";
|
||||
|
@ -164,6 +173,7 @@ namespace UnitTests.Helpers
|
|||
|
||||
[TestCategory("Helpers")]
|
||||
[TestMethod]
|
||||
[Obsolete]
|
||||
public void Test_StorageHelper_NewPersonTest()
|
||||
{
|
||||
string key = "Contact";
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Toolkit.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Windows.ApplicationModel;
|
||||
|
@ -57,10 +58,10 @@ namespace UnitTests.Helpers
|
|||
// Simulate a first app startup
|
||||
_ = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true);
|
||||
|
||||
LocalObjectStorageHelper localObjectStorageHelper = new(new SystemSerializer());
|
||||
var settingsStorage = ApplicationDataStorageHelper.GetCurrent();
|
||||
PackageVersion previousVersion = new() { Build = 42, Major = 1111, Minor = 2222, Revision = 12345 };
|
||||
|
||||
localObjectStorageHelper.Save("currentVersion", previousVersion.ToFormattedString());
|
||||
settingsStorage.Save("currentVersion", previousVersion.ToFormattedString());
|
||||
|
||||
var systemInformation = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true);
|
||||
var currentAppVersion = Package.Current.Id.Version;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -175,10 +175,13 @@
|
|||
<Compile Include="Geometry\Test_Utils.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="Helpers\JsonObjectSerializer.cs" />
|
||||
<Compile Include="Helpers\JsonObjectSerializer2.cs" />
|
||||
<Compile Include="Helpers\SystemTextJsonSerializer.cs" />
|
||||
<Compile Include="Helpers\SystemTextJsonSerializer2.cs" />
|
||||
<Compile Include="Helpers\TestCollectionCapableDeepLinkParser.cs" />
|
||||
<Compile Include="Helpers\TestDeepLinkParser.cs" />
|
||||
<Compile Include="Extensions\Test_DispatcherQueueExtensions.cs" />
|
||||
<Compile Include="Helpers\Test_ApplicationDataStorageHelper.cs" />
|
||||
<Compile Include="Helpers\Test_DispatcherHelper.cs" />
|
||||
<Compile Include="Helpers\Test_AdvancedCollectionView.cs" />
|
||||
<Compile Include="Helpers\Test_BackgroundTaskHelper.cs" />
|
||||
|
|
|
@ -11,14 +11,14 @@ pr:
|
|||
pool:
|
||||
vmImage: windows-2019
|
||||
|
||||
variables:
|
||||
variables:
|
||||
BuildConfiguration: Release
|
||||
|
||||
jobs:
|
||||
### BUILD ###
|
||||
- job: BuildBits
|
||||
timeoutInMinutes: 60
|
||||
|
||||
|
||||
steps:
|
||||
- task: BatchScript@1
|
||||
inputs:
|
||||
|
@ -32,7 +32,7 @@ jobs:
|
|||
inputs:
|
||||
versionSpec: 5.6.0
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
|
@ -45,12 +45,12 @@ jobs:
|
|||
#- powershell: .\build\Install-WindowsSdkISO.ps1 19041
|
||||
# displayName: Insider SDK
|
||||
|
||||
- powershell: .\build\build.ps1 -target=Build
|
||||
- powershell: .\build\build.ps1 -Target Build
|
||||
displayName: Build
|
||||
|
||||
### Unit Tests ###
|
||||
|
||||
- powershell: .\build\build.ps1 -target=Test
|
||||
|
||||
- powershell: .\build\build.ps1 -Target Test
|
||||
displayName: Test
|
||||
|
||||
- task: PublishTestResults@2
|
||||
|
@ -62,7 +62,7 @@ jobs:
|
|||
|
||||
### UI Integration Tests ###
|
||||
|
||||
- powershell: .\build\build.ps1 -target=UITest
|
||||
- powershell: .\build\build.ps1 -Target UITest
|
||||
displayName: UI Integration Tests
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
|
@ -81,7 +81,7 @@ jobs:
|
|||
|
||||
### Package ###
|
||||
|
||||
- powershell: .\build\build.ps1 -target=Package
|
||||
- powershell: .\build\build.ps1 -Target Package
|
||||
displayName: Package
|
||||
|
||||
- task: PowerShell@2
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
- job: SmokeTests
|
||||
dependsOn: BuildBits
|
||||
timeoutInMinutes: 60
|
||||
|
||||
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download NuGet Packages Artifact
|
||||
|
@ -113,7 +113,7 @@ jobs:
|
|||
artifact: Packages
|
||||
path: .\bin\nupkg
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
- task: DotNetCoreCLI@2
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
|
@ -123,7 +123,7 @@ jobs:
|
|||
- script: nbgv cloud
|
||||
displayName: Set Version
|
||||
|
||||
- powershell: .\build\build.ps1 -target=SmokeTest
|
||||
- powershell: .\build\build.ps1 -Target SmokeTest
|
||||
displayName: SmokeTest
|
||||
|
||||
- task: CopyFiles@2
|
||||
|
|
|
@ -5,17 +5,16 @@ $ErrorActionPreference = 'Stop'
|
|||
# Unique set of Windows SDK versions referenced in files
|
||||
$versions = New-Object System.Collections.Generic.HashSet[System.String]
|
||||
|
||||
function Get-Nodes
|
||||
{
|
||||
function Get-Nodes {
|
||||
param(
|
||||
[parameter(ValueFromPipeline=$true)]
|
||||
[xml] $xml,
|
||||
[parameter(Mandatory=$true)]
|
||||
[parameter(ValueFromPipeline = $true)]
|
||||
[xml] $xml,
|
||||
[parameter(Mandatory = $true)]
|
||||
[string] $nodeName)
|
||||
|
||||
# Try the old style csproj. Also format required for .targets and .props files
|
||||
$n = Select-Xml -Xml $xml.Project -Namespace @{d = $ns } -XPath "//d:$nodeName"
|
||||
|
||||
|
||||
# Try the SDK-style files
|
||||
if (!$n) {
|
||||
$r = Select-Xml -Xml $xml.Project -XPath "//$nodeName"
|
||||
|
@ -24,11 +23,10 @@ function Get-Nodes
|
|||
return $r
|
||||
}
|
||||
|
||||
function Get-NodeValue
|
||||
{
|
||||
function Get-NodeValue {
|
||||
param(
|
||||
[parameter(ValueFromPipeline=$true)]
|
||||
[xml] $xml,
|
||||
[parameter(ValueFromPipeline = $true)]
|
||||
[xml] $xml,
|
||||
[string] $nodeName)
|
||||
|
||||
$node = get-nodes $xml $nodeName
|
||||
|
@ -42,10 +40,9 @@ function Get-NodeValue
|
|||
return [string]""
|
||||
}
|
||||
|
||||
function Get-SdkVersion
|
||||
{
|
||||
function Get-SdkVersion {
|
||||
param(
|
||||
[Parameter(ValueFromPipeline=$true)] $file)
|
||||
[Parameter(ValueFromPipeline = $true)] $file)
|
||||
|
||||
[xml] $xml = Get-Content $file
|
||||
|
||||
|
@ -67,26 +64,32 @@ function Get-SdkVersion
|
|||
$versions.Add("10.0." + $version + ".0") | Out-Null
|
||||
}
|
||||
|
||||
function Test-RegistryPathAndValue
|
||||
{
|
||||
function Test-RegistryPathAndValue {
|
||||
param (
|
||||
[parameter(Mandatory=$true)]
|
||||
[parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $path,
|
||||
[parameter(Mandatory=$true)]
|
||||
[parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $value)
|
||||
|
||||
try
|
||||
{
|
||||
if (Test-Path $path)
|
||||
{
|
||||
try {
|
||||
if (Test-Path $path) {
|
||||
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
|
||||
return $true
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
catch {
|
||||
}
|
||||
|
||||
try {
|
||||
$path = $path -replace "HKLM:\\SOFTWARE\\", "HKLM:\\SOFTWARE\\WOW6432Node\\"
|
||||
if (Test-Path $path) {
|
||||
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
|
||||
return $true
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return $false
|
||||
|
@ -101,24 +104,23 @@ function Test-InstallWindowsSdk([string] $WindowsSDKVersion) {
|
|||
|
||||
$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options"
|
||||
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
|
||||
{
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) {
|
||||
# A Windows SDK is installed
|
||||
# Is an SDK of our version installed with the options we need?
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions")
|
||||
{
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") {
|
||||
# It appears we have what we need. Double check the disk
|
||||
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
|
||||
if ($sdkRoot)
|
||||
{
|
||||
if (Test-Path $sdkRoot)
|
||||
{
|
||||
if (!$sdkRoot) {
|
||||
$WindowsSDKRegPath = $WindowsSDKRegPath -replace "HKLM:\\SOFTWARE\\", "HKLM:\\SOFTWARE\\WOW6432Node\\"
|
||||
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
|
||||
}
|
||||
|
||||
if ($sdkRoot) {
|
||||
if (Test-Path $sdkRoot) {
|
||||
$refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
|
||||
if (Test-Path $refPath)
|
||||
{
|
||||
if (Test-Path $refPath) {
|
||||
$umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
|
||||
if (Test-Path $umdPath)
|
||||
{
|
||||
if (Test-Path $umdPath) {
|
||||
# Pretty sure we have what we need
|
||||
$retval = $false
|
||||
}
|
||||
|
@ -131,13 +133,13 @@ function Test-InstallWindowsSdk([string] $WindowsSDKVersion) {
|
|||
return $retval
|
||||
}
|
||||
|
||||
if(!$PSScriptRoot){
|
||||
if (!$PSScriptRoot) {
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
}
|
||||
|
||||
Write-Host -NoNewline "Locating referenced Windows SDK versions..."
|
||||
|
||||
Get-ChildItem *.csproj -Recurse | ForEach-Object { get-sdkversion $_}
|
||||
Get-ChildItem *.csproj -Recurse | ForEach-Object { get-sdkversion $_ }
|
||||
Get-ChildItem *.targets -Recurse | ForEach-Object { get-sdkversion $_ }
|
||||
Get-ChildItem *.props -Recurse | ForEach-Object { get-sdkversion $_ }
|
||||
|
||||
|
@ -146,11 +148,11 @@ Write-Host
|
|||
|
||||
$anyInstallRequired = $false;
|
||||
|
||||
foreach($version in $versions) {
|
||||
foreach ($version in $versions) {
|
||||
if ($version -match "10\.0\.\d{5}\.0") {
|
||||
$installRequired = Test-InstallWindowsSDK $version
|
||||
Write-Host "Windows SDK '$version' install required: $installRequired"
|
||||
if ($installRequired) {
|
||||
if ($installRequired) {
|
||||
# Automatically invoke Install-WindowsSDKIso.ps1 ?
|
||||
$anyInstallRequired = $true
|
||||
}
|
||||
|
@ -158,8 +160,9 @@ foreach($version in $versions) {
|
|||
}
|
||||
|
||||
Write-Host
|
||||
if ($anyInstallRequired) {
|
||||
if ($anyInstallRequired) {
|
||||
throw "At least one Windows SDK is missing from this machine"
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Write-Host "All referenced Windows SDKs are installed!"
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
mkdir c:\winsdktemp
|
||||
|
||||
$client = new-object System.Net.WebClient
|
||||
$client.DownloadFile("https://go.microsoft.com/fwlink/p/?linkid=870807","c:\winsdktemp\winsdksetup.exe")
|
||||
$WinSdkTempDir = "C:\WinSdkTemp\"
|
||||
$WinSdkSetupExe = "C:\WinSdkTemp\" + "WinSdkSetup.exe"
|
||||
|
||||
Start-Process -Wait "c:\winsdktemp\winsdksetup.exe" "/features OptionId.UWPCpp /q"
|
||||
mkdir $WinSdkTempDir
|
||||
|
||||
$client = [System.Net.WebClient]::new()
|
||||
$client.DownloadFile("https://go.microsoft.com/fwlink/p/?linkid=870807", $WinSdkSetupExe)
|
||||
|
||||
Start-Process -Wait $WinSdkSetupExe "/features OptionId.UWPCpp /q"
|
|
@ -1,6 +1,6 @@
|
|||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)]
|
||||
[string]$buildNumber)
|
||||
param([Parameter(Mandatory = $true)]
|
||||
[string]$buildNumber)
|
||||
|
||||
# Ensure the error action preference is set to the default for PowerShell3, 'Stop'
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
@ -14,11 +14,10 @@ $WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed O
|
|||
$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification"
|
||||
$PublicKeyTokens = @("31bf3856ad364e35")
|
||||
|
||||
function Download-File
|
||||
{
|
||||
function Download-File {
|
||||
param ([string] $outDir,
|
||||
[string] $downloadUrl,
|
||||
[string] $downloadName)
|
||||
[string] $downloadUrl,
|
||||
[string] $downloadName)
|
||||
|
||||
$downloadPath = Join-Path $outDir "$downloadName.download"
|
||||
$downloadDest = Join-Path $outDir $downloadName
|
||||
|
@ -26,21 +25,17 @@ function Download-File
|
|||
|
||||
Write-Host -NoNewline "Downloading $downloadName..."
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
$webclient = new-object System.Net.WebClient
|
||||
$webclient.DownloadFile($downloadUrl, $downloadPath)
|
||||
}
|
||||
catch [System.Net.WebException]
|
||||
{
|
||||
catch [System.Net.WebException] {
|
||||
Write-Host
|
||||
Write-Warning "Failed to fetch updated file from $downloadUrl"
|
||||
if (!(Test-Path $downloadDest))
|
||||
{
|
||||
if (!(Test-Path $downloadDest)) {
|
||||
throw "$downloadName was not found at $downloadDest"
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
Write-Warning "$downloadName may be out of date"
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +45,7 @@ function Download-File
|
|||
$downloadDestTemp = $downloadPath;
|
||||
|
||||
# Delete and rename to final dest
|
||||
if (Test-Path -PathType Container $downloadDest)
|
||||
{
|
||||
if (Test-Path -PathType Container $downloadDest) {
|
||||
[System.IO.Directory]::Delete($downloadDest, $true)
|
||||
}
|
||||
|
||||
|
@ -61,20 +55,16 @@ function Download-File
|
|||
return $downloadDest
|
||||
}
|
||||
|
||||
function Get-ISODriveLetter
|
||||
{
|
||||
function Get-ISODriveLetter {
|
||||
param ([string] $isoPath)
|
||||
|
||||
$diskImage = Get-DiskImage -ImagePath $isoPath
|
||||
if ($diskImage)
|
||||
{
|
||||
if ($diskImage) {
|
||||
$volume = Get-Volume -DiskImage $diskImage
|
||||
|
||||
if ($volume)
|
||||
{
|
||||
if ($volume) {
|
||||
$driveLetter = $volume.DriveLetter
|
||||
if ($driveLetter)
|
||||
{
|
||||
if ($driveLetter) {
|
||||
$driveLetter += ":"
|
||||
return $driveLetter
|
||||
}
|
||||
|
@ -84,15 +74,13 @@ function Get-ISODriveLetter
|
|||
return $null
|
||||
}
|
||||
|
||||
function Mount-ISO
|
||||
{
|
||||
function Mount-ISO {
|
||||
param ([string] $isoPath)
|
||||
|
||||
# Check if image is already mounted
|
||||
$isoDrive = Get-ISODriveLetter $isoPath
|
||||
|
||||
if (!$isoDrive)
|
||||
{
|
||||
if (!$isoDrive) {
|
||||
Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null
|
||||
}
|
||||
|
||||
|
@ -100,84 +88,68 @@ function Mount-ISO
|
|||
Write-Verbose "$isoPath mounted to ${isoDrive}:"
|
||||
}
|
||||
|
||||
function Dismount-ISO
|
||||
{
|
||||
function Dismount-ISO {
|
||||
param ([string] $isoPath)
|
||||
|
||||
$isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter
|
||||
|
||||
if ($isoDrive)
|
||||
{
|
||||
if ($isoDrive) {
|
||||
Write-Verbose "$isoPath dismounted"
|
||||
Dismount-DiskImage -ImagePath $isoPath | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Disable-StrongName
|
||||
{
|
||||
function Disable-StrongName {
|
||||
param ([string] $publicKeyToken = "*")
|
||||
|
||||
reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
|
||||
{
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
||||
reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Test-Admin
|
||||
{
|
||||
function Test-Admin {
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal $identity
|
||||
$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Test-RegistryPathAndValue
|
||||
{
|
||||
function Test-RegistryPathAndValue {
|
||||
param (
|
||||
[parameter(Mandatory=$true)]
|
||||
[parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $path,
|
||||
[parameter(Mandatory=$true)]
|
||||
[parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $value)
|
||||
|
||||
try
|
||||
{
|
||||
if (Test-Path $path)
|
||||
{
|
||||
try {
|
||||
if (Test-Path $path) {
|
||||
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
|
||||
return $true
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
catch {
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
function Test-InstallWindowsSDK
|
||||
{
|
||||
function Test-InstallWindowsSDK {
|
||||
$retval = $true
|
||||
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
|
||||
{
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) {
|
||||
# A Windows SDK is installed
|
||||
# Is an SDK of our version installed with the options we need?
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions")
|
||||
{
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") {
|
||||
# It appears we have what we need. Double check the disk
|
||||
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
|
||||
if ($sdkRoot)
|
||||
{
|
||||
if (Test-Path $sdkRoot)
|
||||
{
|
||||
if ($sdkRoot) {
|
||||
if (Test-Path $sdkRoot) {
|
||||
$refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
|
||||
if (Test-Path $refPath)
|
||||
{
|
||||
if (Test-Path $refPath) {
|
||||
$umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
|
||||
if (Test-Path $umdPath)
|
||||
{
|
||||
if (Test-Path $umdPath) {
|
||||
# Pretty sure we have what we need
|
||||
$retval = $false
|
||||
}
|
||||
|
@ -190,13 +162,10 @@ function Test-InstallWindowsSDK
|
|||
return $retval
|
||||
}
|
||||
|
||||
function Test-InstallStrongNameHijack
|
||||
{
|
||||
foreach($publicKeyToken in $PublicKeyTokens)
|
||||
{
|
||||
function Test-InstallStrongNameHijack {
|
||||
foreach ($publicKeyToken in $PublicKeyTokens) {
|
||||
$key = "$StrongNameRegPath\*,$publicKeyToken"
|
||||
if (!(Test-Path $key))
|
||||
{
|
||||
if (!(Test-Path $key)) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
@ -206,51 +175,42 @@ function Test-InstallStrongNameHijack
|
|||
|
||||
Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..."
|
||||
$InstallWindowsSDK = Test-InstallWindowsSDK
|
||||
if ($InstallWindowsSDK)
|
||||
{
|
||||
if ($InstallWindowsSDK) {
|
||||
Write-Host "Installation required"
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
Write-Host "INSTALLED"
|
||||
}
|
||||
|
||||
$StrongNameHijack = Test-InstallStrongNameHijack
|
||||
Write-Host -NoNewline "Checking if StrongName bypass required..."
|
||||
|
||||
if ($StrongNameHijack)
|
||||
{
|
||||
if ($StrongNameHijack) {
|
||||
Write-Host "REQUIRED"
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
Write-Host "Done"
|
||||
}
|
||||
|
||||
if ($StrongNameHijack -or $InstallWindowsSDK)
|
||||
{
|
||||
if (!(Test-Admin))
|
||||
{
|
||||
if ($StrongNameHijack -or $InstallWindowsSDK) {
|
||||
if (!(Test-Admin)) {
|
||||
Write-Host
|
||||
throw "ERROR: Elevation required"
|
||||
}
|
||||
}
|
||||
|
||||
if ($InstallWindowsSDK)
|
||||
{
|
||||
if ($InstallWindowsSDK) {
|
||||
# Static(ish) link for Windows SDK
|
||||
# Note: there is a delay from Windows SDK announcements to availability via the static link
|
||||
$uri = "https://software-download.microsoft.com/download/sg/Windows_InsiderPreview_SDK_en-us_$($buildNumber)_1.iso";
|
||||
|
||||
if ($env:TEMP -eq $null)
|
||||
{
|
||||
if ($null -eq $env:TEMP) {
|
||||
$env:TEMP = Join-Path $env:SystemDrive 'temp'
|
||||
}
|
||||
|
||||
$winsdkTempDir = Join-Path $env:TEMP "WindowsSDK"
|
||||
|
||||
if (![System.IO.Directory]::Exists($winsdkTempDir))
|
||||
{
|
||||
if (![System.IO.Directory]::Exists($winsdkTempDir)) {
|
||||
[void][System.IO.Directory]::CreateDirectory($winsdkTempDir)
|
||||
}
|
||||
|
||||
|
@ -260,41 +220,35 @@ if ($InstallWindowsSDK)
|
|||
$downloadFile = Download-File $winsdkTempDir $uri $file
|
||||
|
||||
# TODO Check if zip, exe, iso, etc.
|
||||
try
|
||||
{
|
||||
try {
|
||||
Write-Host -NoNewline "Mounting ISO $file..."
|
||||
Mount-ISO $downloadFile
|
||||
Write-Host "Done"
|
||||
|
||||
$isoDrive = Get-ISODriveLetter $downloadFile
|
||||
|
||||
if (Test-Path $isoDrive)
|
||||
{
|
||||
if (Test-Path $isoDrive) {
|
||||
Write-Host -NoNewLine "Installing WinSDK..."
|
||||
|
||||
$setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe"
|
||||
Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q"
|
||||
Write-Host "Done"
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
throw "Could not find mounted ISO at ${isoDrive}"
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
finally {
|
||||
Write-Host -NoNewline "Dismounting ISO $file..."
|
||||
#Dismount-ISO $downloadFile
|
||||
# Dismount-ISO $downloadFile
|
||||
Write-Host "Done"
|
||||
}
|
||||
}
|
||||
|
||||
if ($StrongNameHijack)
|
||||
{
|
||||
if ($StrongNameHijack) {
|
||||
Write-Host -NoNewline "Disabling StrongName for Windows SDK..."
|
||||
|
||||
foreach($key in $PublicKeyTokens)
|
||||
{
|
||||
foreach ($key in $PublicKeyTokens) {
|
||||
Disable-StrongName $key
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
$currentDirectory = split-path $MyInvocation.MyCommand.Definition
|
||||
|
||||
# See if we have the ClientSecret available
|
||||
if([string]::IsNullOrEmpty($Env:SignClientSecret)){
|
||||
Write-Host "Client Secret not found, not signing packages"
|
||||
return;
|
||||
if ([string]::IsNullOrEmpty($Env:SignClientSecret)) {
|
||||
Write-Host "Client Secret not found, not signing packages"
|
||||
return;
|
||||
}
|
||||
|
||||
dotnet tool install --tool-path . SignClient
|
||||
|
@ -13,16 +13,16 @@ dotnet tool install --tool-path . SignClient
|
|||
|
||||
$appSettings = "$currentDirectory\SignClientSettings.json"
|
||||
|
||||
$nupkgs = gci $Env:ArtifactDirectory\*.nupkg -recurse | Select -ExpandProperty FullName
|
||||
$nupkgs = Get-ChildItem $Env:ArtifactDirectory\*.nupkg -recurse | Select-Object -ExpandProperty FullName
|
||||
|
||||
foreach ($nupkg in $nupkgs){
|
||||
Write-Host "Submitting $nupkg for signing"
|
||||
foreach ($nupkg in $nupkgs) {
|
||||
Write-Host "Submitting $nupkg for signing"
|
||||
|
||||
.\SignClient 'sign' -c $appSettings -i $nupkg -r $Env:SignClientUser -s $Env:SignClientSecret -n 'Windows Community Toolkit' -d 'Windows Community Toolkit' -u 'https://developer.microsoft.com/en-us/windows/uwp-community-toolkit'
|
||||
.\SignClient 'sign' -c $appSettings -i $nupkg -r $Env:SignClientUser -s $Env:SignClientSecret -n 'Windows Community Toolkit' -d 'Windows Community Toolkit' -u 'https://developer.microsoft.com/en-us/windows/uwp-community-toolkit'
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
exit 1
|
||||
}
|
||||
Write-Host "Finished signing $nupkg"
|
||||
Write-Host "Finished signing $nupkg"
|
||||
}
|
||||
|
||||
Write-Host "Sign-package complete"
|
|
@ -1,3 +1,3 @@
|
|||
@ECHO OFF
|
||||
PowerShell.exe -file "%~dp0build.ps1" -target=StyleXaml
|
||||
PowerShell.exe -file "%~dp0build.ps1" -Target StyleXaml
|
||||
PAUSE
|
|
@ -1,3 +1,3 @@
|
|||
@ECHO OFF
|
||||
PowerShell.exe -file "%~dp0build.ps1" -target=UpdateHeaders
|
||||
PowerShell.exe -file "%~dp0build.ps1" -Target UpdateHeaders
|
||||
PAUSE
|
|
@ -1,10 +1,10 @@
|
|||
#module nuget:?package=Cake.LongPath.Module&version=0.7.0
|
||||
#module nuget:?package=Cake.LongPath.Module&version=1.0.1
|
||||
|
||||
#addin nuget:?package=Cake.FileHelpers&version=3.2.1
|
||||
#addin nuget:?package=Cake.Powershell&version=0.4.8
|
||||
#addin nuget:?package=Cake.GitVersioning&version=3.3.37
|
||||
#addin nuget:?package=Cake.FileHelpers&version=4.0.1
|
||||
#addin nuget:?package=Cake.Powershell&version=1.0.1
|
||||
#addin nuget:?package=Cake.GitVersioning&version=3.4.220
|
||||
|
||||
#tool nuget:?package=MSTest.TestAdapter&version=2.1.0
|
||||
#tool nuget:?package=MSTest.TestAdapter&version=2.2.5
|
||||
#tool nuget:?package=vswhere&version=2.8.4
|
||||
|
||||
using System;
|
||||
|
@ -16,6 +16,7 @@ using System.Text.RegularExpressions;
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// VERSIONS
|
||||
|
@ -35,7 +36,7 @@ var toolsDir = buildDir + "/tools";
|
|||
var binDir = baseDir + "/bin";
|
||||
var nupkgDir = binDir + "/nupkg";
|
||||
|
||||
var taefBinDir = baseDir + "/UITests/UITests.Tests.TAEF/bin/Release/netcoreapp3.1/win10-x86";
|
||||
var taefBinDir = baseDir + $"/UITests/UITests.Tests.TAEF/bin/{configuration}/netcoreapp3.1/win10-x86";
|
||||
|
||||
var styler = toolsDir + "/XamlStyler.Console/tools/xstyler.exe";
|
||||
var stylerFile = baseDir + "/settings.xamlstyler";
|
||||
|
@ -94,7 +95,7 @@ void VerifyHeaders(bool Replace)
|
|||
|
||||
if(!Replace && hasMissing)
|
||||
{
|
||||
throw new Exception("Please run UpdateHeaders.bat or '.\\build.ps1 -target=UpdateHeaders' and commit the changes.");
|
||||
throw new Exception("Please run UpdateHeaders.bat or '.\\build.ps1 -Target UpdateHeaders' and commit the changes.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +152,7 @@ Task("BuildProjects")
|
|||
{
|
||||
MaxCpuCount = 0
|
||||
}
|
||||
.SetConfiguration("Release")
|
||||
.SetConfiguration(configuration)
|
||||
.WithTarget("Restore");
|
||||
|
||||
MSBuild(Solution, buildSettings);
|
||||
|
@ -163,7 +164,7 @@ Task("BuildProjects")
|
|||
{
|
||||
MaxCpuCount = 0
|
||||
}
|
||||
.SetConfiguration("Release")
|
||||
.SetConfiguration(configuration)
|
||||
.WithTarget("Build")
|
||||
.WithProperty("GenerateLibraryLayout", "true");
|
||||
|
||||
|
@ -176,13 +177,14 @@ Task("InheritDoc")
|
|||
.Does(() =>
|
||||
{
|
||||
Information("\nDownloading InheritDoc...");
|
||||
var installSettings = new NuGetInstallSettings {
|
||||
var installSettings = new NuGetInstallSettings
|
||||
{
|
||||
ExcludeVersion = true,
|
||||
Version = inheritDocVersion,
|
||||
OutputDirectory = toolsDir
|
||||
};
|
||||
|
||||
NuGetInstall(new []{"InheritDoc"}, installSettings);
|
||||
NuGetInstall(new[] {"InheritDoc"}, installSettings);
|
||||
|
||||
var args = new ProcessArgumentBuilder()
|
||||
.AppendSwitchQuoted("-b", baseDir)
|
||||
|
@ -210,10 +212,11 @@ Task("Package")
|
|||
.Does(() =>
|
||||
{
|
||||
// Invoke the pack target in the end
|
||||
var buildSettings = new MSBuildSettings {
|
||||
var buildSettings = new MSBuildSettings
|
||||
{
|
||||
MaxCpuCount = 0
|
||||
}
|
||||
.SetConfiguration("Release")
|
||||
.SetConfiguration(configuration)
|
||||
.WithTarget("Pack")
|
||||
.WithProperty("GenerateLibraryLayout", "true")
|
||||
.WithProperty("PackageOutputPath", nupkgDir);
|
||||
|
@ -251,15 +254,15 @@ Task("Test")
|
|||
ArgumentCustomization = arg => arg.Append("/logger:trx;LogFileName=VsTestResultsUwp.trx /framework:FrameworkUap10"),
|
||||
};
|
||||
|
||||
VSTest(baseDir + "/**/Release/**/UnitTests.*.appxrecipe", testSettings);
|
||||
VSTest(baseDir + $"/**/{configuration}/**/UnitTests.*.appxrecipe", testSettings);
|
||||
}).DoesForEach(GetFiles(baseDir + "/**/UnitTests.*NetCore.csproj"), (file) =>
|
||||
{
|
||||
Information("\nRunning NetCore Unit Tests");
|
||||
var testSettings = new DotNetCoreTestSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
Configuration = configuration,
|
||||
NoBuild = true,
|
||||
Logger = "trx;LogFilePrefix=VsTestResults",
|
||||
Loggers = new[] { "trx;LogFilePrefix=VsTestResults" },
|
||||
Verbosity = DotNetCoreVerbosity.Normal,
|
||||
ArgumentCustomization = arg => arg.Append($"-s {baseDir}/.runsettings"),
|
||||
};
|
||||
|
@ -304,15 +307,14 @@ Task("MSTestUITest")
|
|||
|
||||
var testSettings = new DotNetCoreTestSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
Configuration = configuration,
|
||||
NoBuild = true,
|
||||
Logger = "trx;LogFilePrefix=VsTestResults",
|
||||
Loggers = new[] { "trx;LogFilePrefix=VsTestResults" },
|
||||
Verbosity = DotNetCoreVerbosity.Normal
|
||||
};
|
||||
DotNetCoreTest(file.FullPath, testSettings);
|
||||
});
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// TASK TARGETS
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -335,12 +337,13 @@ Task("StyleXaml")
|
|||
.Does(() =>
|
||||
{
|
||||
Information("\nDownloading XamlStyler...");
|
||||
var installSettings = new NuGetInstallSettings {
|
||||
var installSettings = new NuGetInstallSettings
|
||||
{
|
||||
ExcludeVersion = true,
|
||||
OutputDirectory = toolsDir
|
||||
};
|
||||
|
||||
NuGetInstall(new []{"xamlstyler.console"}, installSettings);
|
||||
NuGetInstall(new[] {"xamlstyler.console"}, installSettings);
|
||||
|
||||
Func<IFileSystemInfo, bool> exclude_objDir =
|
||||
fileSystemInfo => !fileSystemInfo.Path.Segments.Contains("obj");
|
||||
|
@ -353,8 +356,6 @@ Task("StyleXaml")
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// EXECUTION
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
233
build/build.ps1
233
build/build.ps1
|
@ -13,19 +13,18 @@ This is a Powershell script to bootstrap a Cake build.
|
|||
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
|
||||
and execute your Cake build script with the parameters you provide.
|
||||
|
||||
.PARAMETER Script
|
||||
The build script to execute.
|
||||
.PARAMETER Target
|
||||
The build script target to run.
|
||||
.PARAMETER Configuration
|
||||
The build configuration to use.
|
||||
.PARAMETER Verbosity
|
||||
Specifies the amount of information to be displayed.
|
||||
.PARAMETER Experimental
|
||||
Tells Cake to use the latest Roslyn release.
|
||||
.PARAMETER WhatIf
|
||||
Performs a dry run of the build script.
|
||||
No tasks will be executed.
|
||||
.PARAMETER Mono
|
||||
Tells Cake to use the Mono scripting engine.
|
||||
.PARAMETER ShowDescription
|
||||
Shows description about tasks.
|
||||
.PARAMETER DryRun
|
||||
Performs a dry run.
|
||||
.PARAMETER SkipToolPackageRestore
|
||||
Skips restoring of packages.
|
||||
.PARAMETER ScriptArgs
|
||||
|
@ -38,18 +37,41 @@ https://cakebuild.net
|
|||
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[string]$Target = "Default",
|
||||
[string]$Script,
|
||||
[string]$Target,
|
||||
[string]$Configuration,
|
||||
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
||||
[string]$Verbosity = "Verbose",
|
||||
[switch]$Experimental,
|
||||
[Alias("DryRun","Noop")]
|
||||
[switch]$WhatIf,
|
||||
[switch]$Mono,
|
||||
[string]$Verbosity,
|
||||
[switch]$ShowDescription,
|
||||
[Alias("WhatIf", "Noop")]
|
||||
[switch]$DryRun,
|
||||
[switch]$SkipToolPackageRestore,
|
||||
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ScriptArgs
|
||||
)
|
||||
|
||||
# This is an automatic variable in PowerShell Core, but not in Windows PowerShell 5.x
|
||||
if (-not (Test-Path variable:global:IsCoreCLR)) {
|
||||
$IsCoreCLR = $false
|
||||
}
|
||||
|
||||
# Attempt to set highest encryption available for SecurityProtocol.
|
||||
# PowerShell will not set this by default (until maybe .NET 4.6.x). This
|
||||
# will typically produce a message for PowerShell v2 (just an info
|
||||
# message though)
|
||||
try {
|
||||
# Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48)
|
||||
# Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't
|
||||
# exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
|
||||
# installed (.NET 4.5 is an in-place upgrade).
|
||||
# PowerShell Core already has support for TLS 1.2 so we can skip this if running in that.
|
||||
if (-not $IsCoreCLR) {
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
|
||||
}
|
||||
} catch {
|
||||
Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3'
|
||||
}
|
||||
|
||||
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
|
||||
function MD5HashFile([string] $filePath)
|
||||
{
|
||||
|
@ -68,62 +90,64 @@ function MD5HashFile([string] $filePath)
|
|||
}
|
||||
finally
|
||||
{
|
||||
if ($file -ne $null)
|
||||
if ($null -ne $file)
|
||||
{
|
||||
$file.Dispose()
|
||||
}
|
||||
|
||||
if ($null -ne $md5)
|
||||
{
|
||||
$md5.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetProxyEnabledWebClient
|
||||
{
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
|
||||
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
|
||||
$wc.Proxy = $proxy
|
||||
return $wc
|
||||
}
|
||||
|
||||
Write-Host "Preparing to run build script..."
|
||||
|
||||
if(!$PSScriptRoot){
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
}
|
||||
|
||||
if(!$Script){
|
||||
$Script = Join-Path $PSScriptRoot "build.cake"
|
||||
}
|
||||
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
|
||||
$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
|
||||
$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
|
||||
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
|
||||
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
|
||||
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
|
||||
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
|
||||
$MODULES_DIR = Join-Path $PSScriptRoot "tools/modules"
|
||||
$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
|
||||
$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
|
||||
$MODULES_PACKAGES_CONFIG_MD5 = Join-Path $MODULES_DIR "packages.config.md5sum"
|
||||
|
||||
# Should we use mono?
|
||||
$UseMono = "";
|
||||
if($Mono.IsPresent) {
|
||||
Write-Verbose -Message "Using the Mono based scripting engine."
|
||||
$UseMono = "-mono"
|
||||
}
|
||||
|
||||
# Should we use the new Roslyn?
|
||||
$UseExperimental = "";
|
||||
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
|
||||
Write-Verbose -Message "Using experimental version of Roslyn."
|
||||
$UseExperimental = "-experimental"
|
||||
}
|
||||
|
||||
# Is this a dry run?
|
||||
$UseDryRun = "";
|
||||
if($WhatIf.IsPresent) {
|
||||
$UseDryRun = "-dryrun"
|
||||
}
|
||||
$env:CAKE_PATHS_TOOLS = $TOOLS_DIR
|
||||
$env:CAKE_PATHS_ADDINS = $ADDINS_DIR
|
||||
$env:CAKE_PATHS_MODULES = $MODULES_DIR
|
||||
|
||||
# Make sure tools folder exists
|
||||
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
|
||||
Write-Verbose -Message "Creating tools directory..."
|
||||
New-Item -Path $TOOLS_DIR -Type directory | out-null
|
||||
New-Item -Path $TOOLS_DIR -Type Directory | Out-Null
|
||||
}
|
||||
|
||||
# Fix to force PS to use TLS12
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# Make sure that packages.config exist.
|
||||
if (!(Test-Path $PACKAGES_CONFIG)) {
|
||||
Write-Verbose -Message "Downloading packages.config..."
|
||||
try { (New-Object System.Net.WebClient).DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
|
||||
try {
|
||||
$wc = GetProxyEnabledWebClient
|
||||
$wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
|
||||
} catch {
|
||||
Throw "Could not download packages.config."
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +157,7 @@ if (!(Test-Path $NUGET_EXE)) {
|
|||
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
|
||||
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
|
||||
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
|
||||
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
|
||||
if ($null -ne $NUGET_EXE_IN_PATH -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
|
||||
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
|
||||
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
|
||||
}
|
||||
|
@ -143,14 +167,26 @@ if (!(Test-Path $NUGET_EXE)) {
|
|||
if (!(Test-Path $NUGET_EXE)) {
|
||||
Write-Verbose -Message "Downloading NuGet.exe..."
|
||||
try {
|
||||
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
|
||||
$wc = GetProxyEnabledWebClient
|
||||
$wc.DownloadFile($NUGET_URL, $NUGET_EXE)
|
||||
} catch {
|
||||
Throw "Could not download NuGet.exe."
|
||||
}
|
||||
}
|
||||
|
||||
# These are automatic variables in PowerShell Core, but not in Windows PowerShell 5.x
|
||||
if (-not (Test-Path variable:global:IsMacOS)) {
|
||||
$IsLinux = $false
|
||||
$IsMacOS = $false
|
||||
}
|
||||
|
||||
# Save nuget.exe path to environment to be available to child processed
|
||||
$ENV:NUGET_EXE = $NUGET_EXE
|
||||
$env:NUGET_EXE = $NUGET_EXE
|
||||
$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) {
|
||||
"mono `"$NUGET_EXE`""
|
||||
} else {
|
||||
"`"$NUGET_EXE`""
|
||||
}
|
||||
|
||||
# Restore tools from NuGet?
|
||||
if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||
|
@ -158,15 +194,17 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
|
|||
Set-Location $TOOLS_DIR
|
||||
|
||||
# Check for changes in packages.config and remove installed tools if true.
|
||||
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
|
||||
[string] $md5Hash = MD5HashFile $PACKAGES_CONFIG
|
||||
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
|
||||
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||
Write-Verbose -Message "Missing or changed package.config hash..."
|
||||
Remove-Item * -Recurse -Exclude packages.config,nuget.exe
|
||||
Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
|
||||
Remove-Item -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
|
||||
$NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occurred while restoring NuGet tools."
|
||||
|
@ -175,7 +213,42 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
|
|||
{
|
||||
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
|
||||
}
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
Write-Verbose -Message ($NuGetOutput | Out-String)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Restore addins from NuGet
|
||||
if (Test-Path $ADDINS_PACKAGES_CONFIG) {
|
||||
Push-Location
|
||||
Set-Location $ADDINS_DIR
|
||||
|
||||
Write-Verbose -Message "Restoring addins from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occurred while restoring NuGet addins."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | Out-String)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Restore modules from NuGet
|
||||
if (Test-Path $MODULES_PACKAGES_CONFIG) {
|
||||
Push-Location
|
||||
Set-Location $MODULES_DIR
|
||||
|
||||
Write-Verbose -Message "Restoring modules from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occurred while restoring NuGet modules."
|
||||
}
|
||||
|
||||
Write-Verbose -Message ($NuGetOutput | Out-String)
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
|
@ -184,49 +257,31 @@ if (!(Test-Path $CAKE_EXE)) {
|
|||
Throw "Could not find Cake.exe at $CAKE_EXE"
|
||||
}
|
||||
|
||||
# Make sure modules folder exists
|
||||
if ((Test-Path $PSScriptRoot) -and !(Test-Path $MODULES_DIR)) {
|
||||
Write-Verbose -Message "Creating tools/modules directory..."
|
||||
New-Item -Path $MODULES_DIR -Type directory | out-null
|
||||
$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) {
|
||||
"mono `"$CAKE_EXE`""
|
||||
} else {
|
||||
"`"$CAKE_EXE`""
|
||||
}
|
||||
|
||||
# Restore modules from NuGet?
|
||||
if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||
Push-Location
|
||||
Set-Location $MODULES_DIR
|
||||
|
||||
# Check for changes in modules packages.config and remove installed tools if true.
|
||||
[string] $md5Hash = MD5HashFile($MODULES_PACKAGES_CONFIG)
|
||||
if((!(Test-Path $MODULES_PACKAGES_CONFIG_MD5)) -Or
|
||||
($md5Hash -ne (Get-Content $MODULES_PACKAGES_CONFIG_MD5 ))) {
|
||||
Write-Verbose -Message "Missing or changed modules package.config hash..."
|
||||
Remove-Item * -Recurse -Exclude packages.config,packages.config.md5sum,nuget.exe
|
||||
}
|
||||
|
||||
Write-Verbose -Message "Restoring modules from NuGet..."
|
||||
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Throw "An error occurred while restoring NuGet modules."
|
||||
}
|
||||
else
|
||||
{
|
||||
$md5Hash | Out-File $MODULES_PACKAGES_CONFIG_MD5 -Encoding "ASCII"
|
||||
}
|
||||
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||
Pop-Location
|
||||
}
|
||||
# Build an array (not a string) of Cake arguments to be joined later
|
||||
$cakeArguments = @()
|
||||
if ($Script) { $cakeArguments += "`"$Script`"" }
|
||||
if ($Target) { $cakeArguments += "--target=`"$Target`"" }
|
||||
if ($Configuration) { $cakeArguments += "--configuration=$Configuration" }
|
||||
if ($Verbosity) { $cakeArguments += "--verbosity=$Verbosity" }
|
||||
if ($ShowDescription) { $cakeArguments += "--showdescription" }
|
||||
if ($DryRun) { $cakeArguments += "--dryrun" }
|
||||
$cakeArguments += $ScriptArgs
|
||||
|
||||
# Start Cake
|
||||
$path = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$Script = "$path/build.cake"
|
||||
|
||||
Write-Host "Bootstrapping Cake..."
|
||||
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" --bootstrap"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "An error occurred while bootstrapping Cake."
|
||||
}
|
||||
|
||||
Write-Host "Running build script..."
|
||||
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
|
||||
exit $LASTEXITCODE
|
||||
Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")"
|
||||
$cakeExitCode = $LASTEXITCODE
|
||||
|
||||
# Clean up environment variables that were created earlier in this bootstrapper
|
||||
$env:CAKE_PATHS_TOOLS = $null
|
||||
$env:CAKE_PATHS_ADDINS = $null
|
||||
$env:CAKE_PATHS_MODULES = $null
|
||||
|
||||
# Return exit code
|
||||
exit $cakeExitCode
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<packages>
|
||||
<package id="Cake" version="0.37.0" />
|
||||
<package id="Cake" version="1.1.0" />
|
||||
</packages>
|
|
@ -8,4 +8,4 @@
|
|||
<disabledPackageSources>
|
||||
<clear />
|
||||
</disabledPackageSources>
|
||||
</configuration>
|
||||
</configuration>
|
|
@ -7,7 +7,7 @@
|
|||
"SeparateByGroups": false,
|
||||
"AttributeIndentation": 0,
|
||||
"AttributeIndentationStyle": 1,
|
||||
"RemoveDesignTimeReferences": false,
|
||||
"RemoveDesignTimeReferences": false,
|
||||
"EnableAttributeReordering": true,
|
||||
"AttributeOrderingRuleGroups": [
|
||||
"x:Class",
|
||||
|
@ -39,4 +39,4 @@
|
|||
"FormatOnSave": true,
|
||||
"CommentPadding": 2,
|
||||
"IndentSize": 4
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"orderingRules": {
|
||||
"usingDirectivesPlacement": "outsideNamespace"
|
||||
}
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"orderingRules": {
|
||||
"usingDirectivesPlacement": "outsideNamespace"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"^refs/heads/dev$", // we release out of dev
|
||||
"^refs/heads/rel/\\d+\\.\\d+\\.\\d+" // we also release branches starting with rel/N.N.N
|
||||
],
|
||||
"nugetPackageVersion":{
|
||||
"nugetPackageVersion": {
|
||||
"semVer": 2
|
||||
},
|
||||
"cloudBuild": {
|
||||
|
@ -13,4 +13,4 @@
|
|||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче