VR4Medical/ICI/Library/PackageCache/com.unity.xr.management@20be87dea580/Editor/AndroidManifest/AndroidManifestProcessor.cs
2025-07-29 13:45:50 +03:00

219 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using UnityEditor;
using UnityEditor.Android;
using UnityEditor.XR.Management;
using UnityEngine;
using UnityEngine.XR.Management;
namespace Unity.XR.Management.AndroidManifest.Editor
{
/// <summary>
/// Class that retrieves Android manifest entries required by classes that implement the IAndroidManifestEntryProvider interface.
/// </summary>
internal class AndroidManifestProcessor
{
private static readonly string k_androidManifestFileName = "AndroidManifest.xml";
#if UNITY_2021_1_OR_NEWER
private static readonly string k_xrLibraryDirectoryName = "xrmanifest.androidlib";
private static readonly string k_xrLibraryManifestRelativePath = string.Join(Path.DirectorySeparatorChar.ToString(), k_xrLibraryDirectoryName, k_androidManifestFileName);
#endif
private static readonly List<string> k_activityElementPath = new List<string>() { "manifest", "application", "activity" };
private readonly string m_unityLibraryManifestFilePath;
#if UNITY_2021_1_OR_NEWER
private readonly string m_xrPackageManifestTemplateFilePath;
private readonly string m_xrLibraryManifestFilePath;
#endif
private readonly XRManagerSettings m_xrSettings;
internal AndroidManifestProcessor(string gradleProjectPath, XRManagerSettings settings)
{
m_unityLibraryManifestFilePath = string.Join(Path.DirectorySeparatorChar.ToString(), gradleProjectPath, "src", "main", k_androidManifestFileName);
m_xrSettings = settings;
}
#if UNITY_2021_1_OR_NEWER
internal AndroidManifestProcessor(
string gradleProjectPath,
string xrManagementPackagePath,
XRManagerSettings settings)
{
m_xrPackageManifestTemplateFilePath = string.Join(Path.DirectorySeparatorChar.ToString(), xrManagementPackagePath, k_xrLibraryManifestRelativePath);
m_xrLibraryManifestFilePath = string.Join(Path.DirectorySeparatorChar.ToString(), gradleProjectPath, k_xrLibraryManifestRelativePath);
m_unityLibraryManifestFilePath = string.Join(Path.DirectorySeparatorChar.ToString(), gradleProjectPath, "src", "main", k_androidManifestFileName);
m_xrSettings = settings;
}
#endif
internal bool UseActivityAppEntry { get; set; } = true;
internal bool UseGameActivityAppEntry { get; set; } = false;
internal void ProcessManifestRequirements(List<IAndroidManifestRequirementProvider> manifestProviders)
{
var activeLoaders = GetActiveLoaderList();
// Get manifest entries from providers
var manifestRequirements = manifestProviders
.Select(provider => provider.ProvideManifestRequirement())
.OfType<ManifestRequirement>()
.Distinct()
// Requirements can apply to different platforms, so we filter out those whose loaders aren't currently active
.Where(requirement => requirement.SupportedXRLoaders.Any(loaderType => activeLoaders.Contains(loaderType)))
.ToList();
var mergedRequiredElements =
MergeElements(
manifestRequirements
.SelectMany(requirement => requirement.OverrideElements));
var elementsToBeRemoved = manifestRequirements
.SelectMany(requirement => requirement.RemoveElements)
.OfType<ManifestElement>();
#if UNITY_2021_1_OR_NEWER
// The intent-filter elements are not merged by default,
// so we separate them from the XR manifest to add them later.
// Otherwise, the application won't load correctly.
var newRequiredElements = manifestRequirements
.SelectMany(requirement => requirement.NewElements)
.Where(element => !element.ElementPath.Contains("activity"));
var newActivityElements = manifestRequirements
.SelectMany(requirement => requirement.NewElements)
.Where(element =>
element.ElementPath.Contains("activity")
&& !element.ElementPath.Contains("intent-filter"));
var newIntentElements = manifestRequirements
.SelectMany(requirement => requirement.NewElements)
.Where(element => element.ElementPath.Contains("intent-filter"));
{
var xrLibraryManifest = new AndroidManifestDocument(m_xrPackageManifestTemplateFilePath);
var unityLibraryManifest = new AndroidManifestDocument(m_unityLibraryManifestFilePath);
// Create activity elements depending on the selected application entry in Player Settings
if (UseActivityAppEntry)
{
xrLibraryManifest.CreateNewElement(k_activityElementPath, new Dictionary<string, string> { { "name", "com.unity3d.player.UnityPlayerActivity" } });
}
if (UseGameActivityAppEntry)
{
xrLibraryManifest.CreateNewElement(k_activityElementPath, new Dictionary<string, string> { { "name", "com.unity3d.player.UnityPlayerGameActivity" } });
}
AddExportedAttributeToActivity(xrLibraryManifest, newIntentElements);
if (UseActivityAppEntry && UseGameActivityAppEntry)
{
xrLibraryManifest.CreateElements(newRequiredElements);
// Add all related activity elements to each element
foreach (var newActivityElement in newActivityElements)
{
xrLibraryManifest.CreateNewElementInAllPaths(newActivityElement.ElementPath, newActivityElement.Attributes);
}
xrLibraryManifest.OverrideElements(mergedRequiredElements);
// Add all related activity elements to each element
foreach (var newIntentElement in newIntentElements)
{
unityLibraryManifest.CreateNewElementInAllPaths(newIntentElement.ElementPath, newIntentElement.Attributes);
}
unityLibraryManifest.RemoveElements(elementsToBeRemoved);
}
else
{
xrLibraryManifest.CreateElements(newRequiredElements);
xrLibraryManifest.CreateElements(newActivityElements);
xrLibraryManifest.OverrideElements(mergedRequiredElements);
unityLibraryManifest.CreateElements(newIntentElements, false); // Add the intents in the unity library manifest
unityLibraryManifest.RemoveElements(elementsToBeRemoved);
}
// Write updated manifests
xrLibraryManifest.SaveAs(m_xrLibraryManifestFilePath);
unityLibraryManifest.Save();
}
#else
var newRequiredElements = manifestRequirements
.SelectMany(requirement => requirement.NewElements);
{
var manifest = new AndroidManifestDocument(m_unityLibraryManifestFilePath);
manifest.CreateNewElement(k_activityElementPath, new Dictionary<string, string> { { "name", "com.unity3d.player.UnityPlayerActivity" } });
manifest.CreateElements(newRequiredElements);
manifest.OverrideElements(mergedRequiredElements);
manifest.RemoveElements(elementsToBeRemoved);
// Write manifest into project's library path
manifest.Save();
}
#endif
}
/// <summary>
/// Merges the elements of given <see cref="IEnumerable{T}"/> of type <see cref="ManifestElement"/> based on their <see cref="ManifestElement.ElementPath"/>.
/// Their key-value pair attributes are deduped and merged into a single element.
/// </summary>
/// <param name="source"><see cref="IEnumerable{T}"/> of type <see cref="ManifestElement"/> containing all elements to be merged.</param>
/// <returns>Filtered <see cref="IEnumerable{T}"/> of type <see cref="ManifestElement"/> with unique elements.</returns>
private IEnumerable<ManifestElement> MergeElements(IEnumerable<ManifestElement> source)
{
return source
.GroupBy(
(requirement) => requirement.ElementPath,
(requirement) => requirement,
(elementPath, groupedRequirements) => {
var mergedAttributes = groupedRequirements
.SelectMany(requirement => requirement.Attributes)
.Distinct()
.ToDictionary(pair => pair.Key, pair => pair.Value);
return new ManifestElement
{
ElementPath = elementPath,
Attributes = mergedAttributes
};
});
}
private List<Type> GetActiveLoaderList()
{
if (!m_xrSettings)
{
// No loaders active, don't throw error
Debug.LogWarning("No XR Manager settings found, manifest entries will not be updated.");
return new List<Type>();
}
return m_xrSettings.activeLoaders
.Select(loader => loader.GetType())
.ToList();
}
private void AddExportedAttributeToActivity(
AndroidManifestDocument xrLibraryManifest,
IEnumerable<ManifestElement> newIntentElements)
{
if (newIntentElements.Any())
{
// Add exported attribute to all activities in the XR library manifest, as required by the Android manifest
var activityPath = string.Join("/", k_activityElementPath);
var activityNodes = xrLibraryManifest.SelectNodes(activityPath);
foreach (var activity in activityNodes)
{
XmlAttribute exportedAttribute = xrLibraryManifest.CreateAttribute("android:exported", "http://schemas.android.com/apk/res/android");
exportedAttribute.Value = "true";
((XmlElement)activity).Attributes.Append(exportedAttribute);
}
}
}
}
}