Merge branch 'main' into winui

# Conflicts:
#	CommunityToolkit.Common/Extensions/ISettingsStorageHelperExtensions.cs
#	CommunityToolkit.Common/Helpers/ObjectStorage/DirectoryItemType.cs
#	CommunityToolkit.Common/Helpers/ObjectStorage/IFileStorageHelper.cs
#	CommunityToolkit.Common/Helpers/ObjectStorage/IObjectSerializer.cs
#	CommunityToolkit.Common/Helpers/ObjectStorage/ISettingsStorageHelper.cs
#	CommunityToolkit.Common/Helpers/ObjectStorage/SystemSerializer.cs
#	CommunityToolkit.WinUI.SampleApp/Controls/SampleAppMarkdownRenderer.cs
#	CommunityToolkit.WinUI.SampleApp/Models/Sample.cs
#	CommunityToolkit.WinUI.SampleApp/Models/Samples.cs
#	CommunityToolkit.WinUI.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs
#	CommunityToolkit.WinUI.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs
#	CommunityToolkit.WinUI/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs
#	CommunityToolkit.WinUI/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs
#	CommunityToolkit.WinUI/Helpers/ObjectStorage/IObjectSerializer.cs
#	CommunityToolkit.WinUI/Helpers/SystemInformation.cs
#	UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
#	azure-pipelines.yml
#	build/StyleXaml.bat
#	build/UpdateHeaders.bat
#	build/build.cake
#	build/build.ps1
This commit is contained in:
Alexandre Zollinger Chohfi 2021-07-29 14:27:35 -07:00
Родитель 0b8589132b 9326ea29c6
Коммит a19da3c409
44 изменённых файлов: 1249 добавлений и 333 удалений

Просмотреть файл

@ -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>

Просмотреть файл

@ -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 CommunityToolkit.Common.Helpers;
namespace CommunityToolkit.Common.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 CommunityToolkit.Common.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 CommunityToolkit.Common.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 CommunityToolkit.Common.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 CommunityToolkit.Common.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 CommunityToolkit.Common.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();
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Common.Helpers;
using CommunityToolkit.Common.Parsers.Markdown;
using CommunityToolkit.Common.Parsers.Markdown.Blocks;
using CommunityToolkit.Common.Parsers.Markdown.Inlines;
@ -407,19 +408,19 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.Graph.Converters;
// using CommunityToolkit.Graph.Providers;
using CommunityToolkit.Common.Helpers;
using CommunityToolkit.WinUI.Helpers;
using CommunityToolkit.WinUI.Input.GazeInteraction;
using CommunityToolkit.WinUI.SampleApp.Models;
@ -44,7 +45,7 @@ namespace CommunityToolkit.WinUI.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);

Просмотреть файл

