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

Add support for more Package properties #9532

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,25 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UITests.Shared.Windows_ApplicationModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:generic="using:System.Collections.Generic"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.Resources>
<Style TargetType="TextBlock" x:Key="LabelStyle">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</UserControl.Resources>
<StackPanel Margin="12" Spacing="4">
<TextBlock Text="Display Name" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind DisplayName}" />
<TextBlock Text="Installed Path" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind InstalledPath}" />
<TextBlock Text="Architecture" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind Architecture}" />
<TextBlock Text="FamilyName" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind FamilyName}" />
<TextBlock Text="FullName" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind FullName}" />
<TextBlock Text="Name" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="ProductId" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind ProductId}" />
<TextBlock Text="Publisher" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind Publisher}" />
<TextBlock Text="PublisherId" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind PublisherId}" />
<TextBlock Text="ResourceId" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind ResourceId}" />
<TextBlock Text="Version" Style="{StaticResource LabelStyle}" />
<TextBlock Text="{x:Bind Version}" />
</StackPanel>
<ListView ItemsSource="{x:Bind Items}" Margin="12">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:PackageProperty">
<StackPanel Orientation="Horizontal">
<TextBlock>
<Run Text="{x:Bind Name}" FontWeight="Bold" /><Run Text=":" />
<Run Text="{x:Bind Value}" />
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Uno.UI.Samples.Controls;
using Windows.ApplicationModel;
using Windows.System;
Expand All @@ -13,32 +15,68 @@ public sealed partial class PackageTests : UserControl
public PackageTests()
{
this.InitializeComponent();
DisplayName = SafeSet(() => Package.Current.DisplayName);
InstalledPath = SafeSet(() => Package.Current.InstalledPath);
var packageId = Package.Current.Id;
Architecture = SafeSet(() => packageId.Architecture);
FamilyName = SafeSet(() => packageId.FamilyName);
FullName = SafeSet(() => packageId.FullName);
Name = SafeSet(() => packageId.Name);
Publisher = SafeSet(() => packageId.Publisher);
PublisherId = SafeSet(() => packageId.PublisherId);
ResourceId = SafeSet(() => packageId.ResourceId);
Version = SafeSet(() => $"{packageId.Version.Major}.{packageId.Version.Minor}.{packageId.Version.Revision}.{packageId.Version.Build}");

AddProperties(Package.Current);
}

private string SafeSet(Func<object> propertyGetter)
public void AddProperties(Package package)
{
SafeAdd("DisplayName", () => package.DisplayName);
SafeAdd("Description", () => package.Description);
SafeAdd("EffectiveExternalLocation", () => package.EffectiveExternalLocation?.Path);
SafeAdd("EffectiveExternalPath", () => package.EffectiveExternalPath);
SafeAdd("EffectiveLocation", () => package.EffectiveLocation.Path);
SafeAdd("EffectivePath", () => package.EffectivePath);
SafeAdd("Status", () => package.Status);
SafeAdd("PublisherDisplayName", () => package.PublisherDisplayName);
SafeAdd("InstallDate", () => package.InstallDate);
SafeAdd("InstalledDate", () => package.InstalledDate);
SafeAdd("InstalledLocation", () => package.InstalledLocation.Path);
SafeAdd("InstalledPath", () => package.InstalledPath);
SafeAdd("IsBundle", () => package.IsBundle);
SafeAdd("IsDevelopmentMode", () => package.IsDevelopmentMode);
SafeAdd("IsFramework", () => package.IsFramework);
SafeAdd("IsOptional", () => package.IsOptional);
SafeAdd("IsResourcePackage", () => package.IsResourcePackage);
SafeAdd("IsStub", () => package.IsStub);
SafeAdd("Logo", () => package.Logo);
SafeAdd("MachineExternalLocation", () => package.MachineExternalLocation?.Path);
SafeAdd("MachineExternalPath", () => package.MachineExternalPath);
SafeAdd("MutableLocation", () => package.MutableLocation);
SafeAdd("MutablePath", () => package.MutablePath);
SafeAdd("SignatureKind", () => package.SignatureKind);
SafeAdd("UserExternalLocation", () => package.UserExternalLocation?.Path);
SafeAdd("UserExternalPath", () => package.UserExternalPath);
SafeAdd("Id.Architecture", () => package.Id.Architecture);
SafeAdd("Id.FamilyName", () => package.Id.FamilyName);
SafeAdd("Id.FullName", () => package.Id.FullName);
SafeAdd("Id.Name", () => package.Id.Name);
SafeAdd("Id.Publisher", () => package.Id.Publisher);
SafeAdd("Id.PublisherId", () => package.Id.PublisherId);
SafeAdd("Id.ResourceId", () => package.Id.ResourceId);
SafeAdd("Id.Version", () => $"{package.Id.Version.Major}.{package.Id.Version.Minor}.{package.Id.Version.Revision}.{package.Id.Version.Build}");
}

