Skip to content

Commit a65e1e3

Browse files
authored
Feature: Added SystemInformation and associated helpers (#121)
1 parent 921db39 commit a65e1e3

File tree

11 files changed

+1631
-1
lines changed

11 files changed

+1631
-1
lines changed

Directory.Packages.props

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
99
<!-- This is kept only for Riverside.Toolkit.Flyouts and will be removed at a later date -->
1010
<PackageVersion Include="CommunityToolkit.Uwp.Media" Version="8.2.250129-preview2" />
11-
<PackageVersion Include="CommunityToolkit.WinUI.Media" Version="8.2.250129-preview2" />
11+
<PackageVersion Include="CommunityToolkit.WinUI.Media" Version="8.2.241112-preview1" />
12+
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.241112-preview1" />
13+
<PackageVersion Include="CommunityToolkit.WinUI.Helpers" Version="8.2.241112-preview1" />
14+
<PackageVersion Include="CommunityToolkit.Uwp.Extensions" Version="8.2.250129-preview2" />
15+
<PackageVersion Include="CommunityToolkit.Uwp.Helpers" Version="8.2.250129-preview2" />
1216
<PackageVersion Include="DependencyPropertyGenerator" Version="1.4.0" />
1317
<PackageVersion Include="H.NotifyIcon" Version="2.0.108" />
1418
<PackageVersion Include="iNKORE.UI.WPF.Modern" Version="0.10.0" />

eng/MultiTarget/GlobalUsings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313
global using global::Windows.ApplicationModel.Activation;
1414
global using global::Windows.Storage;
1515
global using global::Windows.UI;
16+
global using global::Windows.UI.Core;
1617
global using global::Windows.UI.WindowManagement;
18+
global using global::Windows.System;
19+
global using global::Windows.System.UserProfile;
20+
global using global::Windows.System.Profile;
21+
global using global::Windows.Storage.Search;
22+
global using global::Windows.Storage.Streams;
1723
#endif
1824

1925
#if WinUI || Uno

src/core/Riverside.Toolkit.Controls.TitleBar/TitleBarEx.EventHandlers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using WinUIEx;
2+
using WindowSizeChangedEventArgs = Microsoft.UI.Xaml.WindowSizeChangedEventArgs;
23

34
namespace Riverside.Toolkit.Controls;
45

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
#if !Wpf
2+
3+
using CommunityToolkit.Common.Helpers;
4+
using CommunityToolkit.Helpers;
5+
using Riverside.Extensions.Accountability;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Linq;
10+
using System.Threading.Tasks;
11+
using Windows.Storage;
12+
using Windows.System;
13+
using Path = System.IO.Path;
14+
15+
namespace Riverside.Toolkit.Helpers.ObjectStorage;
16+
17+
/// <summary>
18+
/// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints.
19+
/// </summary>
20+
/// <param name="appData">The data store to interact with.</param>
21+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
22+
[NotMyCode("MIT", "https://github.com/CommunityToolkit/WindowsCommunityToolkit", ".NET Foundation", null)]
23+
public partial class ApplicationDataStorageHelper(ApplicationData appData, IObjectSerializer? objectSerializer = null) : IFileStorageHelper, ISettingsStorageHelper<string>
24+
{
25+
/// <summary>
26+
/// Gets the settings container.
27+
/// </summary>
28+
public ApplicationDataContainer Settings => AppData.LocalSettings;
29+
30+
/// <summary>
31+
/// Gets the storage folder.
32+
/// </summary>
33+
public StorageFolder Folder => AppData.LocalFolder;
34+
35+
/// <summary>
36+
/// Gets the storage host.
37+
/// </summary>
38+
protected ApplicationData AppData { get; } = appData ?? throw new ArgumentNullException(nameof(appData));
39+
40+
/// <summary>
41+
/// Gets the serializer for converting stored values.
42+
/// </summary>
43+
protected IObjectSerializer Serializer { get; } = objectSerializer ?? new SystemSerializer();
44+
45+
/// <summary>
46+
/// Get a new instance using ApplicationData.Current and the provided serializer.
47+
/// </summary>
48+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
49+
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
50+
public static ApplicationDataStorageHelper GetCurrent(IObjectSerializer? objectSerializer = null)
51+
{
52+
var appData = ApplicationData.Current;
53+
return new ApplicationDataStorageHelper(appData, objectSerializer);
54+
}
55+
56+
#if !Uno
57+
/// <summary>
58+
/// Get a new instance using the ApplicationData for the provided user and serializer.
59+
/// </summary>
60+
/// <param name="user">App data user owner.</param>
61+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
62+
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
63+
public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user, IObjectSerializer? objectSerializer = null)
64+
{
65+
var appData = await ApplicationData.GetForUserAsync(user);
66+
return new ApplicationDataStorageHelper(appData, objectSerializer);
67+
}
68+
#endif
69+
70+
/// <summary>
71+
/// Determines whether a setting already exists.
72+
/// </summary>
73+
/// <param name="key">Key of the setting (that contains object).</param>
74+
/// <returns>True if a value exists.</returns>
75+
public bool KeyExists(string key)
76+
{
77+
return Settings.Values.ContainsKey(key);
78+
}
79+
80+
/// <summary>
81+
/// Retrieves a single item by its key.
82+
/// </summary>
83+
/// <typeparam name="T">Type of object retrieved.</typeparam>
84+
/// <param name="key">Key of the object.</param>
85+
/// <param name="default">Default value of the object.</param>
86+
/// <returns>The TValue object.</returns>
87+
public T? Read<T>(string key, T? @default = default)
88+
{
89+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
90+
{
91+
return Serializer.Deserialize<T>(valueString);
92+
}
93+
94+
return @default;
95+
}
96+
97+
/// <inheritdoc />
98+
public bool TryRead<T>(string key, out T? value)
99+
{
100+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
101+
{
102+
value = Serializer.Deserialize<T>(valueString);
103+
return true;
104+
}
105+
106+
value = default;
107+
return false;
108+
}
109+
110+
/// <inheritdoc />
111+
public void Save<T>(string key, T value)
112+
{
113+
Settings.Values[key] = Serializer.Serialize(value);
114+
}
115+
116+
/// <inheritdoc />
117+
public bool TryDelete(string key)
118+
{
119+
return Settings.Values.Remove(key);
120+
}
121+
122+
/// <inheritdoc />
123+
public void Clear()
124+
{
125+
Settings.Values.Clear();
126+
}
127+
128+
/// <summary>
129+
/// Determines whether a setting already exists in composite.
130+
/// </summary>
131+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
132+
/// <param name="key">Key of the setting (that contains object).</param>
133+
/// <returns>True if a value exists.</returns>
134+
public bool KeyExists(string compositeKey, string key)
135+
{
136+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
137+
{
138+
return composite.ContainsKey(key);
139+
}
140+
141+
return false;
142+
}
143+
144+
/// <summary>
145+
/// Attempts to retrieve a single item by its key in composite.
146+
/// </summary>
147+
/// <typeparam name="T">Type of object retrieved.</typeparam>
148+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
149+
/// <param name="key">Key of the object.</param>
150+
/// <param name="value">The value of the object retrieved.</param>
151+
/// <returns>The T object.</returns>
152+
public bool TryRead<T>(string compositeKey, string key, out T? value)
153+
{
154+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
155+
{
156+
string compositeValue = (string)composite[key];
157+
if (compositeValue != null)
158+
{
159+
value = Serializer.Deserialize<T>(compositeValue);
160+
return true;
161+
}
162+
}
163+
164+
value = default;
165+
return false;
166+
}
167+
168+
/// <summary>
169+
/// Retrieves a single item by its key in composite.
170+
/// </summary>
171+
/// <typeparam name="T">Type of object retrieved.</typeparam>
172+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
173+
/// <param name="key">Key of the object.</param>
174+
/// <param name="default">Default value of the object.</param>
175+
/// <returns>The T object.</returns>
176+
public T? Read<T>(string compositeKey, string key, T? @default = default)
177+
{
178+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
179+
{
180+
if (composite.TryGetValue(key, out var valueObj) && valueObj is string value)
181+
{
182+
return Serializer.Deserialize<T>(value);
183+
}
184+
}
185+
186+
return @default;
187+
}
188+
189+
/// <summary>
190+
/// Saves a group of items by its key in a composite.
191+
/// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application
192+
/// and for groups of settings which need to be treated in an atomic way.
193+
/// </summary>
194+
/// <typeparam name="T">Type of object saved.</typeparam>
195+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
196+
/// <param name="values">Objects to save.</param>
197+
public void Save<T>(string compositeKey, IDictionary<string, T> values)
198+
{
199+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
200+
{
201+
foreach (KeyValuePair<string, T> setting in values)
202+
{
203+
var serializedValue = Serializer.Serialize(setting.Value) ?? string.Empty;
204+
if (composite.ContainsKey(setting.Key))
205+
{
206+
composite[setting.Key] = serializedValue;
207+
}
208+
else
209+
{
210+
composite.Add(setting.Key, serializedValue);
211+
}
212+
}
213+
}
214+
else
215+
{
216+
composite = [];
217+
foreach (KeyValuePair<string, T> setting in values)
218+
{
219+
var serializedValue = Serializer.Serialize(setting.Value) ?? string.Empty;
220+
composite.Add(setting.Key, serializedValue);
221+
}
222+
223+
Settings.Values[compositeKey] = composite;
224+
}
225+
}
226+
227+
/// <summary>
228+
/// Deletes a single item by its key in composite.
229+
/// </summary>
230+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
231+
/// <param name="key">Key of the object.</param>
232+
/// <returns>A boolean indicator of success.</returns>
233+
public bool TryDelete(string compositeKey, string key)
234+
{
235+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
236+
{
237+
return composite.Remove(key);
238+
}
239+
240+
return false;
241+
}
242+
243+
/// <inheritdoc />
244+
public Task<T?> ReadFileAsync<T>(string filePath, T? @default = default)
245+
{
246+
return ReadFileAsync<T>(Folder, filePath, @default);
247+
}
248+
249+
/// <inheritdoc />
250+
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath)
251+
{
252+
return ReadFolderAsync(Folder, folderPath);
253+
}
254+
255+
/// <inheritdoc />
256+
public Task CreateFileAsync<T>(string filePath, T value)
257+
{
258+
return CreateFileAsync<T>(Folder, filePath, value);
259+
}
260+
261+
/// <inheritdoc />
262+
public Task CreateFolderAsync(string folderPath)
263+
{
264+
return CreateFolderAsync(Folder, folderPath);
265+
}
266+
267+
/// <inheritdoc />
268+
public Task<bool> TryDeleteItemAsync(string itemPath)
269+
{
270+
return TryDeleteItemAsync(Folder, itemPath);
271+
}
272+
273+
/// <inheritdoc />
274+
public Task<bool> TryRenameItemAsync(string itemPath, string newName)
275+
{
276+
return TryRenameItemAsync(Folder, itemPath, newName);
277+
}
278+
279+
private async Task<T?> ReadFileAsync<T>(StorageFolder folder, string filePath, T? @default = default)
280+
{
281+
string value = await StorageFileHelper.ReadTextFromFileAsync(folder, NormalizePath(filePath));
282+
return (value != null) ? Serializer.Deserialize<T>(value) : @default;
283+
}
284+
285+
private async Task<IEnumerable<(DirectoryItemType, string)>> ReadFolderAsync(StorageFolder folder, string folderPath)
286+
{
287+
var targetFolder = await folder.GetFolderAsync(NormalizePath(folderPath));
288+
var items = await targetFolder.GetItemsAsync();
289+
290+
return items.Select((item) =>
291+
{
292+
var itemType = item.IsOfType(StorageItemTypes.File) ? DirectoryItemType.File
293+
: item.IsOfType(StorageItemTypes.Folder) ? DirectoryItemType.Folder
294+
: DirectoryItemType.None;
295+
296+
return (itemType, item.Name);
297+
});
298+
}
299+
300+
private async Task<StorageFile> CreateFileAsync<T>(StorageFolder folder, string filePath, T value)
301+
{
302+
var serializedValue = Serializer.Serialize(value)?.ToString() ?? string.Empty;
303+
return await StorageFileHelper.WriteTextToFileAsync(folder, serializedValue, NormalizePath(filePath), CreationCollisionOption.ReplaceExisting);
304+
}
305+
306+
private async Task CreateFolderAsync(StorageFolder folder, string folderPath)
307+
{
308+
await folder.CreateFolderAsync(NormalizePath(folderPath), CreationCollisionOption.OpenIfExists);
309+
}
310+
311+
private async Task<bool> TryDeleteItemAsync(StorageFolder folder, string itemPath)
312+
{
313+
try
314+
{
315+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
316+
await item.DeleteAsync();
317+
return true;
318+
}
319+
catch
320+
{
321+
return false;
322+
}
323+
}
324+
325+
private async Task<bool> TryRenameItemAsync(StorageFolder folder, string itemPath, string newName)
326+
{
327+
try
328+
{
329+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
330+
await item.RenameAsync(newName, NameCollisionOption.FailIfExists);
331+
return true;
332+
}
333+
catch
334+
{
335+
return false;
336+
}
337+
}
338+
339+
private string NormalizePath(string path)
340+
{
341+
var directoryName = Path.GetDirectoryName(path) ?? string.Empty;
342+
var fileName = Path.GetFileName(path);
343+
return Path.Combine(directoryName, fileName);
344+
}
345+
}
346+
347+
#endif

0 commit comments

Comments
 (0)