@ -10,8 +10,8 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Common.Helpers;
using CommunityToolkit.WinUI.Helpers;
using Windows.ApplicationModel;
namespace CommunityToolkit.WinUI.SampleApp
{
@ -23,7 +23,7 @@ namespace CommunityToolkit.WinUI.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)
{
@ -129,7 +129,7 @@ namespace CommunityToolkit.WinUI.SampleApp
if (_recentSamples == null)
{
_recentSamples = new LinkedList<Sample>();
var savedSamples = _localObjectStorageHelper.Read<string>(_recentSamplesStorageKey);
var savedSamples = _settingsStorage.Read<string>(_recentSamplesStorageKey);
if (savedSamples != null)
{
@ -175,7 +175,7 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.Common.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 CommunityToolkit.Common.Helpers;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI.Xaml;
@ -9,7 +10,7 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.SampleApp.SamplePages
}
// Save into local storage
localStorageHelper.Save(KeyTextBox.Text, ContentTextBox.Text);
_settingsStorage.Save(KeyTextBox.Text, ContentTextBox.Text);
}
}
}

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Windows.ApplicationModel;
using Windows.UI.Core;
using NavigationView = Microsoft.UI.Xaml.Controls;
namespace CommunityToolkit.WinUI.UI.Controls
{
@ -42,12 +43,11 @@ namespace CommunityToolkit.WinUI.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>
@ -203,7 +203,7 @@ namespace CommunityToolkit.WinUI.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)
{
@ -336,8 +336,6 @@ namespace CommunityToolkit.WinUI.UI.Controls
/// </summary>
private void SetBackButtonVisibility(ListDetailsViewState? previousState = null)
{
const int backButtonVisible = 1;
if (DesignMode.DesignModeEnabled)
{
return;
@ -368,7 +366,7 @@ namespace CommunityToolkit.WinUI.UI.Controls
}
else
{
SetNavigationViewBackButtonState(backButtonVisible, true);
SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible.Visible, true);
}
}
}
@ -458,27 +456,18 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.Common.Helpers;
using Windows.Storage;
namespace CommunityToolkit.WinUI.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 CommunityToolkit.Common.Helpers;
using Windows.Storage;
using Windows.System;
#nullable enable
namespace CommunityToolkit.WinUI.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="Common.Helpers.SystemSerializer"/>.</param>
public ApplicationDataStorageHelper(ApplicationData appData, Common.Helpers.IObjectSerializer? objectSerializer = null)
{
this.AppData = appData ?? throw new ArgumentNullException(nameof(appData));
this.Serializer = objectSerializer ?? new Common.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 Common.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="Common.Helpers.SystemSerializer"/>.</param>
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
public static ApplicationDataStorageHelper GetCurrent(Common.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="Common.Helpers.SystemSerializer"/>.</param>
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user, Common.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.Helpers
{
/// <summary>
/// A basic serialization service.
/// </summary>
[Obsolete("IObjectSerializer has been migrated to the Microsoft.Toolkit (CommunityToolkit.Common) package.")]
public interface IObjectSerializer
{
/// <summary>

Просмотреть файл

@ -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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.Helpers
@ -9,20 +10,21 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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>

Просмотреть файл

@ -6,6 +6,7 @@ using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Common.Helpers;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
@ -23,9 +24,9 @@ namespace CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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)
@ -323,20 +324,20 @@ namespace CommunityToolkit.WinUI.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;
}
@ -349,13 +350,13 @@ namespace CommunityToolkit.WinUI.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
@ -365,7 +366,7 @@ namespace CommunityToolkit.WinUI.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());
}
@ -376,28 +377,28 @@ namespace CommunityToolkit.WinUI.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;
}

Просмотреть файл

@ -7,11 +7,13 @@ using System.Reflection;
using CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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 CommunityToolkit.WinUI.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;

Просмотреть файл

@ -79,10 +79,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" />

Просмотреть файл

@ -56,12 +56,12 @@ jobs:
version: 5.0.302
performMultiLevelLookup: true
- 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
@ -73,7 +73,7 @@ jobs:
### UI Integration Tests ###
- powershell: .\build\build.ps1 --target=UITest
- powershell: .\build\build.ps1 -Target UITest
displayName: UI Integration Tests
- task: PublishPipelineArtifact@1
@ -92,7 +92,7 @@ jobs:
### Package ###
- powershell: .\build\build.ps1 --target=Package
- powershell: .\build\build.ps1 -Target Package
displayName: Package
- task: PowerShell@2
@ -134,7 +134,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
@ -150,12 +150,5 @@ jobs:
# artifactType: container
# artifactName: SmokeTestBundles
# - task: PublishBuildArtifacts@1
# displayName: Publish Smoke Test Artifacts
# inputs:
# pathToPublish: $(build.artifactstagingdirectory)\SmokeTestBundles
# artifactType: container
# artifactName: SmokeTestBundles
#
# - powershell: .\SmokeTests\SmokeTestAnalysis.ps1
# displayName: Analyze Package Sizes

Просмотреть файл

