Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Application.Properties obsolete and fix implementation #2158

Merged
merged 10 commits into from
Aug 25, 2021
26 changes: 13 additions & 13 deletions Compatibility.ControlGallery.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30503.244
# Visual Studio Version 17
VisualStudioVersion = 17.0.31620.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controls", "Controls", "{9AD757F5-E57A-459D-A0A7-E0675E045B84}"
EndProject
Expand Down Expand Up @@ -125,39 +125,39 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Maps", "Maps", "{4030A39F-7
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{AC1BF2B0-ACB7-42EF-9D4F-CF4424A4EF76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.Android.UnitTests", "src\Compatibility\Core\tests\Android\Compatibility.Android.UnitTests.csproj", "{A4C57790-71D1-467C-A69E-2BD84CB6666C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.Android.UnitTests", "src\Compatibility\Core\tests\Android\Compatibility.Android.UnitTests.csproj", "{A4C57790-71D1-467C-A69E-2BD84CB6666C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{9318E5D8-A9C4-42DB-89B8-36EC31362ABB}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Compatibility.ControlGallery.Issues.Shared", "src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.shproj", "{AE2513CB-4E5E-4E5C-8237-88954D4C9433}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.ControlGallery.Core", "src\Compatibility\ControlGallery\src\Core\Compatibility.ControlGallery.Core.csproj", "{E7D17A14-8399-4139-BDAA-EE4C1A3447FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.ControlGallery.Android", "src\Compatibility\ControlGallery\src\Android\Compatibility.ControlGallery.Android.csproj", "{BB933AF2-9498-4C4E-9F76-AF66A550BED3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.ControlGallery.Android", "src\Compatibility\ControlGallery\src\Android\Compatibility.ControlGallery.Android.csproj", "{BB933AF2-9498-4C4E-9F76-AF66A550BED3}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Compatibility.UITests.Shared", "src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.Shared.shproj", "{E175485B-3C8C-47D7-8DD5-F7FED627EB25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.ControlGallery.Android.UITests", "src\Compatibility\ControlGallery\test\Android.UITests\Compatibility.ControlGallery.Android.UITests.csproj", "{A34EBE01-25BF-4E69-A2DC-2288DC625541}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.ControlGallery.Android.UITests", "src\Compatibility\ControlGallery\test\Android.UITests\Compatibility.ControlGallery.Android.UITests.csproj", "{A34EBE01-25BF-4E69-A2DC-2288DC625541}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.ControlGallery.iOS.UITests", "src\Compatibility\ControlGallery\test\iOS.UITests\Compatibility.ControlGallery.iOS.UITests.csproj", "{EADD8100-B3AE-4A31-92C4-267A64A1C6EB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.ControlGallery.iOS.UITests", "src\Compatibility\ControlGallery\test\iOS.UITests\Compatibility.ControlGallery.iOS.UITests.csproj", "{EADD8100-B3AE-4A31-92C4-267A64A1C6EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.ControlGallery.iOS", "src\Compatibility\ControlGallery\src\iOS\Compatibility.ControlGallery.iOS.csproj", "{C7131F14-274F-4B55-ACA9-E81731AD012F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.ControlGallery.iOS", "src\Compatibility\ControlGallery\src\iOS\Compatibility.ControlGallery.iOS.csproj", "{C7131F14-274F-4B55-ACA9-E81731AD012F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.Maps.iOS", "src\Compatibility\Maps\src\iOS\Compatibility.Maps.iOS.csproj", "{ABA078C4-F9BB-4924-8B2B-10FE0D2F5491}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.Maps.iOS", "src\Compatibility\Maps\src\iOS\Compatibility.Maps.iOS.csproj", "{ABA078C4-F9BB-4924-8B2B-10FE0D2F5491}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compatibility.iOS.UnitTests", "src\Compatibility\Core\tests\iOS\Compatibility.iOS.UnitTests.csproj", "{52C50E88-7F15-45FE-A63C-8E7C76CA2BAE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility.iOS.UnitTests", "src\Compatibility\Core\tests\iOS\Compatibility.iOS.UnitTests.csproj", "{52C50E88-7F15-45FE-A63C-8E7C76CA2BAE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compatibility", "src\Compatibility\Core\src\Compatibility.csproj", "{8E86F0DA-ED1C-409E-A7A9-DF3E58498620}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{a34ebe01-25bf-4e69-a2dc-2288dc625541}*SharedItemsImports = 4
src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.projitems*{a34ebe01-25bf-4e69-a2dc-2288dc625541}*SharedItemsImports = 4
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{a34ebe01-25bf-4e69-a2dc-2288dc625541}*SharedItemsImports = 5
src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.projitems*{a34ebe01-25bf-4e69-a2dc-2288dc625541}*SharedItemsImports = 5
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{ae2513cb-4e5e-4e5c-8237-88954d4c9433}*SharedItemsImports = 13
src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.projitems*{e175485b-3c8c-47d7-8dd5-f7fed627eb25}*SharedItemsImports = 13
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{e7d17a14-8399-4139-bdaa-ee4c1a3447fc}*SharedItemsImports = 5
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{eadd8100-b3ae-4a31-92c4-267a64a1c6eb}*SharedItemsImports = 4
src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.projitems*{eadd8100-b3ae-4a31-92c4-267a64a1c6eb}*SharedItemsImports = 4
src\Compatibility\ControlGallery\src\Issues.Shared\Compatibility.ControlGallery.Issues.Shared.projitems*{eadd8100-b3ae-4a31-92c4-267a64a1c6eb}*SharedItemsImports = 5
src\Compatibility\ControlGallery\src\UITests.Shared\Compatibility.UITests.projitems*{eadd8100-b3ae-4a31-92c4-267a64a1c6eb}*SharedItemsImports = 5
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Microsoft.Maui.Controls.Compatibility.ControlGallery
{
#pragma warning disable CS0618 // Type or member is obsolete
public class MainPageLifeCycleTests : ContentPage
{
readonly StackLayout _numTimesStartedLayout;
Expand Down Expand Up @@ -75,4 +76,5 @@ public void UpdateLabels()
((Label)_numTimesResumedLayout.Children[1]).Text = ((int)Application.Current.Properties["TimesResumed"]).ToString();
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}
2 changes: 2 additions & 0 deletions src/Compatibility/ControlGallery/src/Core/SimpleApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Microsoft.Maui.Controls.Compatibility.ControlGallery
{
#pragma warning disable CS0618 // Type or member is obsolete
public class SimpleApp : Application
{
public SimpleApp()
Expand Down Expand Up @@ -39,4 +40,5 @@ static async void SerializeProperties()
await Current.SavePropertiesAsync();
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ public async Task GitHub8682()
{
await Task.Run(async () =>
{
#pragma warning disable CS0618 // Type or member is obsolete
await Application.Current.SavePropertiesAsync();
#pragma warning restore CS0618 // Type or member is obsolete
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using NUnit.Framework;
#endif

#pragma warning disable CS0618 // Type or member is obsolete
namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Issues
{
#if UITEST
Expand Down Expand Up @@ -98,3 +99,4 @@ public void Bugzilla28709Test()
#endif
}
}
#pragma warning restore CS0618 // Type or member is obsolete
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Linq;
#endif

#pragma warning disable CS0618 // Type or member is obsolete
namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Issues
{
#if UITEST
Expand Down Expand Up @@ -127,3 +128,4 @@ public void Issue2104Test()
#endif
}
}
#pragma warning restore CS0618 // Type or member is obsolete
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using NUnit.Framework;
#endif

#pragma warning disable CS0618 // Type or member is obsolete
namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Issues
{
#if UITEST
Expand Down Expand Up @@ -94,4 +95,5 @@ public void SaveDifferentLengthValuesIntoPropertyStore()
}
#endif
}
#pragma warning restore CS0618 // Type or member is obsolete
}
96 changes: 41 additions & 55 deletions src/Compatibility/Core/src/Android/Deserializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Threading.Tasks;
Expand All @@ -11,37 +12,35 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
internal class Deserializer : IDeserializer
{
const string PropertyStoreFile = "PropertyStore.forms";
const string PropertyStoreFile = "PropertyStore.maui";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since no compatibility with Xamarin.Forms is expected, would it be better to use System.Json for serialization and deserialization? It is at least more space efficient if not faster too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is because it is a separate dependency we have to bring in. Also, this feature is very obsolete and bringing in a dependency for a feature we basically want to delete is not worth it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under the impression that System.Json is part of net6.0 and hence net6.0-android. Never mind then 😅. I agree, it's not worth it.


static string GetFilePath()
=> Path.Combine(Essentials.FileSystem.CacheDirectory, PropertyStoreFile);

public Task<IDictionary<string, object>> DeserializePropertiesAsync()
{
// Deserialize property dictionary to local storage
// Make sure to use Internal
return Task.Run(() =>
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.FileExists(PropertyStoreFile))
return null;
var path = GetFilePath();

using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
{
if (stream.Length == 0)
return null;
if (!File.Exists(path))
return null;

try
{
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
return (IDictionary<string, object>)dcs.ReadObject(reader);
}
catch (Exception e)
{
Debug.WriteLine("Could not deserialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}");
}
}
using var stream = File.OpenRead(path);
using var xmlReader = XmlReader.Create(stream);
using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader);

try
{
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
return (IDictionary<string, object>)dcs.ReadObject(reader);
}
catch (Exception e)
{
Debug.WriteLine("Could not deserialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}");
}

return null;
Expand All @@ -51,45 +50,32 @@ public Task<IDictionary<string, object>> DeserializePropertiesAsync()
public Task SerializePropertiesAsync(IDictionary<string, object> properties)
{
properties = new Dictionary<string, object>(properties);

// No need to write 0 properties if no file exists
if (properties.Count <= 0)
return Task.CompletedTask;

// Serialize property dictionary to local storage
// Make sure to use Internal
return Task.Run(() =>
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
// No need to write 0 properties if no file exists
if (properties.Count == 0 && !store.FileExists(PropertyStoreFile))
{
return;
}
using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
{
try
{
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
dcs.WriteObject(writer, properties);
writer.Flush();
}
catch (Exception e)
{
Debug.WriteLine("Could not serialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}");
return;
}
}
var path = GetFilePath();

try
{
if (store.FileExists(PropertyStoreFile))
store.DeleteFile(PropertyStoreFile);
store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
}
catch (Exception e)
{
Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}");
}
using var stream = File.Create(path);
using var xmlWriter = XmlWriter.Create(stream);
using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter);

try
{
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
dcs.WriteObject(writer, properties);
writer.Flush();
}
catch (Exception e)
{
Debug.WriteLine("Could not serialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}");
return;
}
});
}
Expand Down
85 changes: 51 additions & 34 deletions src/Compatibility/Core/src/Windows/WindowsSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,79 @@
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Windows.Storage;
using System.Xml;
using Microsoft.Maui.Controls.Internals;

namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
internal sealed class WindowsSerializer : IDeserializer
{
const string PropertyStoreFile = "PropertyStore.forms";
const string PropertyStoreFile = "PropertyStore.maui";

public async Task<IDictionary<string, object>> DeserializePropertiesAsync()
static string GetFilePath()
=> Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), PropertyStoreFile);

public Task<IDictionary<string, object>> DeserializePropertiesAsync()
{
try
// Deserialize property dictionary to local storage
// Make sure to use Internal
return Task.Run(() =>
{
StorageFile file = await ApplicationData.Current.RoamingFolder.GetFileAsync(PropertyStoreFile).DontSync();
using (Stream stream = (await file.OpenReadAsync().DontSync()).AsStreamForRead())
{
if (stream.Length == 0)
return new Dictionary<string, object>(4);

try
{
var serializer = new DataContractSerializer(typeof(IDictionary<string, object>));
return (IDictionary<string, object>)serializer.ReadObject(stream);
}
catch (Exception e)
{
Debug.WriteLine("Could not deserialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}");
}
var path = GetFilePath();

if (!File.Exists(path))
return null;

using var stream = File.OpenRead(path);
using var xmlReader = XmlReader.Create(stream);
using var reader = XmlDictionaryReader.CreateDictionaryReader(xmlReader);

try
{
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
return (IDictionary<string, object>)dcs.ReadObject(reader);
}
}
catch (FileNotFoundException)
{
return new Dictionary<string, object>(4);
}
catch (Exception e)
{
Debug.WriteLine("Could not deserialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while reading Application properties: {e}");
}

return null;
});
}

public async Task SerializePropertiesAsync(IDictionary<string, object> properties)
public Task SerializePropertiesAsync(IDictionary<string, object> properties)
{
StorageFile file = await ApplicationData.Current.RoamingFolder.CreateFileAsync(PropertyStoreFile, CreationCollisionOption.ReplaceExisting).DontSync();
using (StorageStreamTransaction transaction = await file.OpenTransactedWriteAsync().DontSync())
properties = new Dictionary<string, object>(properties);

// No need to write 0 properties if no file exists
if (properties.Count <= 0)
return Task.CompletedTask;

// Serialize property dictionary to local storage
// Make sure to use Internal
return Task.Run(() =>
{
var path = GetFilePath();

using var stream = File.Create(path);
using var xmlWriter = XmlWriter.Create(stream);
using var writer = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter);

try
{
Stream stream = transaction.Stream.AsStream();
var serializer = new DataContractSerializer(typeof(IDictionary<string, object>));
serializer.WriteObject(stream, properties);
await transaction.CommitAsync().DontSync();
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
dcs.WriteObject(writer, properties);
writer.Flush();
}
catch (Exception e)
{
Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
Debug.WriteLine("Could not serialize properties: " + e.Message);
Log.Warning("Microsoft.Maui.Controls.Compatibility PropertyStore", $"Exception while writing Application properties: {e}");
return;
}
}
});
}
}
}
Loading