Singularity/Assets/Plugins/ImaginationOverflow/UniversalDeepLinking/Editor/Xcode/ProjectCapabilityManager.cs
2024-05-06 11:45:45 -07:00

579 lines
25 KiB
C#

using System;
using System.IO;
namespace ImaginationOverflow.UniversalDeepLinking.Editor.Xcode
{
// This class is here to help you add capabilities to your Xcode project.
// Because capabilities modify the PBXProject, the entitlements file and/or the Info.plist and not consistently,
// it can be tedious.
// Therefore this class open the PBXProject that is always modify by capabilities and open Entitlement and info.plist only when needed.
// For optimisation reasons, we write the file only in the close method.
// If you don't call it the file will not be written.
public class ProjectCapabilityManager
{
private readonly string m_BuildPath;
private readonly string m_TargetGuid;
private readonly string m_PBXProjectPath;
private readonly string m_EntitlementFilePath;
private PlistDocument m_Entitlements;
private PlistDocument m_InfoPlist;
protected internal PBXProject project;
// Create the manager with the required parameter to open files and set the properties in the write place.
public ProjectCapabilityManager(string pbxProjectPath, string entitlementFilePath, string targetName)
{
m_BuildPath = Directory.GetParent(Path.GetDirectoryName(pbxProjectPath)).FullName;
m_EntitlementFilePath = entitlementFilePath;
m_PBXProjectPath = pbxProjectPath;
project = new PBXProject();
project.ReadFromString(File.ReadAllText(m_PBXProjectPath));
m_TargetGuid = project.TargetGuidByName(targetName);
}
// Write the actual file to the disk.
// If you don't call this method nothing will change.
public void WriteToFile()
{
File.WriteAllText(m_PBXProjectPath, project.WriteToString());
if (m_Entitlements != null)
m_Entitlements.WriteToFile(PBXPath.Combine(m_BuildPath, m_EntitlementFilePath));
if (m_InfoPlist != null)
m_InfoPlist.WriteToFile(PBXPath.Combine(m_BuildPath, "Info.plist"));
}
// Add the iCloud capability with the desired options.
public void AddiCloud(bool keyValueStorage, bool iCloudDocument, string[] customContainers)
{
var ent = GetOrCreateEntitlementDoc();
var val = (ent.root[ICloudEntitlements.ContainerIdValue] = new PlistElementArray()) as PlistElementArray;
if (iCloudDocument)
{
val.values.Add(new PlistElementString(ICloudEntitlements.ContainerIdValue));
var ser = (ent.root[ICloudEntitlements.ServicesKey] = new PlistElementArray()) as PlistElementArray;
ser.values.Add(new PlistElementString(ICloudEntitlements.ServicesKitValue));
ser.values.Add(new PlistElementString(ICloudEntitlements.ServicesDocValue));
var ubiquity = (ent.root[ICloudEntitlements.UbiquityContainerIdKey] = new PlistElementArray()) as PlistElementArray;
ubiquity.values.Add(new PlistElementString(ICloudEntitlements.UbiquityContainerIdValue));
for (var i = 0; i < customContainers.Length; i++)
{
ser.values.Add(new PlistElementString(customContainers[i]));
}
}
if (keyValueStorage)
{
ent.root[ICloudEntitlements.KeyValueStoreKey] = new PlistElementString(ICloudEntitlements.KeyValueStoreValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.iCloud, m_EntitlementFilePath, iCloudDocument);
}
// Add Push (or remote) Notifications capability to your project
public void AddPushNotifications(bool development)
{
GetOrCreateEntitlementDoc().root[PushNotificationEntitlements.Key] = new PlistElementString(development ? PushNotificationEntitlements.DevelopmentValue : PushNotificationEntitlements.ProductionValue);
project.AddCapability(m_TargetGuid, PBXCapabilityType.PushNotifications, m_EntitlementFilePath);
}
// Add GameCenter capability to the project.
public void AddGameCenter()
{
var arr = (GetOrCreateInfoDoc().root[GameCenterInfo.Key] ?? (GetOrCreateInfoDoc().root[GameCenterInfo.Key] = new PlistElementArray())) as PlistElementArray;
arr.values.Add(new PlistElementString(GameCenterInfo.Value));
project.AddCapability(m_TargetGuid, PBXCapabilityType.GameCenter);
}
// Add Wallet capability to the project.
public void AddWallet(string[] passSubset)
{
var arr = (GetOrCreateEntitlementDoc().root[WalletEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
if ((passSubset == null || passSubset.Length == 0) && arr != null)
{
arr.values.Add(new PlistElementString(WalletEntitlements.BaseValue + WalletEntitlements.BaseValue));
}
else
{
for (var i = 0; i < passSubset.Length; i++)
{
if (arr != null)
arr.values.Add(new PlistElementString(WalletEntitlements.BaseValue + passSubset[i]));
}
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.Wallet, m_EntitlementFilePath);
}
// Add Siri capability to the project.
public void AddSiri()
{
GetOrCreateEntitlementDoc().root[SiriEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.Siri, m_EntitlementFilePath);
}
// Add Apple Pay capability to the project.
public void AddApplePay(string[] merchants)
{
var arr = (GetOrCreateEntitlementDoc().root[ApplePayEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < merchants.Length; i++)
{
arr.values.Add(new PlistElementString(merchants[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.ApplePay, m_EntitlementFilePath);
}
// Add In App Purchase capability to the project.
public void AddInAppPurchase()
{
project.AddCapability(m_TargetGuid, PBXCapabilityType.InAppPurchase);
}
// Add Maps capability to the project.
public void AddMaps(MapsOptions options)
{
var bundleArr = (GetOrCreateInfoDoc().root[MapsInfo.BundleKey] ?? (GetOrCreateInfoDoc().root[MapsInfo.BundleKey] = new PlistElementArray())) as PlistElementArray;
bundleArr.values.Add(new PlistElementDict());
PlistElementDict bundleDic = GetOrCreateUniqueDictElementInArray(bundleArr);
bundleDic[MapsInfo.BundleNameKey] = new PlistElementString(MapsInfo.BundleNameValue);
var bundleTypeArr = (bundleDic[MapsInfo.BundleTypeKey] ?? (bundleDic[MapsInfo.BundleTypeKey] = new PlistElementArray())) as PlistElementArray;
GetOrCreateStringElementInArray(bundleTypeArr, MapsInfo.BundleTypeValue);
var optionArr = (GetOrCreateInfoDoc().root[MapsInfo.ModeKey] ??
(GetOrCreateInfoDoc().root[MapsInfo.ModeKey] = new PlistElementArray())) as PlistElementArray;
if ((options & MapsOptions.Airplane) == MapsOptions.Airplane)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModePlaneValue);
}
if ((options & MapsOptions.Bike) == MapsOptions.Bike)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeBikeValue);
}
if ((options & MapsOptions.Bus) == MapsOptions.Bus)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeBusValue);
}
if ((options & MapsOptions.Car) == MapsOptions.Car)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeCarValue);
}
if ((options & MapsOptions.Ferry) == MapsOptions.Ferry)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeFerryValue);
}
if ((options & MapsOptions.Other) == MapsOptions.Other)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeOtherValue);
}
if ((options & MapsOptions.Pedestrian) == MapsOptions.Pedestrian)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModePedestrianValue);
}
if ((options & MapsOptions.RideSharing) == MapsOptions.RideSharing)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeRideShareValue);
}
if ((options & MapsOptions.StreetCar) == MapsOptions.StreetCar)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeStreetCarValue);
}
if ((options & MapsOptions.Subway) == MapsOptions.Subway)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeSubwayValue);
}
if ((options & MapsOptions.Taxi) == MapsOptions.Taxi)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeTaxiValue);
}
if ((options & MapsOptions.Train) == MapsOptions.Train)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeTrainValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.Maps);
}
// Add Personal VPN capability to the project.
public void AddPersonalVPN()
{
var arr = (GetOrCreateEntitlementDoc().root[VPNEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
arr.values.Add(new PlistElementString(VPNEntitlements.Value));
project.AddCapability(m_TargetGuid, PBXCapabilityType.PersonalVPN, m_EntitlementFilePath);
}
// Add Background capability to the project with the options wanted.
public void AddBackgroundModes(BackgroundModesOptions options)
{
var optionArr = (GetOrCreateInfoDoc().root[BackgroundInfo.Key] ??
(GetOrCreateInfoDoc().root[BackgroundInfo.Key] = new PlistElementArray())) as PlistElementArray;
if ((options & BackgroundModesOptions.ActsAsABluetoothLEAccessory) == BackgroundModesOptions.ActsAsABluetoothLEAccessory)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeActsBluetoothValue);
}
if ((options & BackgroundModesOptions.AudioAirplayPiP) == BackgroundModesOptions.AudioAirplayPiP)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeAudioValue);
}
if ((options & BackgroundModesOptions.BackgroundFetch) == BackgroundModesOptions.BackgroundFetch)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeFetchValue);
}
if ((options & BackgroundModesOptions.ExternalAccessoryCommunication) == BackgroundModesOptions.ExternalAccessoryCommunication)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeExtAccessoryValue);
}
if ((options & BackgroundModesOptions.LocationUpdates) == BackgroundModesOptions.LocationUpdates)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeLocationValue);
}
if ((options & BackgroundModesOptions.NewsstandDownloads) == BackgroundModesOptions.NewsstandDownloads)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeNewsstandValue);
}
if ((options & BackgroundModesOptions.RemoteNotifications) == BackgroundModesOptions.RemoteNotifications)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModePushValue);
}
if ((options & BackgroundModesOptions.VoiceOverIP) == BackgroundModesOptions.VoiceOverIP)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeVOIPValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.BackgroundModes);
}
// Add Keychain Sharing capability to the project with a list of groups.
public void AddKeychainSharing(string[] accessGroups)
{
var arr = (GetOrCreateEntitlementDoc().root[KeyChainEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
if (accessGroups != null)
{
for (var i = 0; i < accessGroups.Length; i++)
{
arr.values.Add(new PlistElementString(accessGroups[i]));
}
}
else
{
arr.values.Add(new PlistElementString(KeyChainEntitlements.DefaultValue));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.KeychainSharing, m_EntitlementFilePath);
}
// Add Inter App Audio capability to the project.
public void AddInterAppAudio()
{
GetOrCreateEntitlementDoc().root[AudioEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.InterAppAudio, m_EntitlementFilePath);
}
// Add Associated Domains capability to the project.
public void AddAssociatedDomains(string[] domains)
{
var arr = (GetOrCreateEntitlementDoc().root[AssociatedDomainsEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < domains.Length; i++)
{
arr.values.Add(new PlistElementString(domains[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.AssociatedDomains, m_EntitlementFilePath);
}
// Add App Groups capability to the project.
public void AddAppGroups(string[] groups)
{
var arr = (GetOrCreateEntitlementDoc().root[AppGroupsEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < groups.Length; i++)
{
arr.values.Add(new PlistElementString(groups[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.AppGroups, m_EntitlementFilePath);
}
// Add HomeKit capability to the project.
public void AddHomeKit()
{
GetOrCreateEntitlementDoc().root[HomeKitEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.HomeKit, m_EntitlementFilePath);
}
// Add Data Protection capability to the project.
public void AddDataProtection()
{
GetOrCreateEntitlementDoc().root[DataProtectionEntitlements.Key] = new PlistElementString(DataProtectionEntitlements.Value);
project.AddCapability(m_TargetGuid, PBXCapabilityType.DataProtection, m_EntitlementFilePath);
}
// Add HealthKit capability to the project.
public void AddHealthKit()
{
var capabilityArr = (GetOrCreateInfoDoc().root[HealthInfo.Key] ??
(GetOrCreateInfoDoc().root[HealthInfo.Key] = new PlistElementArray())) as PlistElementArray;
GetOrCreateStringElementInArray(capabilityArr, HealthInfo.Value);
GetOrCreateEntitlementDoc().root[HealthKitEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.HealthKit, m_EntitlementFilePath);
}
// Add Wireless Accessory Configuration capability to the project.
public void AddWirelessAccessoryConfiguration()
{
GetOrCreateEntitlementDoc().root[WirelessAccessoryConfigurationEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.WirelessAccessoryConfiguration, m_EntitlementFilePath);
}
private PlistDocument GetOrCreateEntitlementDoc()
{
if (m_Entitlements == null)
{
m_Entitlements = new PlistDocument();
string[] entitlementsFiles = Directory.GetFiles(m_BuildPath, m_EntitlementFilePath);
if (entitlementsFiles.Length > 0)
{
m_Entitlements.ReadFromFile(entitlementsFiles[0]);
}
else
{
m_Entitlements.Create();
}
}
return m_Entitlements;
}
private PlistDocument GetOrCreateInfoDoc()
{
if (m_InfoPlist == null)
{
m_InfoPlist = new PlistDocument();
string[] infoFiles = Directory.GetFiles(m_BuildPath + "/", "Info.plist");
if (infoFiles.Length > 0)
{
m_InfoPlist.ReadFromFile(infoFiles[0]);
}
else
{
m_InfoPlist.Create();
}
}
return m_InfoPlist;
}
private PlistElementString GetOrCreateStringElementInArray(PlistElementArray root, string value)
{
PlistElementString r = null;
var c = root.values.Count;
var exist = false;
for (var i = 0; i < c; i++)
{
if (root.values[i] is PlistElementString && (root.values[i] as PlistElementString).value == value)
{
r = root.values[i] as PlistElementString;
exist = true;
}
}
if (!exist)
{
r = new PlistElementString(value);
root.values.Add(r);
}
return r;
}
private PlistElementDict GetOrCreateUniqueDictElementInArray(PlistElementArray root)
{
PlistElementDict r;
if (root.values.Count == 0)
{
r = root.values[0] as PlistElementDict;
}
else
{
r = new PlistElementDict();
root.values.Add(r);
}
return r;
}
}
// The list of options available for Background Mode.
[Flags]
[Serializable]
public enum BackgroundModesOptions
{
None = 0,
AudioAirplayPiP = 1<<0,
LocationUpdates = 1<<1,
VoiceOverIP = 1<<2,
NewsstandDownloads = 1<<3,
ExternalAccessoryCommunication = 1<<4,
UsesBluetoothLEAccessory = 1<<5,
ActsAsABluetoothLEAccessory = 1<<6,
BackgroundFetch = 1<<7,
RemoteNotifications = 1<<8
}
// The list of options available for Maps.
[Serializable]
[Flags]
public enum MapsOptions
{
None = 0,
Airplane = 1<<0,
Bike = 1<<1,
Bus = 1<<2,
Car = 1<<3,
Ferry = 1<<4,
Pedestrian = 1<<5,
RideSharing = 1<<6,
StreetCar = 1<<7,
Subway = 1<<8,
Taxi = 1<<9,
Train = 1<<10,
Other = 1<<11
}
/* Follows the large quantity of string used as key and value all over the place in the info.plist or entitlements file. */
internal class GameCenterInfo
{
internal static readonly string Key = "UIRequiredDeviceCapabilities";
internal static readonly string Value = "gamekit";
}
internal class MapsInfo
{
internal static readonly string BundleKey = "CFBundleDocumentTypes";
internal static readonly string BundleNameKey = "CFBundleTypeName";
internal static readonly string BundleNameValue = "MKDirectionsRequest";
internal static readonly string BundleTypeKey = "LSItemContentTypes";
internal static readonly string BundleTypeValue = "com.apple.maps.directionsrequest";
internal static readonly string ModeKey = "MKDirectionsApplicationSupportedModes";
internal static readonly string ModePlaneValue = "MKDirectionsModePlane";
internal static readonly string ModeBikeValue = "MKDirectionsModeBike";
internal static readonly string ModeBusValue = "MKDirectionsModeBus";
internal static readonly string ModeCarValue = "MKDirectionsModeCar";
internal static readonly string ModeFerryValue = "MKDirectionsModeFerry";
internal static readonly string ModeOtherValue = "MKDirectionsModeOther";
internal static readonly string ModePedestrianValue = "MKDirectionsModePedestrian";
internal static readonly string ModeRideShareValue = "MKDirectionsModeRideShare";
internal static readonly string ModeStreetCarValue = "MKDirectionsModeStreetCar";
internal static readonly string ModeSubwayValue = "MKDirectionsModeSubway";
internal static readonly string ModeTaxiValue = "MKDirectionsModeTaxi";
internal static readonly string ModeTrainValue = "MKDirectionsModeTrain";
}
internal class BackgroundInfo
{
internal static readonly string Key = "UIBackgroundModes";
internal static readonly string ModeAudioValue = "audio";
internal static readonly string ModeBluetoothValue = "bluetooth-central";
internal static readonly string ModeActsBluetoothValue = "bluetooth-peripheral";
internal static readonly string ModeExtAccessoryValue = "external-accessory";
internal static readonly string ModeFetchValue = "fetch";
internal static readonly string ModeLocationValue = "location";
internal static readonly string ModeNewsstandValue = "newsstand-content";
internal static readonly string ModePushValue = "remote-notification";
internal static readonly string ModeVOIPValue = "voip";
}
internal class HealthInfo
{
internal static readonly string Key = "UIRequiredDeviceCapabilities";
internal static readonly string Value = "healthkit";
}
internal class ICloudEntitlements
{
internal static readonly string ContainerIdKey = "com.apple.developer.icloud-container-identifiers";
internal static readonly string UbiquityContainerIdKey = "com.apple.developer.ubiquity-container-identifiers";
internal static readonly string ContainerIdValue = "iCloud.$(CFBundleIdentifier)";
internal static readonly string UbiquityContainerIdValue = "iCloud.$(CFBundleIdentifier)";
internal static readonly string ServicesKey = "com.apple.developer.icloud-services";
internal static readonly string ServicesDocValue = "CloudDocuments";
internal static readonly string ServicesKitValue = "CloudKit";
internal static readonly string KeyValueStoreKey = "com.apple.developer.ubiquity-kvstore-identifier";
internal static readonly string KeyValueStoreValue = "$(TeamIdentifierPrefix)$(CFBundleIdentifier)";
}
internal class PushNotificationEntitlements
{
internal static readonly string Key = "aps-environment";
internal static readonly string DevelopmentValue = "development";
internal static readonly string ProductionValue = "production";
}
internal class WalletEntitlements
{
internal static readonly string Key = "com.apple.developer.pass-type-identifiers";
internal static readonly string BaseValue = "$(TeamIdentifierPrefix)";
internal static readonly string DefaultValue = "*";
}
internal class SiriEntitlements
{
internal static readonly string Key = "com.apple.developer.siri";
}
internal class ApplePayEntitlements
{
internal static readonly string Key = "com.apple.developer.in-app-payments";
}
internal class VPNEntitlements
{
internal static readonly string Key = "com.apple.developer.networking.vpn.api";
internal static readonly string Value = "allow-vpn";
}
internal class KeyChainEntitlements
{
internal static readonly string Key = "keychain-access-groups";
internal static readonly string DefaultValue = "$(AppIdentifierPrefix)$(CFBundleIdentifier)";
}
internal class AudioEntitlements
{
internal static readonly string Key = "inter-app-audio";
}
internal class AssociatedDomainsEntitlements
{
// value is an array of string of domains
internal static readonly string Key = "com.apple.developer.associated-domains";
}
internal class AppGroupsEntitlements
{
// value is an array of string of groups
internal static readonly string Key = "com.apple.security.application-groups";
}
internal class HomeKitEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.developer.homekit";
}
internal class DataProtectionEntitlements
{
internal static readonly string Key = "com.apple.developer.default-data-protection";
internal static readonly string Value = "NSFileProtectionComplete";
}
internal class HealthKitEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.developer.healthkit";
}
internal class WirelessAccessoryConfigurationEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.external-accessory.wireless-configuration";
}
}