@ -5,12 +5,11 @@ $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)]
[parameter(ValueFromPipeline = $true)]
[xml] $xml,
[parameter(Mandatory=$true)]
[parameter(Mandatory = $true)]
[string] $nodeName)
# Try the old style csproj. Also format required for .targets and .props files
@ -24,10 +23,9 @@ function Get-Nodes
return $r
}
function Get-NodeValue
{
function Get-NodeValue {
param(
[parameter(ValueFromPipeline=$true)]
[parameter(ValueFromPipeline = $true)]
[xml] $xml,
[string] $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,22 @@ 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 {
}
return $false
@ -101,24 +94,18 @@ 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) {
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 +118,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,7 +133,7 @@ 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"
@ -160,6 +147,7 @@ foreach($version in $versions) {
Write-Host
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=1.0.1
#addin nuget:?package=Cake.FileHelpers&version=4.0.1
#addin nuget:?package=Cake.Powershell&version=1.0.0
#addin nuget:?package=Cake.GitVersioning&version=3.4.190
#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;
@ -94,7 +94,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.");
}
}
@ -196,13 +196,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)
@ -230,7 +231,8 @@ Task("Package")
.Does(() =>
{
// Invoke the pack target in the end
var buildSettings = new MSBuildSettings {
var buildSettings = new MSBuildSettings
{
MaxCpuCount = 0
}
.SetConfiguration("Release")
@ -280,7 +282,7 @@ Task("Test")
{
Configuration = "Release",
NoBuild = true,
Loggers = new [] { "trx;LogFilePrefix=VsTestResults" },
Loggers = new[] { "trx;LogFilePrefix=VsTestResults" },
Verbosity = DotNetCoreVerbosity.Normal,
ArgumentCustomization = arg => arg.Append($"-s {baseDir}/.runsettings"),
};
@ -329,13 +331,12 @@ Task("MSTestUITest")
{
Configuration = "Release",
NoBuild = true,
Loggers = new [] { "trx;LogFilePrefix=VsTestResults" },
Loggers = new[] { "trx;LogFilePrefix=VsTestResults" },
Verbosity = DotNetCoreVerbosity.Normal
};
DotNetCoreTest(file.FullPath, testSettings);
});
//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////
@ -358,12 +359,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");
@ -376,8 +378,6 @@ Task("StyleXaml")
}
});
//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -10,6 +10,7 @@ This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
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
@ -85,10 +86,15 @@ function MD5HashFile([string] $filePath)
}
finally
{
if ($file -ne $null)
if ($null -ne $file)
{
$file.Dispose()
}
if ($null -ne $md5)
{
$md5.Dispose()
}
}
}
@ -147,7 +153,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
}
@ -165,7 +171,7 @@ if (!(Test-Path $NUGET_EXE)) {
}
# These are automatic variables in PowerShell Core, but not in Windows PowerShell 5.x
if (-not (Test-Path variable:global:ismacos)) {
if (-not (Test-Path variable:global:IsMacOS)) {
$IsLinux = $false
$IsMacOS = $false
}
@ -266,4 +272,12 @@ $cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")"
exit $LASTEXITCODE
$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

Просмотреть файл

@ -7,7 +7,7 @@
"SeparateByGroups": false,
"AttributeIndentation": 0,
"AttributeIndentationStyle": 1,
"RemoveDesignTimeReferences": false,
"RemoveDesignTimeReferences": false,
"EnableAttributeReordering": true,
"AttributeOrderingRuleGroups": [
"x:Class",

Просмотреть файл

@ -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"
}
}
}

Просмотреть файл

@ -7,7 +7,7 @@
"^refs/heads/rel/\\d+\\.\\d+\\.\\d+", // we also release branches starting with rel/N.N.N
"^refs/heads/rel/winui/\\d+\\.\\d+\\.\\d+" // we also release branches starting with rel/winui/N.N.N
],
"nugetPackageVersion":{
"nugetPackageVersion": {
"semVer": 2
},
"cloudBuild": {