712 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			712 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C#
		
	
	
//
 | 
						|
//  MaxIntegrationManager.cs
 | 
						|
//  AppLovin MAX Unity Plugin
 | 
						|
//
 | 
						|
//  Created by Santosh Bagadi on 8/29/19.
 | 
						|
//  Copyright © 2019 AppLovin. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
#if UNITY_IOS || UNITY_IPHONE
 | 
						|
 | 
						|
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.IO;
 | 
						|
using System.Linq;
 | 
						|
using System.Text;
 | 
						|
using UnityEditor;
 | 
						|
using UnityEditor.Callbacks;
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
using UnityEditor.iOS.Xcode.Extensions;
 | 
						|
#endif
 | 
						|
using UnityEditor.iOS.Xcode;
 | 
						|
using UnityEngine;
 | 
						|
using Debug = UnityEngine.Debug;
 | 
						|
using UnityEngine.Networking;
 | 
						|
 | 
						|
namespace AppLovinMax.Scripts.IntegrationManager.Editor
 | 
						|
{
 | 
						|
    [Serializable]
 | 
						|
    public class SkAdNetworkData
 | 
						|
    {
 | 
						|
        [SerializeField] public string[] SkAdNetworkIds;
 | 
						|
    }
 | 
						|
 | 
						|
    public class AppLovinPostProcessiOS
 | 
						|
    {
 | 
						|
        private const string OutputFileName = "AppLovinQualityServiceSetup.rb";
 | 
						|
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
        private const string TargetUnityIphonePodfileLine = "target 'Unity-iPhone' do";
 | 
						|
        private const string UseFrameworksPodfileLine = "use_frameworks!";
 | 
						|
        private const string UseFrameworksDynamicPodfileLine = "use_frameworks! :linkage => :dynamic";
 | 
						|
        private const string UseFrameworksStaticPodfileLine = "use_frameworks! :linkage => :static";
 | 
						|
#else
 | 
						|
        private const string UnityMainTargetName = "Unity-iPhone";
 | 
						|
#endif
 | 
						|
        private const string LegacyResourcesDirectoryName = "Resources";
 | 
						|
        private const string AppLovinMaxResourcesDirectoryName = "AppLovinMAXResources";
 | 
						|
        private const string AppLovinAdvertisingAttributionEndpoint = "https://postbacks-app.com";
 | 
						|
 | 
						|
        private const string AppLovinSettingsPlistFileName = "AppLovin-Settings.plist";
 | 
						|
        private const string KeyConsentFlowInfo = "ConsentFlowInfo";
 | 
						|
        private const string KeyConsentFlowEnabled = "ConsentFlowEnabled";
 | 
						|
        private const string KeyConsentFlowTermsOfService = "ConsentFlowTermsOfService";
 | 
						|
        private const string KeyConsentFlowPrivacyPolicy = "ConsentFlowPrivacyPolicy";
 | 
						|
        private const string KeyConsentFlowDebugUserGeography = "ConsentFlowDebugUserGeography";
 | 
						|
 | 
						|
        private static List<string> DynamicLibrariesToEmbed
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                var dynamicLibrariesToEmbed = new List<string>
 | 
						|
                {
 | 
						|
                    "AppLovinSDK.xcframework",
 | 
						|
                    "DTBiOSSDK.xcframework",
 | 
						|
                    "FBAEMKit.xcframework",
 | 
						|
                    "FBSDKCoreKit_Basics.xcframework",
 | 
						|
                    "FBSDKCoreKit.xcframework",
 | 
						|
                    "FBSDKGamingServicesKit.xcframework",
 | 
						|
                    "FBSDKLoginKit.xcframework",
 | 
						|
                    "FBSDKShareKit.xcframework",
 | 
						|
                    "HyprMX.xcframework",
 | 
						|
                    "Maio.xcframework",
 | 
						|
                    "MobileFuseSDK.xcframework",
 | 
						|
                    "MolocoSDK.xcframework",
 | 
						|
                    "OMSDK_Appodeal.xcframework",
 | 
						|
                    "OMSDK_Ogury.xcframework",
 | 
						|
                    "OMSDK_Pubnativenet.xcframework",
 | 
						|
                    "OMSDK_Smaato.xcframework"
 | 
						|
                };
 | 
						|
 | 
						|
                // LinkedIn Audience Network SDK is distributed as a static library starting version 1.2.0
 | 
						|
                if (AppLovinIntegrationManager.IsAdapterOlderThanMinVersionInstalled("LinkedIn", "1.2.0.0"))
 | 
						|
                {
 | 
						|
                    dynamicLibrariesToEmbed.Add("LinkedinAudienceNetwork.xcframework");
 | 
						|
                }
 | 
						|
 | 
						|
                // Fyber/IA SDK is distributed as a static library starting version 8.2.7
 | 
						|
                if (AppLovinIntegrationManager.IsAdapterOlderThanMinVersionInstalled("Fyber", "8.2.7.0"))
 | 
						|
                {
 | 
						|
                    dynamicLibrariesToEmbed.Add("IASDKCore.xcframework");
 | 
						|
                }
 | 
						|
 | 
						|
                return dynamicLibrariesToEmbed;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static string PluginMediationDirectory
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                var pluginParentDir = AppLovinIntegrationManager.MediationSpecificPluginParentDirectory;
 | 
						|
                return Path.Combine(pluginParentDir, "MaxSdk/Mediation/");
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Adds AppLovin Quality Service to the iOS project once the project has been exported.
 | 
						|
        ///
 | 
						|
        /// 1. Downloads the Quality Service ruby script.
 | 
						|
        /// 2. Runs the script using Ruby which integrates AppLovin Quality Service to the project.
 | 
						|
        /// </summary>
 | 
						|
        [PostProcessBuild(int.MaxValue)] // We want to run Quality Service script last.
 | 
						|
        public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
 | 
						|
        {
 | 
						|
            if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
 | 
						|
 | 
						|
            var sdkKey = AppLovinSettings.Instance.SdkKey;
 | 
						|
            if (string.IsNullOrEmpty(sdkKey))
 | 
						|
            {
 | 
						|
                MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var outputFilePath = Path.Combine(buildPath, OutputFileName);
 | 
						|
 | 
						|
            // Check if Quality Service is already installed.
 | 
						|
            if (File.Exists(outputFilePath) && Directory.Exists(Path.Combine(buildPath, "AppLovinQualityService")))
 | 
						|
            {
 | 
						|
                // TODO: Check if there is a way to validate if the SDK key matches the script. Else the pub can't use append when/if they change the SDK Key.
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Download the ruby script needed to install Quality Service
 | 
						|
            var downloadHandler = new DownloadHandlerFile(outputFilePath);
 | 
						|
            var postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey);
 | 
						|
            var bodyRaw = Encoding.UTF8.GetBytes(postJson);
 | 
						|
            var uploadHandler = new UploadHandlerRaw(bodyRaw);
 | 
						|
            uploadHandler.contentType = "application/json";
 | 
						|
 | 
						|
            using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/ios_setup2"))
 | 
						|
            {
 | 
						|
                unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
 | 
						|
                unityWebRequest.downloadHandler = downloadHandler;
 | 
						|
                unityWebRequest.uploadHandler = uploadHandler;
 | 
						|
                var operation = unityWebRequest.SendWebRequest();
 | 
						|
 | 
						|
                // Wait for the download to complete or the request to timeout.
 | 
						|
                while (!operation.isDone) { }
 | 
						|
 | 
						|
#if UNITY_2020_1_OR_NEWER
 | 
						|
                if (unityWebRequest.result != UnityWebRequest.Result.Success)
 | 
						|
#else
 | 
						|
                if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
 | 
						|
#endif
 | 
						|
                {
 | 
						|
                    MaxSdkLogger.UserError("AppLovin Quality Service installation failed. Failed to download script with error: " + unityWebRequest.error);
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                // Check if Ruby is installed
 | 
						|
                var rubyVersion = AppLovinCommandLine.Run("ruby", "--version", buildPath);
 | 
						|
                if (rubyVersion.ExitCode != 0)
 | 
						|
                {
 | 
						|
                    MaxSdkLogger.UserError("AppLovin Quality Service installation requires Ruby. Please install Ruby, export it to your system PATH and re-export the project.");
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                // Ruby is installed, run `ruby AppLovinQualityServiceSetup.rb`
 | 
						|
                var result = AppLovinCommandLine.Run("ruby", OutputFileName, buildPath);
 | 
						|
 | 
						|
                // Check if we have an error.
 | 
						|
                if (result.ExitCode != 0) MaxSdkLogger.UserError("Failed to set up AppLovin Quality Service");
 | 
						|
 | 
						|
                MaxSdkLogger.UserDebug(result.Message);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        [PostProcessBuildAttribute(int.MaxValue)]
 | 
						|
        public static void MaxPostProcessPbxProject(BuildTarget buildTarget, string buildPath)
 | 
						|
        {
 | 
						|
            var projectPath = PBXProject.GetPBXProjectPath(buildPath);
 | 
						|
            var project = new PBXProject();
 | 
						|
            project.ReadFromFile(projectPath);
 | 
						|
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
            var unityMainTargetGuid = project.GetUnityMainTargetGuid();
 | 
						|
            var unityFrameworkTargetGuid = project.GetUnityFrameworkTargetGuid();
 | 
						|
#else
 | 
						|
            var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
 | 
						|
            var unityFrameworkTargetGuid = project.TargetGuidByName(UnityMainTargetName);
 | 
						|
#endif
 | 
						|
            EmbedDynamicLibrariesIfNeeded(buildPath, project, unityMainTargetGuid);
 | 
						|
 | 
						|
            var internalSettingsEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
 | 
						|
            var userTrackingUsageDescriptionDe = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe : AppLovinSettings.Instance.UserTrackingUsageDescriptionDe;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionEn = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn : AppLovinSettings.Instance.UserTrackingUsageDescriptionEn;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionEn, "en", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionEs = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs : AppLovinSettings.Instance.UserTrackingUsageDescriptionEs;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionEs, "es", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionFr = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr : AppLovinSettings.Instance.UserTrackingUsageDescriptionFr;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionFr, "fr", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionJa = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa : AppLovinSettings.Instance.UserTrackingUsageDescriptionJa;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionJa, "ja", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionKo = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo : AppLovinSettings.Instance.UserTrackingUsageDescriptionKo;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionKo, "ko", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionZhHans = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans : AppLovinSettings.Instance.UserTrackingUsageDescriptionZhHans;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionZhHans, "zh-Hans", buildPath, project, unityMainTargetGuid);
 | 
						|
            var userTrackingUsageDescriptionZhHant = internalSettingsEnabled ? AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant : AppLovinSettings.Instance.UserTrackingUsageDescriptionZhHant;
 | 
						|
            LocalizeUserTrackingDescriptionIfNeeded(userTrackingUsageDescriptionZhHant, "zh-Hant", buildPath, project, unityMainTargetGuid);
 | 
						|
 | 
						|
            AddSwiftSupport(buildPath, project, unityFrameworkTargetGuid, unityMainTargetGuid);
 | 
						|
            AddYandexSettingsIfNeeded(project, unityMainTargetGuid);
 | 
						|
 | 
						|
            project.WriteToFile(projectPath);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void EmbedDynamicLibrariesIfNeeded(string buildPath, PBXProject project, string targetGuid)
 | 
						|
        {
 | 
						|
            // Check that the Pods directory exists (it might not if a publisher is building with Generate Podfile setting disabled in EDM).
 | 
						|
            var podsDirectory = Path.Combine(buildPath, "Pods");
 | 
						|
            if (!Directory.Exists(podsDirectory)) return;
 | 
						|
 | 
						|
            var dynamicLibraryPathsPresentInProject = new List<string>();
 | 
						|
            foreach (var dynamicLibraryToSearch in DynamicLibrariesToEmbed)
 | 
						|
            {
 | 
						|
                // both .framework and .xcframework are directories, not files
 | 
						|
                var directories = Directory.GetDirectories(podsDirectory, dynamicLibraryToSearch, SearchOption.AllDirectories);
 | 
						|
                if (directories.Length <= 0) continue;
 | 
						|
 | 
						|
                var dynamicLibraryAbsolutePath = directories[0];
 | 
						|
                var index = dynamicLibraryAbsolutePath.LastIndexOf("Pods");
 | 
						|
                var relativePath = dynamicLibraryAbsolutePath.Substring(index);
 | 
						|
                dynamicLibraryPathsPresentInProject.Add(relativePath);
 | 
						|
            }
 | 
						|
 | 
						|
            if (dynamicLibraryPathsPresentInProject.Count <= 0) return;
 | 
						|
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
            if (ShouldEmbedDynamicLibraries(buildPath))
 | 
						|
            {
 | 
						|
                foreach (var dynamicLibraryPath in dynamicLibraryPathsPresentInProject)
 | 
						|
                {
 | 
						|
                    var fileGuid = project.AddFile(dynamicLibraryPath, dynamicLibraryPath);
 | 
						|
                    project.AddFileToEmbedFrameworks(targetGuid, fileGuid);
 | 
						|
                }
 | 
						|
            }
 | 
						|
#else
 | 
						|
            string runpathSearchPaths;
 | 
						|
            runpathSearchPaths = project.GetBuildPropertyForAnyConfig(targetGuid, "LD_RUNPATH_SEARCH_PATHS");
 | 
						|
            runpathSearchPaths += string.IsNullOrEmpty(runpathSearchPaths) ? "" : " ";
 | 
						|
 | 
						|
            // Check if runtime search paths already contains the required search paths for dynamic libraries.
 | 
						|
            if (runpathSearchPaths.Contains("@executable_path/Frameworks")) return;
 | 
						|
 | 
						|
            runpathSearchPaths += "@executable_path/Frameworks";
 | 
						|
            project.SetBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", runpathSearchPaths);
 | 
						|
#endif
 | 
						|
        }
 | 
						|
 | 
						|
        private static void LocalizeUserTrackingDescriptionIfNeeded(string localizedUserTrackingDescription, string localeCode, string buildPath, PBXProject project, string targetGuid)
 | 
						|
        {
 | 
						|
            // Use the legacy resources directory name if the build is being appended (the "Resources" directory already exists if it is an incremental build).
 | 
						|
            var resourcesDirectoryName = Directory.Exists(Path.Combine(buildPath, LegacyResourcesDirectoryName)) ? LegacyResourcesDirectoryName : AppLovinMaxResourcesDirectoryName;
 | 
						|
            var resourcesDirectoryPath = Path.Combine(buildPath, resourcesDirectoryName);
 | 
						|
            var localeSpecificDirectoryName = localeCode + ".lproj";
 | 
						|
            var localeSpecificDirectoryPath = Path.Combine(resourcesDirectoryPath, localeSpecificDirectoryName);
 | 
						|
            var infoPlistStringsFilePath = Path.Combine(localeSpecificDirectoryPath, "InfoPlist.strings");
 | 
						|
 | 
						|
            // Check if localization has been disabled between builds, and remove them as needed.
 | 
						|
            if (ShouldRemoveLocalization(localizedUserTrackingDescription))
 | 
						|
            {
 | 
						|
                if (!File.Exists(infoPlistStringsFilePath)) return;
 | 
						|
 | 
						|
                File.Delete(infoPlistStringsFilePath);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Create intermediate directories as needed.
 | 
						|
            if (!Directory.Exists(resourcesDirectoryPath))
 | 
						|
            {
 | 
						|
                Directory.CreateDirectory(resourcesDirectoryPath);
 | 
						|
            }
 | 
						|
 | 
						|
            if (!Directory.Exists(localeSpecificDirectoryPath))
 | 
						|
            {
 | 
						|
                Directory.CreateDirectory(localeSpecificDirectoryPath);
 | 
						|
            }
 | 
						|
 | 
						|
            var localizedDescriptionLine = "\"NSUserTrackingUsageDescription\" = \"" + localizedUserTrackingDescription + "\";\n";
 | 
						|
            // File already exists, update it in case the value changed between builds.
 | 
						|
            if (File.Exists(infoPlistStringsFilePath))
 | 
						|
            {
 | 
						|
                var output = new List<string>();
 | 
						|
                var lines = File.ReadAllLines(infoPlistStringsFilePath);
 | 
						|
                var keyUpdated = false;
 | 
						|
                foreach (var line in lines)
 | 
						|
                {
 | 
						|
                    if (line.Contains("NSUserTrackingUsageDescription"))
 | 
						|
                    {
 | 
						|
                        output.Add(localizedDescriptionLine);
 | 
						|
                        keyUpdated = true;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        output.Add(line);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (!keyUpdated)
 | 
						|
                {
 | 
						|
                    output.Add(localizedDescriptionLine);
 | 
						|
                }
 | 
						|
 | 
						|
                File.WriteAllText(infoPlistStringsFilePath, string.Join("\n", output.ToArray()) + "\n");
 | 
						|
            }
 | 
						|
            // File doesn't exist, create one.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                File.WriteAllText(infoPlistStringsFilePath, "/* Localized versions of Info.plist keys - Generated by AL MAX plugin */\n" + localizedDescriptionLine);
 | 
						|
            }
 | 
						|
 | 
						|
            var localeSpecificDirectoryRelativePath = Path.Combine(resourcesDirectoryName, localeSpecificDirectoryName);
 | 
						|
            var guid = project.AddFolderReference(localeSpecificDirectoryRelativePath, localeSpecificDirectoryRelativePath);
 | 
						|
            project.AddFileToBuild(targetGuid, guid);
 | 
						|
        }
 | 
						|
 | 
						|
        private static bool ShouldRemoveLocalization(string localizedUserTrackingDescription)
 | 
						|
        {
 | 
						|
            if (string.IsNullOrEmpty(localizedUserTrackingDescription)) return true;
 | 
						|
 | 
						|
            var settings = AppLovinSettings.Instance;
 | 
						|
            var internalSettings = AppLovinInternalSettings.Instance;
 | 
						|
 | 
						|
            return (!internalSettings.ConsentFlowEnabled || !internalSettings.UserTrackingUsageLocalizationEnabled)
 | 
						|
                   && (!settings.ConsentFlowEnabled || !settings.UserTrackingUsageLocalizationEnabled);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void AddSwiftSupport(string buildPath, PBXProject project, string unityFrameworkTargetGuid, string unityMainTargetGuid)
 | 
						|
        {
 | 
						|
            var swiftFileRelativePath = "Classes/MAXSwiftSupport.swift";
 | 
						|
            var swiftFilePath = Path.Combine(buildPath, swiftFileRelativePath);
 | 
						|
 | 
						|
            // Add Swift file
 | 
						|
            CreateSwiftFile(swiftFilePath);
 | 
						|
            var swiftFileGuid = project.AddFile(swiftFileRelativePath, swiftFileRelativePath, PBXSourceTree.Source);
 | 
						|
            project.AddFileToBuild(unityFrameworkTargetGuid, swiftFileGuid);
 | 
						|
 | 
						|
            // Add Swift version property if needed
 | 
						|
            var swiftVersion = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "SWIFT_VERSION");
 | 
						|
            if (string.IsNullOrEmpty(swiftVersion))
 | 
						|
            {
 | 
						|
                project.SetBuildProperty(unityFrameworkTargetGuid, "SWIFT_VERSION", "5.0");
 | 
						|
            }
 | 
						|
 | 
						|
            // Enable Swift modules
 | 
						|
            project.AddBuildProperty(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES", "YES");
 | 
						|
            project.AddBuildProperty(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
 | 
						|
        }
 | 
						|
 | 
						|
        private static void CreateSwiftFile(string swiftFilePath)
 | 
						|
        {
 | 
						|
            if (File.Exists(swiftFilePath)) return;
 | 
						|
 | 
						|
            // Create a file to write to.
 | 
						|
            using (var writer = File.CreateText(swiftFilePath))
 | 
						|
            {
 | 
						|
                writer.WriteLine("//\n//  MAXSwiftSupport.swift\n//");
 | 
						|
                writer.WriteLine("\nimport Foundation\n");
 | 
						|
                writer.WriteLine("// This file ensures the project includes Swift support.");
 | 
						|
                writer.WriteLine("// It is automatically generated by the MAX Unity Plugin.");
 | 
						|
                writer.Close();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        [PostProcessBuildAttribute(int.MaxValue)]
 | 
						|
        public static void MaxPostProcessPlist(BuildTarget buildTarget, string path)
 | 
						|
        {
 | 
						|
            var plistPath = Path.Combine(path, "Info.plist");
 | 
						|
            var plist = new PlistDocument();
 | 
						|
            plist.ReadFromFile(plistPath);
 | 
						|
 | 
						|
            SetSdkKeyIfNeeded(plist);
 | 
						|
            SetAttributionReportEndpointIfNeeded(plist);
 | 
						|
 | 
						|
            EnableVerboseLoggingIfNeeded(plist);
 | 
						|
            AddGoogleApplicationIdIfNeeded(plist);
 | 
						|
 | 
						|
            AddSdkSettingsIfNeeded(plist, path);
 | 
						|
            EnableTermsFlowIfNeeded(plist);
 | 
						|
            AddSkAdNetworksInfoIfNeeded(plist);
 | 
						|
 | 
						|
            plist.WriteToFile(plistPath);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void SetSdkKeyIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            var sdkKey = AppLovinSettings.Instance.SdkKey;
 | 
						|
            if (string.IsNullOrEmpty(sdkKey)) return;
 | 
						|
 | 
						|
            const string AppLovinVerboseLoggingOnKey = "AppLovinSdkKey";
 | 
						|
            plist.root.SetString(AppLovinVerboseLoggingOnKey, sdkKey);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void SetAttributionReportEndpointIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            if (AppLovinSettings.Instance.SetAttributionReportEndpoint)
 | 
						|
            {
 | 
						|
                plist.root.SetString("NSAdvertisingAttributionReportEndpoint", AppLovinAdvertisingAttributionEndpoint);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                PlistElement attributionReportEndPoint;
 | 
						|
                plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
 | 
						|
 | 
						|
                // Check if we had previously set the attribution endpoint and un-set it.
 | 
						|
                if (attributionReportEndPoint != null && AppLovinAdvertisingAttributionEndpoint.Equals(attributionReportEndPoint.AsString()))
 | 
						|
                {
 | 
						|
                    plist.root.values.Remove("NSAdvertisingAttributionReportEndpoint");
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static void EnableVerboseLoggingIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            if (!EditorPrefs.HasKey(MaxSdkLogger.KeyVerboseLoggingEnabled)) return;
 | 
						|
 | 
						|
            var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled);
 | 
						|
            const string AppLovinVerboseLoggingOnKey = "AppLovinVerboseLoggingOn";
 | 
						|
            if (enabled)
 | 
						|
            {
 | 
						|
                plist.root.SetBoolean(AppLovinVerboseLoggingOnKey, enabled);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                plist.root.values.Remove(AppLovinVerboseLoggingOnKey);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static void AddGoogleApplicationIdIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            if (!AppLovinIntegrationManager.IsAdapterInstalled("Google") && !AppLovinIntegrationManager.IsAdapterInstalled("GoogleAdManager")) return;
 | 
						|
 | 
						|
            const string googleApplicationIdentifier = "GADApplicationIdentifier";
 | 
						|
            var appId = AppLovinSettings.Instance.AdMobIosAppId;
 | 
						|
            // Log error if the App ID is not set.
 | 
						|
            if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
 | 
						|
            {
 | 
						|
                Debug.LogError("[AppLovin MAX] Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            plist.root.SetString(googleApplicationIdentifier, appId);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void AddYandexSettingsIfNeeded(PBXProject project, string unityMainTargetGuid)
 | 
						|
        {
 | 
						|
            if (!AppLovinIntegrationManager.IsAdapterInstalled("Yandex")) return;
 | 
						|
 | 
						|
            if (MaxSdkUtils.CompareVersions(PlayerSettings.iOS.targetOSVersionString, "12.0") == MaxSdkUtils.VersionComparisonResult.Lesser)
 | 
						|
            {
 | 
						|
                Debug.LogWarning("Your iOS target version is under the minimum required version by Yandex. Please update it to 12.0 or newer in your ProjectSettings and rebuild your project.");
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            project.SetBuildProperty(unityMainTargetGuid, "GENERATE_INFOPLIST_FILE", "NO");
 | 
						|
        }
 | 
						|
 | 
						|
        private static void AddSdkSettingsIfNeeded(PlistDocument infoPlist, string buildPath)
 | 
						|
        {
 | 
						|
            // Right now internal settings is only needed for Consent Flow. Remove this setting once we add more settings.
 | 
						|
            if (!AppLovinInternalSettings.Instance.ConsentFlowEnabled) return;
 | 
						|
 | 
						|
            var sdkSettingsPlistPath = Path.Combine(buildPath, AppLovinSettingsPlistFileName);
 | 
						|
            var sdkSettingsPlist = new PlistDocument();
 | 
						|
            if (File.Exists(sdkSettingsPlistPath))
 | 
						|
            {
 | 
						|
                sdkSettingsPlist.ReadFromFile(sdkSettingsPlistPath);
 | 
						|
            }
 | 
						|
 | 
						|
            EnableConsentFlowIfNeeded(sdkSettingsPlist, infoPlist);
 | 
						|
 | 
						|
            sdkSettingsPlist.WriteToFile(sdkSettingsPlistPath);
 | 
						|
 | 
						|
            var projectPath = PBXProject.GetPBXProjectPath(buildPath);
 | 
						|
            var project = new PBXProject();
 | 
						|
            project.ReadFromFile(projectPath);
 | 
						|
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
            var unityMainTargetGuid = project.GetUnityMainTargetGuid();
 | 
						|
#else
 | 
						|
            var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
 | 
						|
#endif
 | 
						|
 | 
						|
            var guid = project.AddFile(AppLovinSettingsPlistFileName, AppLovinSettingsPlistFileName, PBXSourceTree.Source);
 | 
						|
            project.AddFileToBuild(unityMainTargetGuid, guid);
 | 
						|
            project.WriteToFile(projectPath);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void EnableConsentFlowIfNeeded(PlistDocument applovinSettingsPlist, PlistDocument infoPlist)
 | 
						|
        {
 | 
						|
            var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
 | 
						|
            var userTrackingUsageDescription = AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn;
 | 
						|
            var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
 | 
						|
            if (string.IsNullOrEmpty(userTrackingUsageDescription) || string.IsNullOrEmpty(privacyPolicyUrl))
 | 
						|
            {
 | 
						|
                AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL and the `User Tracking Usage Description` in the AppLovin Integration Manager. \n\n" +
 | 
						|
                                                                  "Both values must be included to enable the SDK's consent flow.");
 | 
						|
 | 
						|
                // No need to update the info.plist here. Default consent flow state will be determined on the SDK side.
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var consentFlowInfoRoot = applovinSettingsPlist.root.CreateDict(KeyConsentFlowInfo);
 | 
						|
            consentFlowInfoRoot.SetBoolean(KeyConsentFlowEnabled, consentFlowEnabled);
 | 
						|
            consentFlowInfoRoot.SetString(KeyConsentFlowPrivacyPolicy, privacyPolicyUrl);
 | 
						|
 | 
						|
            var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
 | 
						|
            if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
 | 
						|
            {
 | 
						|
                consentFlowInfoRoot.SetString(KeyConsentFlowTermsOfService, termsOfServiceUrl);
 | 
						|
            }
 | 
						|
 | 
						|
            var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
 | 
						|
            if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
 | 
						|
            {
 | 
						|
                consentFlowInfoRoot.SetString(KeyConsentFlowDebugUserGeography, "gdpr");
 | 
						|
            }
 | 
						|
 | 
						|
            infoPlist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void EnableTermsFlowIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            // Check if terms flow is enabled. No need to update info.plist if consent flow is disabled.
 | 
						|
            var consentFlowEnabled = AppLovinSettings.Instance.ConsentFlowEnabled;
 | 
						|
            if (!consentFlowEnabled) return;
 | 
						|
 | 
						|
            // Check if terms flow is enabled for this format.
 | 
						|
            var consentFlowPlatform = AppLovinSettings.Instance.ConsentFlowPlatform;
 | 
						|
            if (consentFlowPlatform != Platform.All && consentFlowPlatform != Platform.iOS) return;
 | 
						|
 | 
						|
            var userTrackingUsageDescription = AppLovinSettings.Instance.UserTrackingUsageDescriptionEn;
 | 
						|
            var privacyPolicyUrl = AppLovinSettings.Instance.ConsentFlowPrivacyPolicyUrl;
 | 
						|
            if (string.IsNullOrEmpty(userTrackingUsageDescription) || string.IsNullOrEmpty(privacyPolicyUrl))
 | 
						|
            {
 | 
						|
                AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL and the `User Tracking Usage Description` in the AppLovin Integration Manager. \n\n" +
 | 
						|
                                                                  "Both values must be included to enable the SDK's consent flow.");
 | 
						|
 | 
						|
                // No need to update the info.plist here. Default consent flow state will be determined on the SDK side.
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var consentFlowInfoRoot = plist.root.CreateDict("AppLovinConsentFlowInfo");
 | 
						|
            consentFlowInfoRoot.SetBoolean("AppLovinConsentFlowEnabled", consentFlowEnabled);
 | 
						|
            consentFlowInfoRoot.SetString("AppLovinConsentFlowPrivacyPolicy", privacyPolicyUrl);
 | 
						|
 | 
						|
            var termsOfServiceUrl = AppLovinSettings.Instance.ConsentFlowTermsOfServiceUrl;
 | 
						|
            if (!string.IsNullOrEmpty(termsOfServiceUrl))
 | 
						|
            {
 | 
						|
                consentFlowInfoRoot.SetString("AppLovinConsentFlowTermsOfService", termsOfServiceUrl);
 | 
						|
            }
 | 
						|
 | 
						|
            plist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void AddSkAdNetworksInfoIfNeeded(PlistDocument plist)
 | 
						|
        {
 | 
						|
            var skAdNetworkData = GetSkAdNetworkData();
 | 
						|
            var skAdNetworkIds = skAdNetworkData.SkAdNetworkIds;
 | 
						|
            // Check if we have a valid list of SKAdNetworkIds that need to be added.
 | 
						|
            if (skAdNetworkIds == null || skAdNetworkIds.Length < 1) return;
 | 
						|
 | 
						|
            //
 | 
						|
            // Add the SKAdNetworkItems to the plist. It should look like following:
 | 
						|
            //
 | 
						|
            //    <key>SKAdNetworkItems</key>
 | 
						|
            //    <array>
 | 
						|
            //        <dict>
 | 
						|
            //            <key>SKAdNetworkIdentifier</key>
 | 
						|
            //            <string>ABC123XYZ.skadnetwork</string>
 | 
						|
            //        </dict>
 | 
						|
            //        <dict>
 | 
						|
            //            <key>SKAdNetworkIdentifier</key>
 | 
						|
            //            <string>123QWE456.skadnetwork</string>
 | 
						|
            //        </dict>
 | 
						|
            //        <dict>
 | 
						|
            //            <key>SKAdNetworkIdentifier</key>
 | 
						|
            //            <string>987XYZ123.skadnetwork</string>
 | 
						|
            //        </dict>
 | 
						|
            //    </array>
 | 
						|
            //
 | 
						|
            PlistElement skAdNetworkItems;
 | 
						|
            plist.root.values.TryGetValue("SKAdNetworkItems", out skAdNetworkItems);
 | 
						|
            var existingSkAdNetworkIds = new HashSet<string>();
 | 
						|
            // Check if SKAdNetworkItems array is already in the Plist document and collect all the IDs that are already present.
 | 
						|
            if (skAdNetworkItems != null && skAdNetworkItems.GetType() == typeof(PlistElementArray))
 | 
						|
            {
 | 
						|
                var plistElementDictionaries = skAdNetworkItems.AsArray().values.Where(plistElement => plistElement.GetType() == typeof(PlistElementDict));
 | 
						|
                foreach (var plistElement in plistElementDictionaries)
 | 
						|
                {
 | 
						|
                    PlistElement existingId;
 | 
						|
                    plistElement.AsDict().values.TryGetValue("SKAdNetworkIdentifier", out existingId);
 | 
						|
                    if (existingId == null || existingId.GetType() != typeof(PlistElementString) || string.IsNullOrEmpty(existingId.AsString())) continue;
 | 
						|
 | 
						|
                    existingSkAdNetworkIds.Add(existingId.AsString());
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Else, create an array of SKAdNetworkItems into which we will add our IDs.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                skAdNetworkItems = plist.root.CreateArray("SKAdNetworkItems");
 | 
						|
            }
 | 
						|
 | 
						|
            foreach (var skAdNetworkId in skAdNetworkIds)
 | 
						|
            {
 | 
						|
                // Skip adding IDs that are already in the array.
 | 
						|
                if (existingSkAdNetworkIds.Contains(skAdNetworkId)) continue;
 | 
						|
 | 
						|
                var skAdNetworkItemDict = skAdNetworkItems.AsArray().AddDict();
 | 
						|
                skAdNetworkItemDict.SetString("SKAdNetworkIdentifier", skAdNetworkId);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static SkAdNetworkData GetSkAdNetworkData()
 | 
						|
        {
 | 
						|
            var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
 | 
						|
 | 
						|
            // Get the list of installed ad networks to be passed up
 | 
						|
            var maxMediationDirectory = PluginMediationDirectory;
 | 
						|
            if (Directory.Exists(maxMediationDirectory))
 | 
						|
            {
 | 
						|
                var mediationNetworkDirectories = Directory.GetDirectories(maxMediationDirectory);
 | 
						|
                var installedNetworks = mediationNetworkDirectories.Select(Path.GetFileName).ToList();
 | 
						|
                if (AppLovinSettings.Instance.AddApsSkAdNetworkIds)
 | 
						|
                {
 | 
						|
                    installedNetworks.Add("AmazonPublisherServices");
 | 
						|
                }
 | 
						|
 | 
						|
                var adNetworks = string.Join(",", installedNetworks.ToArray());
 | 
						|
                if (!string.IsNullOrEmpty(adNetworks))
 | 
						|
                {
 | 
						|
                    uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            using (var unityWebRequest = UnityWebRequest.Get(uriBuilder.ToString()))
 | 
						|
            {
 | 
						|
                var operation = unityWebRequest.SendWebRequest();
 | 
						|
                // Wait for the download to complete or the request to timeout.
 | 
						|
                while (!operation.isDone) { }
 | 
						|
 | 
						|
#if UNITY_2020_1_OR_NEWER
 | 
						|
                if (unityWebRequest.result != UnityWebRequest.Result.Success)
 | 
						|
#else
 | 
						|
                if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
 | 
						|
#endif
 | 
						|
                {
 | 
						|
                    MaxSdkLogger.UserError("Failed to retrieve SKAdNetwork IDs with error: " + unityWebRequest.error);
 | 
						|
                    return new SkAdNetworkData();
 | 
						|
                }
 | 
						|
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    return JsonUtility.FromJson<SkAdNetworkData>(unityWebRequest.downloadHandler.text);
 | 
						|
                }
 | 
						|
                catch (Exception exception)
 | 
						|
                {
 | 
						|
                    MaxSdkLogger.UserError("Failed to parse data '" + unityWebRequest.downloadHandler.text + "' with exception: " + exception);
 | 
						|
                    return new SkAdNetworkData();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
#if UNITY_2019_3_OR_NEWER
 | 
						|
        /// <summary>
 | 
						|
        /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
						|
        /// |         embed             |  use_frameworks! (:linkage => :dynamic)  |  use_frameworks! :linkage => :static  |  `use_frameworks!` line not present  |
 | 
						|
        /// |---------------------------|------------------------------------------|---------------------------------------|--------------------------------------|
 | 
						|
        /// | Unity-iPhone present      | Do not embed dynamic libraries           | Embed dynamic libraries               | Do not embed dynamic libraries       |
 | 
						|
        /// | Unity-iPhone not present  | Embed dynamic libraries                  | Embed dynamic libraries               | Embed dynamic libraries              |
 | 
						|
        /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="buildPath">An iOS build path</param>
 | 
						|
        /// <returns>Whether or not the dynamic libraries should be embedded.</returns>
 | 
						|
        private static bool ShouldEmbedDynamicLibraries(string buildPath)
 | 
						|
        {
 | 
						|
            var podfilePath = Path.Combine(buildPath, "Podfile");
 | 
						|
            if (!File.Exists(podfilePath)) return false;
 | 
						|
 | 
						|
            // If the Podfile doesn't have a `Unity-iPhone` target, we should embed the dynamic libraries.
 | 
						|
            var lines = File.ReadAllLines(podfilePath);
 | 
						|
            var containsUnityIphoneTarget = lines.Any(line => line.Contains(TargetUnityIphonePodfileLine));
 | 
						|
            if (!containsUnityIphoneTarget) return true;
 | 
						|
 | 
						|
            // If the Podfile does not have a `use_frameworks! :linkage => static` line, we should not embed the dynamic libraries.
 | 
						|
            var useFrameworksStaticLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksStaticPodfileLine));
 | 
						|
            if (useFrameworksStaticLineIndex == -1) return false;
 | 
						|
 | 
						|
            // If more than one of the `use_frameworks!` lines are present, CocoaPods will use the last one.
 | 
						|
            var useFrameworksLineIndex = Array.FindIndex(lines, line => line.Trim() == UseFrameworksPodfileLine); // Check for exact line to avoid matching `use_frameworks! :linkage => static/dynamic`
 | 
						|
            var useFrameworksDynamicLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksDynamicPodfileLine));
 | 
						|
 | 
						|
            // Check if `use_frameworks! :linkage => :static` is the last line of the three. If it is, we should embed the dynamic libraries.
 | 
						|
            return useFrameworksLineIndex < useFrameworksStaticLineIndex && useFrameworksDynamicLineIndex < useFrameworksStaticLineIndex;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |