File: GenerateTizenManifest.cs
Web Access
Project: src\src\SingleProject\Resizetizer\src\Resizetizer.csproj (Microsoft.Maui.Resizetizer)
#nullable enable
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
 
namespace Microsoft.Maui.Resizetizer
{
	public class GenerateTizenManifest : Task
	{
		const string ApplicationIdPlaceholder = "maui-application-id-placeholder";
		const string LabelPlaceholder = "maui-application-title-placeholder";
		const string ManifestVersionPlaceholder = "0.0.0";
		const string AppIconPlaceholder = "maui-appicon-placeholder";
		const string TizenManifestFileName = "tizen-manifest.xml";
		const string IconDefaultDpiType = "xhdpi";
		const string IconImageExtension = ".png";
		const string UiApplicationName = "ui-application";
		const string PackageName = "package";
		const string AppidName = "appid";
		const string VersionName = "version";
		const string LabelName = "label";
		const string IconName = "icon";
		const string SplashScreensName = "splash-screens";
		const string SplashScreenName = "splash-screen";
		const string DpiName = "dpi";
 
		[Required]
		public string IntermediateOutputPath { get; set; } = null!;
 
		[Required]
		public string TizenManifestFile { get; set; } = TizenManifestFileName;
 
		public string GeneratedFilename { get; set; } = TizenManifestFileName;
 
		public string? ApplicationId { get; set; }
 
		public string? ApplicationDisplayVersion { get; set; }
 
		public string? ApplicationVersion { get; set; }
 
		public string? ApplicationTitle { get; set; }
 
		public ITaskItem[]? AppIcon { get; set; }
 
		public ITaskItem[]? SplashScreen { get; set; }
 
		[Output]
		public ITaskItem GeneratedTizenManifest { get; set; } = null!;
 
		string? _tizenManifestFilePath;
 
		public override bool Execute()
		{
			try
			{
				Directory.CreateDirectory(IntermediateOutputPath);
 
				_tizenManifestFilePath = Path.Combine(Environment.CurrentDirectory, TizenManifestFile);
 
				var targetFilename = Path.Combine(IntermediateOutputPath, GeneratedFilename);
 
				var manifest = XDocument.Load(_tizenManifestFilePath);
 
				UpdateManifest(manifest);
 
				manifest.Save(targetFilename);
 
				GeneratedTizenManifest = new TaskItem(targetFilename);
			}
			catch (Exception ex)
			{
				Log.LogErrorFromException(ex);
			}
 
			return !Log.HasLoggedErrors;
		}
 
		void UpdateManifest(XDocument tizenManifest)
		{
			var xmlns = tizenManifest.Root!.GetDefaultNamespace();
			var manifest = tizenManifest.Root;
			var uiApplication = manifest.Element(xmlns + UiApplicationName);
 
			if (manifest == null || uiApplication == null)
			{
				return;
			}
 
			UpdateSharedManifest(xmlns, manifest);
 
			UpdateSharedResources(xmlns, manifest);
		}
 
		void UpdateSharedManifest(XNamespace xmlns, XElement manifest)
		{
			var uiApplication = manifest.Element(xmlns + UiApplicationName);
 
			if (!string.IsNullOrEmpty(ApplicationId))
			{
				UpdateElementAttribute(manifest, PackageName, ApplicationId, ApplicationIdPlaceholder);
				UpdateElementAttribute(uiApplication, AppidName, ApplicationId, ApplicationIdPlaceholder);
			}
 
			if (!string.IsNullOrEmpty(ApplicationDisplayVersion))
			{
				if (TryMergeVersionNumbers(ApplicationDisplayVersion, out var finalVersion))
				{
					UpdateElementAttribute(manifest, VersionName, finalVersion, ManifestVersionPlaceholder);
				}
				else
				{
					Log.LogWarning($"ApplicationDisplayVersion '{ApplicationDisplayVersion}' was not a valid version for Tizen");
				}
 
			}
 
			if (!string.IsNullOrEmpty(ApplicationTitle))
			{
				var label = uiApplication.Element(xmlns + LabelName);
				if (label == null)
				{
					label = new XElement(xmlns + LabelName);
					uiApplication.AddFirst(label);
				}
				UpdateElementValue(label, ApplicationTitle, LabelPlaceholder);
			}
		}
 
		void UpdateSharedResources(XNamespace xmlns, XElement manifestElement)
		{
			var uiApplicationElement = manifestElement.Element(xmlns + UiApplicationName);
			var appIconInfo = AppIcon?.Length > 0 ? ResizeImageInfo.Parse(AppIcon[0]) : null;
 
			if (appIconInfo != null)
			{
				var xiconName = xmlns + IconName;
				var iconElements = uiApplicationElement.Elements(xiconName);
 
				var iconPlaceholderElements = iconElements.Where(d => d.Value == AppIconPlaceholder);
				foreach (var icon in iconPlaceholderElements)
				{
					if (icon.Attribute(DpiName) == null)
					{
						var defaultDpi = DpiPath.Tizen.AppIcon.Where(n => n.Path.EndsWith(IconDefaultDpiType)).FirstOrDefault();
						icon.Value = IconDefaultDpiType + "/" + appIconInfo.OutputName + defaultDpi.FileSuffix + IconImageExtension;
					}
					else
					{
						string dpiValue = icon.Attribute(DpiName).Value;
						string fileSuffix = dpiValue == IconDefaultDpiType ? "xhigh" : "high";
						icon.Value = dpiValue + "/" + appIconInfo.OutputName + fileSuffix + IconImageExtension;
					}
				}
			}
			var splashInfo = SplashScreen?.Length > 0 ? ResizeImageInfo.Parse(SplashScreen[0]) : null;
 
			if (splashInfo != null)
			{
				var splashscreensElement = uiApplicationElement.Element(xmlns + SplashScreensName);
				if (splashscreensElement == null)
				{
					splashscreensElement = new XElement(xmlns + SplashScreensName);
					uiApplicationElement.Add(splashscreensElement);
				}
 
				foreach (var image in TizenSplashUpdater.splashDpiMap)
				{
					var splashElements = splashscreensElement.Elements(xmlns + SplashScreenName).Where(
						d => d.Attribute("type")?.Value == "img"
						&& d.Attribute(DpiName)?.Value == image.Key.Resolution
						&& d.Attribute("orientation")?.Value == image.Key.Orientation
						&& d.Attribute("indicator-display")?.Value == "false");
					if (splashElements.Count() == 0)
					{
						var splashscreenElement = new XElement(xmlns + SplashScreenName);
						splashscreenElement.SetAttributeValue("src", image.Value);
						splashscreenElement.SetAttributeValue("type", "img");
						splashscreenElement.SetAttributeValue(DpiName, image.Key.Resolution);
						splashscreenElement.SetAttributeValue("orientation", image.Key.Orientation);
						splashscreenElement.SetAttributeValue("indicator-display", "false");
						splashscreensElement.Add(splashscreenElement);
					}
				}
			}
		}
 
		void UpdateElementAttribute(XElement element, XName attrName, string? value, string? placeholder)
		{
			var attr = element.Attribute(attrName);
			if (attr == null || string.IsNullOrEmpty(attr.Value) || attr.Value == placeholder)
			{
				element.SetAttributeValue(attrName, value);
			}
		}
 
		void UpdateElementValue(XElement element, string? value, string? placeholder)
		{
			if (string.IsNullOrEmpty(element.Value) || element.Value == placeholder)
			{
				element.Value = value;
			}
		}
 
		public static bool TryMergeVersionNumbers(string? displayVersion, out string? finalVersion)
		{
			displayVersion = displayVersion?.Trim();
			finalVersion = null;
 
			var parts = displayVersion?.Split('.') ?? Array.Empty<string>();
			if (parts.Length > 3)
				return false;
 
			var v = new int[3];
			for (var i = 0; i < 3 && i < parts.Length; i++)
			{
				if (!int.TryParse(parts[i], out var parsed))
					return false;
 
				v[i] = parsed;
			}
 
			if (!VerifyTizenVersion(v[0], v[1], v[2]))
				return false;
 
			finalVersion = $"{v[0]:0}.{v[1]:0}.{v[2]:0}";
			return true;
		}
 
		static bool VerifyTizenVersion(int x, int y, int z)
		{
			return (x < 0 || x > 255 || y < 0 || y > 255 || z < 0 || z > 65535) ? false : true;
		}
	}
}