diff --git a/Assets/AppIcon.ico b/Assets/AppIcon.ico new file mode 100644 index 0000000..e8dd43d Binary files /dev/null and b/Assets/AppIcon.ico differ diff --git a/Assets/AppIconMaster.png b/Assets/AppIconMaster.png new file mode 100644 index 0000000..b1a0953 Binary files /dev/null and b/Assets/AppIconMaster.png differ diff --git a/Assets/LockScreenLogo.scale-200.png b/Assets/LockScreenLogo.scale-200.png index 7440f0d..6f4be11 100644 Binary files a/Assets/LockScreenLogo.scale-200.png and b/Assets/LockScreenLogo.scale-200.png differ diff --git a/Assets/SplashScreen.scale-200.png b/Assets/SplashScreen.scale-200.png index 32f486a..a9a1efd 100644 Binary files a/Assets/SplashScreen.scale-200.png and b/Assets/SplashScreen.scale-200.png differ diff --git a/Assets/Square150x150Logo.scale-200.png b/Assets/Square150x150Logo.scale-200.png index 53ee377..e326039 100644 Binary files a/Assets/Square150x150Logo.scale-200.png and b/Assets/Square150x150Logo.scale-200.png differ diff --git a/Assets/Square44x44Logo.scale-200.png b/Assets/Square44x44Logo.scale-200.png index f713bba..1848ba7 100644 Binary files a/Assets/Square44x44Logo.scale-200.png and b/Assets/Square44x44Logo.scale-200.png differ diff --git a/Assets/Square44x44Logo.targetsize-16_altform-unplated.png b/Assets/Square44x44Logo.targetsize-16_altform-unplated.png new file mode 100644 index 0000000..2b18109 Binary files /dev/null and b/Assets/Square44x44Logo.targetsize-16_altform-unplated.png differ diff --git a/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Assets/Square44x44Logo.targetsize-24_altform-unplated.png index dc9f5be..e24210d 100644 Binary files a/Assets/Square44x44Logo.targetsize-24_altform-unplated.png and b/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/Assets/Square44x44Logo.targetsize-256_altform-unplated.png b/Assets/Square44x44Logo.targetsize-256_altform-unplated.png new file mode 100644 index 0000000..b579fe3 Binary files /dev/null and b/Assets/Square44x44Logo.targetsize-256_altform-unplated.png differ diff --git a/Assets/Square44x44Logo.targetsize-32_altform-unplated.png b/Assets/Square44x44Logo.targetsize-32_altform-unplated.png new file mode 100644 index 0000000..635fc2e Binary files /dev/null and b/Assets/Square44x44Logo.targetsize-32_altform-unplated.png differ diff --git a/Assets/Square44x44Logo.targetsize-48_altform-unplated.png b/Assets/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 0000000..6f4be11 Binary files /dev/null and b/Assets/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/Assets/StoreLogo.png b/Assets/StoreLogo.png index a4586f2..43c8c51 100644 Binary files a/Assets/StoreLogo.png and b/Assets/StoreLogo.png differ diff --git a/Assets/Wide310x150Logo.scale-200.png b/Assets/Wide310x150Logo.scale-200.png index 8b4a5d0..a73775c 100644 Binary files a/Assets/Wide310x150Logo.scale-200.png and b/Assets/Wide310x150Logo.scale-200.png differ diff --git a/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MBN_STOCK_WEBVIEW.LegacyParityApp.csproj b/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MBN_STOCK_WEBVIEW.LegacyParityApp.csproj index d529662..0fd5c11 100644 --- a/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MBN_STOCK_WEBVIEW.LegacyParityApp.csproj +++ b/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MBN_STOCK_WEBVIEW.LegacyParityApp.csproj @@ -18,6 +18,7 @@ win-x64 win-x64 app.manifest + ..\..\Assets\AppIcon.ico false false 0.1.0 @@ -75,10 +76,18 @@ + + PreserveNewest + PreserveNewest + + + + + diff --git a/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs b/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs index 01f82f0..9695605 100644 --- a/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs +++ b/src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.xaml.cs @@ -257,6 +257,11 @@ public sealed partial class MainWindow : Window var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle); var appWindow = AppWindow.GetFromWindowId(windowId); appWindow.Title = "V-Stock 증권정보송출시스템 for 매일경제TV (26.03.26)"; + var iconPath = Path.Combine(AppContext.BaseDirectory, "Assets", "AppIcon.ico"); + if (File.Exists(iconPath)) + { + appWindow.SetIcon(iconPath); + } if (appWindow.Presenter is OverlappedPresenter presenter) { diff --git a/tests/MBN_STOCK_WEBVIEW.LegacyWeb.Tests/LegacyAppIconContractTests.cs b/tests/MBN_STOCK_WEBVIEW.LegacyWeb.Tests/LegacyAppIconContractTests.cs new file mode 100644 index 0000000..a0e7f30 --- /dev/null +++ b/tests/MBN_STOCK_WEBVIEW.LegacyWeb.Tests/LegacyAppIconContractTests.cs @@ -0,0 +1,124 @@ +using System.Buffers.Binary; + +namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests; + +public sealed class LegacyAppIconContractTests +{ + private static readonly string RepositoryRoot = FindRepositoryRoot(); + private static readonly string AssetsRoot = Path.Combine(RepositoryRoot, "Assets"); + private static readonly string AppRoot = Path.Combine( + RepositoryRoot, + "src", + "MBN_STOCK_WEBVIEW.LegacyParityApp"); + + public static TheoryData PackagePngAssets => new() + { + { "LockScreenLogo.scale-200.png", 48, 48 }, + { "SplashScreen.scale-200.png", 1240, 600 }, + { "Square150x150Logo.scale-200.png", 300, 300 }, + { "Square44x44Logo.scale-200.png", 88, 88 }, + { "Square44x44Logo.targetsize-16_altform-unplated.png", 16, 16 }, + { "Square44x44Logo.targetsize-24_altform-unplated.png", 24, 24 }, + { "Square44x44Logo.targetsize-32_altform-unplated.png", 32, 32 }, + { "Square44x44Logo.targetsize-48_altform-unplated.png", 48, 48 }, + { "Square44x44Logo.targetsize-256_altform-unplated.png", 256, 256 }, + { "StoreLogo.png", 50, 50 }, + { "Wide310x150Logo.scale-200.png", 620, 300 } + }; + + [Theory] + [MemberData(nameof(PackagePngAssets))] + public void PackageIconsAreExactRgbaPngAssets(string fileName, int width, int height) + { + var bytes = File.ReadAllBytes(Path.Combine(AssetsRoot, fileName)); + + Assert.True( + bytes.Length > Math.Max(500, (width * height) / 10), + $"{fileName} still resembles an unrendered placeholder asset."); + Assert.Equal(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }, bytes[..8]); + Assert.Equal("IHDR", System.Text.Encoding.ASCII.GetString(bytes, 12, 4)); + Assert.Equal(width, BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(16, 4))); + Assert.Equal(height, BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(20, 4))); + Assert.Equal(8, bytes[24]); + Assert.Equal(6, bytes[25]); + } + + [Fact] + public void ExecutableIconContainsEveryRequiredWindowsSize() + { + var bytes = File.ReadAllBytes(Path.Combine(AssetsRoot, "AppIcon.ico")); + Assert.True(bytes.Length > 1000); + Assert.Equal((ushort)0, BinaryPrimitives.ReadUInt16LittleEndian(bytes.AsSpan(0, 2))); + Assert.Equal((ushort)1, BinaryPrimitives.ReadUInt16LittleEndian(bytes.AsSpan(2, 2))); + var count = BinaryPrimitives.ReadUInt16LittleEndian(bytes.AsSpan(4, 2)); + Assert.Equal(6, count); + + var sizes = new List(); + for (var index = 0; index < count; index++) + { + var width = bytes[6 + (index * 16)]; + var height = bytes[7 + (index * 16)]; + Assert.Equal(width, height); + sizes.Add(width == 0 ? 256 : width); + } + + Assert.Equal(new[] { 16, 24, 32, 48, 64, 256 }, sizes.Order()); + } + + [Fact] + public void ProjectAndWindowUseTheSharedIconWithoutPackagingTheMasterArtwork() + { + var project = File.ReadAllText(Path.Combine( + AppRoot, + "MBN_STOCK_WEBVIEW.LegacyParityApp.csproj")); + var window = File.ReadAllText(Path.Combine(AppRoot, "MainWindow.xaml.cs")); + var manifest = File.ReadAllText(Path.Combine(AppRoot, "Package.appxmanifest")); + + Assert.Contains("..\\..\\Assets\\AppIcon.ico", + project, + StringComparison.Ordinal); + Assert.Contains("Link=\"Assets\\AppIcon.ico\"", project, StringComparison.Ordinal); + foreach (var targetSize in new[] { 16, 24, 32, 48, 256 }) + { + Assert.Contains( + $"Square44x44Logo.targetsize-{targetSize}_altform-unplated.png", + project, + StringComparison.Ordinal); + } + + Assert.DoesNotContain("AppIconMaster.png", project, StringComparison.Ordinal); + Assert.Contains( + "Path.Combine(AppContext.BaseDirectory, \"Assets\", \"AppIcon.ico\")", + window, + StringComparison.Ordinal); + Assert.Contains("appWindow.SetIcon(iconPath)", window, StringComparison.Ordinal); + Assert.Contains("Square150x150Logo=\"Assets\\Square150x150Logo.png\"", + manifest, + StringComparison.Ordinal); + Assert.Contains("Square44x44Logo=\"Assets\\Square44x44Logo.png\"", + manifest, + StringComparison.Ordinal); + Assert.Contains("Wide310x150Logo=\"Assets\\Wide310x150Logo.png\"", + manifest, + StringComparison.Ordinal); + Assert.Contains("", + manifest, + StringComparison.Ordinal); + } + + private static string FindRepositoryRoot() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null) + { + if (File.Exists(Path.Combine(directory.FullName, "MBN_STOCK_WEBVIEW.sln"))) + { + return directory.FullName; + } + + directory = directory.Parent; + } + + throw new DirectoryNotFoundException("Repository root could not be located."); + } +}