public ObservableCollection<PackageProperty> Items { get; } = new ObservableCollection<PackageProperty>();

private void SafeAdd(string propertyName, Func<object> propertyGetter)
{
try
{
return propertyGetter()?.ToString() ?? "(null)";
var value = propertyGetter()?.ToString() ?? "(null)";
Items.Add(new PackageProperty(propertyName, value));
}
catch (NotImplementedException)
{
return "Not implemented";
Items.Add(new PackageProperty(propertyName, "Not implemented"));
}
catch (NotSupportedException)
{
Items.Add(new PackageProperty(propertyName, "Not supported"));
}
catch (Exception ex)
{
return $"Exception thrown - {ex.Message}";
Items.Add(new PackageProperty(propertyName, $"Exception thrown - {ex.Message}"));
}
}

Expand All @@ -64,4 +102,17 @@ private string SafeSet(Func<object> propertyGetter)

public string Version { get; }
}

public class PackageProperty
{
public PackageProperty(string name, string value)
{
Name = name;
Value = value;
}

public string Name { get; }

public string Value { get; }
}
}
4 changes: 2 additions & 2 deletions src/Uno.UWP/ApplicationModel/Package.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ namespace Windows.ApplicationModel
{
public partial class Package
{
public string DisplayName =>
private string GetDisplayName() =>
Application.Context.ApplicationInfo.LoadLabel(Application.Context.PackageManager);

private string GetInstalledPath() => "assets://" + ContextHelper.Current.PackageCodePath;

private bool GetInnerIsDevelopmentMode()
private bool GetIsDevelopmentMode()
{
try
{
Expand Down
165 changes: 64 additions & 101 deletions src/Uno.UWP/ApplicationModel/Package.Other.cs
Original file line number Diff line number Diff line change
@@ -1,132 +1,95 @@
#if !(__IOS__ || __ANDROID__ || __MACOS__)
#nullable enable
#if !(__IOS__ || __ANDROID__ || __MACOS__)
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography;
using System.Xml;
using Uno.Extensions;
using Uno.Foundation.Logging;
using Uno.UI;
using Windows.ApplicationModel.Email.DataProvider;
using Windows.Storage;

namespace Windows.ApplicationModel;

public partial class Package
namespace Windows.ApplicationModel
{
private const string PackageManifestName = "Package.appxmanifest";

private static Assembly? _entryAssembly;
private string _displayName = "";

partial void InitializePlatform()
{
}

internal static bool IsManifestInitialized { get; private set; }

private bool GetInnerIsDevelopmentMode() => false;

private DateTimeOffset GetInstallDate() => DateTimeOffset.Now;

private string GetInstalledPath()
{
if (_entryAssembly?.Location is { Length: > 0 } location)
{
return global::System.IO.Path.GetDirectoryName(location) ?? "";
}
else if (AppContext.BaseDirectory is { Length: > 0 } baseDirectory)
{
return global::System.IO.Path.GetDirectoryName(baseDirectory) ?? "";
}

return Environment.CurrentDirectory;
}

public string DisplayName
public partial class Package
{
get => EnsureLocalized(_displayName);
private set => _displayName = value;
}
private const string PackageManifestName = "Package.appxmanifest";
private static Assembly? _entryAssembly;
private string _displayName = "";
private string _logo = "ms-appx://logo";
private bool _manifestParsed;

public Uri? Logo { get; set; }
private bool GetIsDevelopmentMode() => false;

internal static void SetEntryAssembly(Assembly entryAssembly)
{
_entryAssembly = entryAssembly;
Current.Id.Name = entryAssembly.GetName().Name; // Set the package name to the entry assembly name by default.
Current.ParsePackageManifest();
IsManifestInitialized = true;
}
private DateTimeOffset GetInstallDate() => new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero);

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we could create a file (or read some other file) on first launch then read the date so we can provide that date more reliably?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a good idea! It would be the first launch date, but that is still more accurate than nothing 😀 . However, first we need to resolve #8910 so that it is really app specific.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why use such placeholders? It is simply not supported atrribute.
App programmer should be warned on compile time that he/she should check app logic that uses GetInstalledDate.
E.g.: tryware, and logic as
if(date.Now - GetInstalledDate > 14 days) showerror("free use period ends, please buy this app")

internal static string EnsureLocalized(string stringToLocalize)
{
if (stringToLocalize.StartsWith("ms-resource:", StringComparison.OrdinalIgnoreCase))
private string GetInstalledLocation()
{
var resourceKey = stringToLocalize["ms-resource:".Length..].Trim();
var resourceString = Resources.ResourceLoader.GetForViewIndependentUse().GetString(resourceKey);

if (!string.IsNullOrEmpty(resourceString))
if (!string.IsNullOrEmpty(AppContext.BaseDirectory))
{
stringToLocalize = resourceString;
return global::System.IO.Path.GetDirectoryName(AppContext.BaseDirectory) ?? "";
}
else
{
return Environment.CurrentDirectory;
}
}

return stringToLocalize;
}

private void ParsePackageManifest()
{
if (_entryAssembly is null)
private string GetDisplayName()
{
return;
TryParsePackageManifest();
return _displayName;
}

if (_entryAssembly.GetManifestResourceStream(PackageManifestName) is not { } manifest)
private Uri GetLogo()
{
if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
{
this.Log().Debug($"Skipping manifest reading, unable to find [{PackageManifestName}]");
}

return;
TryParsePackageManifest();
return new Uri(_logo, UriKind.RelativeOrAbsolute);
}

try
internal static void SetEntryAssembly(Assembly entryAssembly)
{
var doc = new XmlDocument();
doc.Load(manifest);

var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("d", "http://schemas.microsoft.com/appx/manifest/foundation/windows10");

DisplayName = doc.SelectSingleNode("/d:Package/d:Properties/d:DisplayName", nsmgr)?.InnerText ?? "";

#if __SKIA__
Description = doc.SelectSingleNode("/d:Package/d:Properties/d:Description", nsmgr)?.InnerText ?? "";
PublisherDisplayName = doc.SelectSingleNode("/d:Package/d:Properties/d:PublisherDisplayName", nsmgr)?.InnerText ?? "";
#endif

var logoUri = doc.SelectSingleNode("/d:Package/d:Properties/d:Logo", nsmgr)?.InnerText ?? "";
if (Uri.TryCreate(logoUri, UriKind.RelativeOrAbsolute, out var logo))
{
Logo = logo;
}
_entryAssembly = entryAssembly;
}

var idNode = doc.SelectSingleNode("/d:Package/d:Identity", nsmgr);
if (idNode is not null)
private void TryParsePackageManifest()
{
if (_entryAssembly != null && !_manifestParsed)
{
Id.Name = idNode.Attributes?.GetNamedItem("Name")?.Value ?? "";
var manifest = _entryAssembly.GetManifestResourceStream(PackageManifestName);

var versionString = idNode.Attributes?.GetNamedItem("Version")?.Value ?? "";
if (Version.TryParse(versionString, out var version))
if (manifest != null)
{
Id.Version = new PackageVersion(version);
try
{
var doc = new XmlDocument();
doc.Load(manifest);

var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("d", "http://schemas.microsoft.com/appx/manifest/foundation/windows10");

_displayName = doc.SelectSingleNode("/d:Package/d:Properties/d:DisplayName", nsmgr)?.InnerText ?? "";
_logo = doc.SelectSingleNode("/d:Package/d:Properties/d:Logo", nsmgr)?.InnerText ?? "";

_manifestParsed = true;
}
catch (Exception ex)
{
if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Error))
{
this.Log().Error($"Failed to read manifest [{PackageManifestName}]", ex);
}
}
}
else
{
if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Debug))
{
this.Log().Debug($"Skipping manifest reading, unable to find [{PackageManifestName}]");
}
}

Id.Publisher = idNode.Attributes?.GetNamedItem("Publisher")?.Value ?? "";
}
}
catch (Exception ex)
{
if (this.Log().IsEnabled(Uno.Foundation.Logging.LogLevel.Error))
{
this.Log().Error($"Failed to read manifest [{PackageManifestName}]", ex);
}
}
}
Expand Down
Loading
Loading