增加sdk

This commit is contained in:
2026-07-14 10:20:45 +08:00
parent 20d09e4ebb
commit 57709d109d
1165 changed files with 59941 additions and 4485 deletions
@@ -16,7 +16,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// Handles auto updates for AppLovin MAX plugin.
/// </summary>
public class AppLovinAutoUpdater
public static class AppLovinAutoUpdater
{
public const string KeyAutoUpdateEnabled = "com.applovin.auto_update_enabled";
private const string KeyLastUpdateCheckTime = "com.applovin.last_update_check_time_v2"; // Updated to v2 to force adapter version checks in plugin version 3.1.10.
@@ -97,7 +97,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (option == 0) // Download
{
MaxSdkLogger.UserDebug("Downloading plugin...");
AppLovinIntegrationManager.downloadPluginProgressCallback = AppLovinIntegrationManagerWindow.OnDownloadPluginProgress;
AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.DownloadPlugin(data.AppLovinMax));
}
else if (option == 1) // Not Now
@@ -18,7 +18,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
///
/// TODO: Currently only supports shell (Linux). Add support for Windows machines.
/// </summary>
public class AppLovinCommandLine
public static class AppLovinCommandLine
{
/// <summary>
/// Result obtained by running a command line command.
@@ -1,75 +0,0 @@
//
// AppLovinDownloadHandler.cs
// AppLovin MAX Unity Plugin
//
// Created by Santosh Bagadi on 7/26/19.
// Copyright © 2019 AppLovin. All rights reserved.
//
#if !UNITY_2017_2_OR_NEWER
using System;
using System.IO;
using UnityEngine.Networking;
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
public class AppLovinDownloadHandler : DownloadHandlerScript
{
// Required by DownloadHandler base class. Called when you address the 'bytes' property.
protected override byte[] GetData()
{
return null;
}
private FileStream fileStream;
public AppLovinDownloadHandler(string path) : base(new byte[2048])
{
var downloadDirectory = Path.GetDirectoryName(path);
if (!Directory.Exists(downloadDirectory))
{
Directory.CreateDirectory(downloadDirectory);
}
try
{
//Open the current file to write to
fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
}
catch (Exception exception)
{
MaxSdkLogger.UserError(string.Format("Failed to create file at {0}\n{1}", path, exception.Message));
}
}
protected override bool ReceiveData(byte[] byteFromServer, int dataLength)
{
if (byteFromServer == null || byteFromServer.Length < 1 || fileStream == null)
{
return false;
}
try
{
//Write the current data to the file
fileStream.Write(byteFromServer, 0, dataLength);
}
catch (Exception exception)
{
fileStream.Close();
fileStream = null;
MaxSdkLogger.UserError(string.Format("Failed to download file{0}", exception.Message));
}
return true;
}
protected override void CompleteContent()
{
fileStream.Close();
}
}
}
#endif
@@ -1,14 +0,0 @@
fileFormatVersion: 2
guid: c85c3812a13e04838951767c1285e4da
labels:
- al_max
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinDownloadHandler.cs
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -6,10 +6,8 @@
// Copyright © 2019 AppLovin. All rights reserved.
//
using AppLovinMax.Scripts.IntegrationManager.Editor;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
namespace AppLovinMax.Scripts.IntegrationManager.Editor
@@ -21,6 +19,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
"AdColony",
"Criteo",
"LinkedIn",
"Nend",
"Snap",
"Tapjoy",
@@ -74,11 +73,22 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
"MaxSdk/Version.md",
"MaxSdk/Version.md.meta",
// TODO: Add MaxTargetingData and MaxUserSegment when the plugin has enough traction.
// The alert_icon.png has been renamed to error_icon.png.
"MaxSdk/Resources/Images/alert_icon.png",
"MaxSdk/Resources/Images/alert_icon.png.meta",
// `TargetingData` has been removed and we no longer set `UserSegment` through the Unity Plugin.
"MaxSdk/Scripts/MaxUserSegment.cs",
"MaxSdk/Scripts/MaxUserSegment.cs.meta",
"MaxSdk/Scripts/MaxTargetingData.cs",
"MaxSdk/Scripts/MaxTargetingData.cs.meta"
};
static AppLovinInitialize()
{
// Don't run obsolete file cleanup logic when entering play mode.
if (EditorApplication.isPlayingOrWillChangePlaymode) return;
#if UNITY_IOS
// Check that the publisher is targeting iOS 9.0+
if (!PlayerSettings.iOS.targetOSVersionString.StartsWith("9.") && !PlayerSettings.iOS.targetOSVersionString.StartsWith("1"))
@@ -8,9 +8,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AppLovinMax.Internal;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
@@ -20,10 +19,12 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
[Serializable]
public class PluginData
{
// ReSharper disable InconsistentNaming - Consistent with JSON data.
public Network AppLovinMax;
public Network[] MediatedNetworks;
public Network[] PartnerMicroSdks;
public DynamicLibraryToEmbed[] ThirdPartyDynamicLibrariesToEmbed;
public Alert[] Alerts;
}
[Serializable]
@@ -46,6 +47,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// }
//
// ReSharper disable InconsistentNaming - Consistent with JSON data.
public string Name;
public string DisplayName;
public string DownloadUrl;
@@ -53,15 +55,18 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public PackageInfo[] Packages;
public string[] PluginFilePaths;
public Versions LatestVersions;
public DynamicLibraryToEmbed[] DynamicLibrariesToEmbed;
[NonSerialized] public Versions CurrentVersions;
[NonSerialized] public MaxSdkUtils.VersionComparisonResult CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Lesser;
[NonSerialized] public bool RequiresUpdate;
public DynamicLibraryToEmbed[] DynamicLibrariesToEmbed;
[NonSerialized] public bool IsCurrentlyInstalling;
}
[Serializable]
public class DynamicLibraryToEmbed
{
// ReSharper disable InconsistentNaming - Consistent with JSON data.
public string PodName;
public string[] FrameworkNames;
@@ -78,12 +83,62 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
public enum Severity
{
Info,
Warning,
Error
}
[Serializable]
public class Alert
{
public string SeverityType;
public string Title;
public string Message;
public string Url;
public string MinimumPluginVersion;
public string MaximumPluginVersion;
public string MinimumUnityVersion;
public string MaximumUnityVersion;
public Severity Severity;
public void InitializeSeverityEnum()
{
switch (SeverityType)
{
case "INFO":
Severity = Severity.Info;
break;
case "WARNING":
Severity = Severity.Warning;
break;
case "ERROR":
Severity = Severity.Error;
break;
default:
MaxSdkLogger.E(string.Format("Alert <{0}> has unsupported severity type <{1}>.", Title, SeverityType));
Severity = Severity.Info;
break;
}
}
public bool ShouldShowAlert()
{
var pluginVersionValid = MaxSdkUtils.IsVersionInRange(MaxSdk.Version, MinimumPluginVersion, MaximumPluginVersion);
var unityVersionValid = MaxSdkUtils.IsVersionInRange(Application.unityVersion, MinimumUnityVersion, MaximumUnityVersion);
return pluginVersionValid && unityVersionValid;
}
}
/// <summary>
/// A helper data class used to get current versions from Dependency.xml files.
/// </summary>
[Serializable]
public class Versions
{
// ReSharper disable InconsistentNaming - Consistent with JSON data.
public string Unity;
public string Android;
public string Ios;
@@ -107,12 +162,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public override int GetHashCode()
{
return new {Unity, Android, Ios}.GetHashCode();
return new {unity = Unity, android = Android, ios = Ios}.GetHashCode();
}
private static string AdapterSdkVersion(string adapterVersion)
{
var index = adapterVersion.LastIndexOf(".");
if (string.IsNullOrEmpty(adapterVersion)) return "";
var index = adapterVersion.LastIndexOf(".", StringComparison.Ordinal);
return index > 0 ? adapterVersion.Substring(0, index) : adapterVersion;
}
}
@@ -123,32 +180,30 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public class AppLovinIntegrationManager
{
/// <summary>
/// Delegate to be called when downloading a plugin with the progress percentage.
/// Delegate to be called when a plugin package's import is started.
/// </summary>
/// <param name="pluginName">The name of the plugin being downloaded.</param>
/// <param name="progress">Percentage downloaded.</param>
/// <param name="done">Whether or not the download is complete.</param>
public delegate void DownloadPluginProgressCallback(string pluginName, float progress, bool done);
internal delegate void ImportPackageStartedCallback(Network network);
/// <summary>
/// Delegate to be called when a plugin package is imported.
/// Delegate to be called when a plugin package is finished importing.
/// </summary>
/// <param name="network">The network data for which the package is imported.</param>
public delegate void ImportPackageCompletedCallback(Network network);
internal delegate void ImportPackageCompletedCallback(Network network);
private static readonly AppLovinIntegrationManager instance = new AppLovinIntegrationManager();
internal static readonly string GradleTemplatePath = Path.Combine("Assets/Plugins/Android", "mainTemplate.gradle");
private const string MaxSdkAssetExportPath = "MaxSdk/Scripts/MaxSdk.cs";
private const string MaxSdkMediationExportPath = "MaxSdk/Mediation";
private static readonly string PluginDataEndpoint = "https://unity.applovin.com/max/1.0/integration_manager_info?plugin_version={0}";
private const string PluginDataEndpoint = "https://unity.applovin.com/max/1.0/integration_manager_info?plugin_version={0}";
private static string externalDependencyManagerVersion;
public static DownloadPluginProgressCallback downloadPluginProgressCallback;
public static ImportPackageCompletedCallback importPackageCompletedCallback;
internal static ImportPackageStartedCallback OnImportPackageStartedCallback;
internal static ImportPackageCompletedCallback OnImportPackageCompletedCallback;
private UnityWebRequest webRequest;
private MaxWebRequest maxWebRequest;
private Network importingNetwork;
/// <summary>
@@ -166,8 +221,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
get
{
// Search for the asset with the default exported path first, In most cases, we should be able to find the asset.
// In some cases where we don't, use the platform specific export path to search for the asset (in case of migrating a project from Windows to Mac or vice versa).
// Search for the asset with the export path label.
// Paths are normalized using AltDirectorySeparatorChar (/) to ensure compatibility across platforms (in case of migrating a project from Windows to Mac or vice versa).
var maxSdkScriptAssetPath = MaxSdkUtils.GetAssetPathForExportPath(MaxSdkAssetExportPath);
// maxSdkScriptAssetPath will always have AltDirectorySeparatorChar (/) as the path separator. Convert to platform specific path.
@@ -176,6 +231,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
public static string MediationDirectory
{
get
{
var mediationAssetPath = MaxSdkUtils.GetAssetPathForExportPath(MaxSdkMediationExportPath);
return mediationAssetPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
}
/// <summary>
/// Whether or not the plugin is in the Unity Package Manager.
/// </summary>
@@ -215,7 +279,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
get
{
if (!string.IsNullOrEmpty(externalDependencyManagerVersion)) return externalDependencyManagerVersion;
if (MaxSdkUtils.IsValidString(externalDependencyManagerVersion)) return externalDependencyManagerVersion;
try
{
@@ -235,6 +299,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private AppLovinIntegrationManager()
{
AssetDatabase.importPackageStarted += packageName =>
{
if (!IsImportingNetwork(packageName)) return;
CallImportPackageStartedCallback(importingNetwork);
};
// Add asset import callbacks.
AssetDatabase.importPackageCompleted += packageName =>
{
@@ -250,7 +321,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
if (!IsImportingNetwork(packageName)) return;
MaxSdkLogger.UserDebug("Package import cancelled.");
importingNetwork = null;
};
@@ -268,15 +338,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public static PluginData LoadPluginDataSync()
{
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
using (var unityWebRequest = UnityWebRequest.Get(url))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
EndPoint = url,
};
// Just wait till www is done
while (!operation.isDone) { }
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
return CreatePluginDataFromWebResponse(unityWebRequest);
}
return CreatePluginDataFromWebResponse(webResponse);
}
/// <summary>
@@ -286,34 +356,31 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public IEnumerator LoadPluginData(Action<PluginData> callback)
{
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
using (var unityWebRequest = UnityWebRequest.Get(url))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
while (!operation.isDone) yield return new WaitForSeconds(0.1f); // Just wait till www is done. Our coroutine is pretty rudimentary.
var pluginData = CreatePluginDataFromWebResponse(unityWebRequest);
EndPoint = url,
};
maxWebRequest = new MaxWebRequest(webRequestConfig);
yield return maxWebRequest.Send(webResponse =>
{
var pluginData = CreatePluginDataFromWebResponse(webResponse);
callback(pluginData);
}
});
}
private static PluginData CreatePluginDataFromWebResponse(UnityWebRequest unityWebRequest)
private static PluginData CreatePluginDataFromWebResponse(WebResponse webResponse)
{
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
if (!webResponse.IsSuccess)
{
MaxSdkLogger.E("Failed to load plugin data. Please check your internet connection.");
MaxSdkLogger.UserError("Failed to load plugin data. Please check your internet connection.");
return null;
}
PluginData pluginData;
try
{
pluginData = JsonUtility.FromJson<PluginData>(unityWebRequest.downloadHandler.text);
pluginData = JsonUtility.FromJson<PluginData>(webResponse.ResponseMessage);
AppLovinPackageManager.PluginData = pluginData;
}
catch (Exception exception)
@@ -334,9 +401,20 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
AppLovinPackageManager.UpdateCurrentVersions(network);
}
foreach (var partnerMicroSdk in pluginData.PartnerMicroSdks)
if (pluginData.PartnerMicroSdks != null)
{
AppLovinPackageManager.UpdateCurrentVersions(partnerMicroSdk);
foreach (var partnerMicroSdk in pluginData.PartnerMicroSdks)
{
AppLovinPackageManager.UpdateCurrentVersions(partnerMicroSdk);
}
}
if (pluginData.Alerts == null) return pluginData;
// Initiate Severity enums from the raw strings in the response
foreach (var alert in pluginData.Alerts)
{
alert.InitializeSeverityEnum();
}
return pluginData;
@@ -351,36 +429,25 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public IEnumerator DownloadPlugin(Network network, bool showImport = true)
{
var path = Path.Combine(Application.temporaryCachePath, GetPluginFileName(network)); // TODO: Maybe delete plugin file after finishing import.
var downloadHandler = new DownloadHandlerFile(path);
webRequest = new UnityWebRequest(network.DownloadUrl)
var webRequestConfig = new WebRequestConfig()
{
method = UnityWebRequest.kHttpVerbGET,
downloadHandler = downloadHandler
DownloadHandler = new DownloadHandlerFile(path),
EndPoint = network.DownloadUrl
};
var operation = webRequest.SendWebRequest();
while (!operation.isDone)
maxWebRequest = new MaxWebRequest(webRequestConfig);
yield return maxWebRequest.Send(webResponse =>
{
yield return new WaitForSeconds(0.1f); // Just wait till webRequest is completed. Our coroutine is pretty rudimentary.
CallDownloadPluginProgressCallback(network.DisplayName, operation.progress, operation.isDone);
}
#if UNITY_2020_1_OR_NEWER
if (webRequest.result != UnityWebRequest.Result.Success)
#else
if (webRequest.isNetworkError || webRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError(webRequest.error);
}
else
{
importingNetwork = network;
AssetDatabase.ImportPackage(path, showImport);
}
webRequest.Dispose();
webRequest = null;
if (webResponse.IsSuccess)
{
importingNetwork = network;
AssetDatabase.ImportPackage(path, showImport);
}
else
{
MaxSdkLogger.UserError("Failed to download plugin package: " + webResponse.ErrorMessage);
}
});
}
/// <summary>
@@ -388,9 +455,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public void CancelDownload()
{
if (webRequest == null) return;
if (maxWebRequest == null) return;
webRequest.Abort();
maxWebRequest.Abort();
}
/// <summary>
@@ -421,18 +488,18 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return importingNetwork != null && GetPluginFileName(importingNetwork).Contains(packageName);
}
private static void CallDownloadPluginProgressCallback(string pluginName, float progress, bool isDone)
private static void CallImportPackageStartedCallback(Network network)
{
if (downloadPluginProgressCallback == null) return;
if (OnImportPackageStartedCallback == null) return;
downloadPluginProgressCallback(pluginName, progress, isDone);
OnImportPackageStartedCallback(network);
}
private static void CallImportPackageCompletedCallback(Network network)
{
if (importPackageCompletedCallback == null) return;
if (OnImportPackageCompletedCallback == null) return;
importPackageCompletedCallback(network);
OnImportPackageCompletedCallback(network);
}
private static object GetEditorUserBuildSetting(string name, object defaultValue)
@@ -8,6 +8,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
@@ -17,57 +18,58 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
public class AppLovinIntegrationManagerWindow : EditorWindow
{
private const string windowTitle = "AppLovin Integration Manager";
private const string WindowTitle = "AppLovin Integration Manager";
private const string appLovinSdkKeyLink = "https://dash.applovin.com/o/account#keys";
private const string AppLovinSdkKeyLink = "https://dash.applovin.com/o/account#keys";
private const string userTrackingUsageDescriptionDocsLink = "https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription";
private const string documentationTermsAndPrivacyPolicyFlow = "https://developers.applovin.com/en/unity/overview/terms-and-privacy-policy-flow";
private const string documentationAdaptersLink = "https://developers.applovin.com/en/unity/preparing-mediated-networks";
private const string documentationNote = "Please ensure that integration instructions (e.g. permissions, ATS settings, etc) specific to each network are implemented as well. Click the link below for more info:";
private const string uninstallIconExportPath = "MaxSdk/Resources/Images/uninstall_icon.png";
private const string alertIconExportPath = "MaxSdk/Resources/Images/alert_icon.png";
private const string warningIconExportPath = "MaxSdk/Resources/Images/warning_icon.png";
private const string UserTrackingUsageDescriptionDocsLink = "https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription";
private const string DocumentationTermsAndPrivacyPolicyFlow = "https://support.axon.ai/en/max/unity/overview/terms-and-privacy-policy-flow";
private const string DocumentationAdaptersLink = "https://support.axon.ai/en/max/unity/preparing-mediated-networks";
private const string DocumentationNote = "Please ensure that integration instructions (e.g. permissions, ATS settings, etc) specific to each network are implemented as well. Click the link below for more info:";
private const string UninstallIconExportPath = "MaxSdk/Resources/Images/uninstall_icon.png";
private const string InfoIconExportPath = "MaxSdk/Resources/Images/info_icon.png";
private const string WarningIconExportPath = "MaxSdk/Resources/Images/warning_icon.png";
private const string ErrorIconExportPath = "MaxSdk/Resources/Images/error_icon.png";
private const string qualityServiceRequiresGradleBuildErrorMsg = "AppLovin Quality Service integration via AppLovin Integration Manager requires Custom Gradle Template enabled or Unity 2018.2 or higher.\n" +
private const string QualityServiceRequiresGradleBuildErrorMsg = "AppLovin Quality Service integration via AppLovin Integration Manager requires Custom Gradle Template enabled or Unity 2018.2 or higher.\n" +
"If you would like to continue using your existing setup, please add Quality Service Plugin to your build.gradle manually.";
private const string customGradleVersionTooltip = "To set the version to 6.9.3, set the field to: https://services.gradle.org/distributions/gradle-6.9.3-bin.zip";
private const string customGradleToolsVersionTooltip = "To set the version to 4.2.0, set the field to: 4.2.0";
private const string CustomGradleVersionTooltip = "To set the version to 6.9.3, set the field to: https://services.gradle.org/distributions/gradle-6.9.3-bin.zip";
private const string CustomGradleToolsVersionTooltip = "To set the version to 4.2.0, set the field to: 4.2.0";
private const string keyShowMicroSdkPartners = "com.applovin.show_micro_sdk_partners";
private const string keyShowMediatedNetworks = "com.applovin.show_mediated_networks";
private const string keyShowSdkSettings = "com.applovin.show_sdk_settings";
private const string keyShowPrivacySettings = "com.applovin.show_privacy_settings";
private const string keyShowOtherSettings = "com.applovin.show_other_settings";
private const string KeyShowAlerts = "com.applovin.show_alerts";
private const string KeyShowMicroSdkPartners = "com.applovin.show_micro_sdk_partners";
private const string KeyShowMediatedNetworks = "com.applovin.show_mediated_networks";
private const string KeyShowSdkSettings = "com.applovin.show_sdk_settings";
private const string KeyShowPrivacySettings = "com.applovin.show_privacy_settings";
private const string KeyShowOtherSettings = "com.applovin.show_other_settings";
private const string expandButtonText = "+";
private const string collapseButtonText = "-";
private const string ExpandButtonText = "+";
private const string CollapseButtonText = "-";
private const string externalDependencyManagerPath = "Assets/ExternalDependencyManager";
private const string ExternalDependencyManagerPath = "Assets/ExternalDependencyManager";
private readonly string[] termsFlowPlatforms = new string[3] {"Both", "Android", "iOS"};
private readonly string[] debugUserGeographies = new string[2] {"Not Set", "GDPR"};
private Vector2 scrollPosition;
private static readonly Vector2 windowMinSize = new Vector2(750, 750);
private const float actionFieldWidth = 60f;
private const float upgradeAllButtonWidth = 80f;
private const float networkFieldMinWidth = 100f;
private const float versionFieldMinWidth = 190f;
private const float privacySettingLabelWidth = 250f;
private const float networkFieldWidthPercentage = 0.22f;
private const float versionFieldWidthPercentage = 0.36f; // There are two version fields. Each take 40% of the width, network field takes the remaining 20%.
private static float previousWindowWidth = windowMinSize.x;
private static GUILayoutOption networkWidthOption = GUILayout.Width(networkFieldMinWidth);
private static GUILayoutOption versionWidthOption = GUILayout.Width(versionFieldMinWidth);
private static readonly Vector2 WindowMinSize = new Vector2(750, 750);
private const float ActionFieldWidth = 70f;
private const float UpgradeAllButtonWidth = 80f;
private const float NetworkFieldMinWidth = 100f;
private const float VersionFieldMinWidth = 190f;
private const float PrivacySettingLabelWidth = 250f;
private const float NetworkFieldWidthPercentage = 0.22f;
private const float VersionFieldWidthPercentage = 0.36f; // There are two version fields. Each take 40% of the width, network field takes the remaining 20%.
private static float previousWindowWidth = WindowMinSize.x;
private static GUILayoutOption networkWidthOption = GUILayout.Width(NetworkFieldMinWidth);
private static GUILayoutOption versionWidthOption = GUILayout.Width(VersionFieldMinWidth);
private static GUILayoutOption privacySettingFieldWidthOption = GUILayout.Width(400);
private static readonly GUILayoutOption fieldWidth = GUILayout.Width(actionFieldWidth);
private static readonly GUILayoutOption upgradeAllButtonFieldWidth = GUILayout.Width(upgradeAllButtonWidth);
private static readonly GUILayoutOption collapseButtonWidthOption = GUILayout.Width(20f);
private static readonly GUILayoutOption FieldWidth = GUILayout.Width(ActionFieldWidth);
private static readonly GUILayoutOption UpgradeAllButtonFieldWidth = GUILayout.Width(UpgradeAllButtonWidth);
private static readonly GUILayoutOption CollapseButtonWidthOption = GUILayout.Width(20f);
private static readonly Color darkModeTextColor = new Color(0.29f, 0.6f, 0.8f);
private static readonly Color DarkModeTextColor = new Color(0.29f, 0.6f, 0.8f);
private GUIStyle titleLabelStyle;
private GUIStyle headerLabelStyle;
@@ -83,13 +85,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private AppLovinEditorCoroutine loadDataCoroutine;
private Texture2D uninstallIcon;
private Texture2D alertIcon;
private Texture2D infoIcon;
private Texture2D warningIcon;
private Texture2D errorIcon;
public static void ShowManager()
{
var manager = GetWindow<AppLovinIntegrationManagerWindow>(utility: true, title: windowTitle, focus: true);
manager.minSize = windowMinSize;
var manager = GetWindow<AppLovinIntegrationManagerWindow>(utility: true, title: WindowTitle, focus: true);
manager.minSize = WindowMinSize;
}
#region Editor Window Lifecyle Methods
@@ -118,7 +121,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
linkLabelStyle = new GUIStyle(EditorStyles.label)
{
wordWrap = true,
normal = {textColor = EditorGUIUtility.isProSkin ? darkModeTextColor : Color.blue}
normal = {textColor = EditorGUIUtility.isProSkin ? DarkModeTextColor : Color.blue}
};
wrapTextLabelStyle = new GUIStyle(EditorStyles.label)
@@ -134,19 +137,27 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
};
// Load uninstall icon texture.
var uninstallIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(uninstallIconExportPath));
uninstallIcon = new Texture2D(0, 0, TextureFormat.RGBA32, false); // 1. Initial size doesn't matter here, will be automatically resized once the image asset is loaded. 2. Set mipChain to false, else the texture has a weird blurry effect.
var uninstallIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(UninstallIconExportPath));
// 1. Set the initial size to 1, as Unity 6000 no longer supports a width or height of 0.
// 2. The image will be automatically resized once the image asset is loaded.
// 3. Set mipChain to false, else the texture has a weird blurry effect.
uninstallIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
uninstallIcon.LoadImage(uninstallIconData);
// Load alert icon texture.
var alertIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(alertIconExportPath));
alertIcon = new Texture2D(0, 0, TextureFormat.RGBA32, false);
alertIcon.LoadImage(alertIconData);
// Load info icon texture.
var infoIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(InfoIconExportPath));
infoIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
infoIcon.LoadImage(infoIconData);
// Load warning icon texture.
var warningIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(warningIconExportPath));
warningIcon = new Texture2D(0, 0, TextureFormat.RGBA32, false);
var warningIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(WarningIconExportPath));
warningIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
warningIcon.LoadImage(warningIconData);
// Load error icon texture.
var errorIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(ErrorIconExportPath));
errorIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
errorIcon.LoadImage(errorIconData);
}
private void OnEnable()
@@ -171,10 +182,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private void OnWindowEnabled()
{
AppLovinIntegrationManager.downloadPluginProgressCallback = OnDownloadPluginProgress;
// Plugin downloaded and imported. Update current versions for the imported package.
AppLovinIntegrationManager.importPackageCompletedCallback = OnImportPackageCompleted;
AppLovinIntegrationManager.OnImportPackageStartedCallback = OnImportPackageStarted;
AppLovinIntegrationManager.OnImportPackageCompletedCallback = OnImportPackageCompleted;
Load();
}
@@ -211,17 +221,36 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// Draw AppLovin MAX plugin details
EditorGUILayout.LabelField("AppLovin MAX Plugin Details", titleLabelStyle);
DrawPluginDetails();
if (pluginData != null && pluginData.PartnerMicroSdks != null)
// Draw alerts
if (pluginData != null && pluginData.Alerts != null)
{
DrawCollapsableSection(keyShowMicroSdkPartners, "AppLovin Micro SDK Partners", DrawPartnerMicroSdks);
var alertsToShow = pluginData.Alerts.Where(alert => alert.ShouldShowAlert()).ToList();
if (alertsToShow.Count > 0)
{
EditorGUILayout.BeginHorizontal();
var showAlertDetails = DrawExpandCollapseButton(KeyShowAlerts);
EditorGUILayout.LabelField("Alerts", titleLabelStyle, GUILayout.Width(45));
DrawAlertCount(alertsToShow);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if (showAlertDetails)
{
DrawAlerts(alertsToShow);
}
}
}
// Draw Micro SDK Partners
if (pluginData != null && !MaxSdkUtils.IsNullOrEmpty(pluginData.PartnerMicroSdks))
{
DrawCollapsibleSection(KeyShowMicroSdkPartners, "AppLovin Micro SDK Partners", DrawPartnerMicroSdks);
}
// Draw mediated networks);
EditorGUILayout.BeginHorizontal();
var showDetails = DrawExpandCollapseButton(keyShowMediatedNetworks);
var showDetails = DrawExpandCollapseButton(KeyShowMediatedNetworks);
EditorGUILayout.LabelField("Mediated Networks", titleLabelStyle);
GUILayout.FlexibleSpace();
DrawUpgradeAllButton();
@@ -231,30 +260,28 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
DrawMediatedNetworks();
}
#if UNITY_2019_2_OR_NEWER
if (!AppLovinIntegrationManager.IsPluginInPackageManager)
{
EditorGUILayout.LabelField("Unity Package Manager Migration", titleLabelStyle);
DrawPluginMigrationHelper();
}
#endif
// Draw AppLovin Quality Service settings
DrawCollapsableSection(keyShowSdkSettings, "SDK Settings", DrawQualityServiceSettings);
DrawCollapsibleSection(KeyShowSdkSettings, "SDK Settings", DrawQualityServiceSettings);
DrawCollapsableSection(keyShowPrivacySettings, "Privacy Settings", DrawPrivacySettings);
DrawCollapsibleSection(KeyShowPrivacySettings, "Privacy Settings", DrawPrivacySettings);
DrawCollapsableSection(keyShowOtherSettings, "Other Settings", DrawOtherSettings);
DrawCollapsibleSection(KeyShowOtherSettings, "Other Settings", DrawOtherSettings);
// Draw Unity environment details
EditorGUILayout.LabelField("Unity Environment Details", titleLabelStyle);
DrawUnityEnvironmentDetails();
// Draw documentation notes
EditorGUILayout.LabelField(new GUIContent(documentationNote), wrapTextLabelStyle);
if (GUILayout.Button(new GUIContent(documentationAdaptersLink), linkLabelStyle))
EditorGUILayout.LabelField(new GUIContent(DocumentationNote), wrapTextLabelStyle);
if (GUILayout.Button(new GUIContent(DocumentationAdaptersLink), linkLabelStyle))
{
Application.OpenURL(documentationAdaptersLink);
Application.OpenURL(DocumentationAdaptersLink);
}
}
@@ -283,7 +310,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.BeginHorizontal();
GUILayout.Space(5);
EditorGUILayout.LabelField("Failed to load plugin data. Please click retry or restart the integration manager.", titleLabelStyle);
if (GUILayout.Button("Retry", fieldWidth))
if (GUILayout.Button("Retry", FieldWidth))
{
pluginDataLoadFailed = false;
Load();
@@ -338,9 +365,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.enabled = upgradeButtonEnabled;
if (GUILayout.Button(new GUIContent("Upgrade"), fieldWidth))
var action = appLovinMax.IsCurrentlyInstalling ? "Installing..." : "Upgrade";
GUI.enabled = upgradeButtonEnabled && !appLovinMax.IsCurrentlyInstalling;
if (GUILayout.Button(new GUIContent(action), FieldWidth))
{
// Only show "Installing..." if the plugin is in the Assets folder
// Manifest edits don't trigger import callbacks, and UPM resolution locks the UI anyway.
appLovinMax.IsCurrentlyInstalling = !AppLovinIntegrationManager.IsPluginInPackageManager;
AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(pluginData.AppLovinMax, true));
}
@@ -356,6 +388,84 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.EndHorizontal();
}
/// <summary>
/// Draw the number of each alert type next to the alert section header.
/// </summary>
private void DrawAlertCount(List<Alert> alerts)
{
if (pluginData == null) return;
var infoAlertsCount = alerts.Count(alert => alert.Severity == Severity.Info);
var warningAlertsCount = alerts.Count(alert => alert.Severity == Severity.Warning);
var errorAlertsCount = alerts.Count(alert => alert.Severity == Severity.Error);
GUILayout.Label(infoIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(infoAlertsCount), GUILayout.Width(20));
GUILayout.Label(warningIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(warningAlertsCount), GUILayout.Width(20));
GUILayout.Label(errorIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(errorAlertsCount), GUILayout.Width(20));
}
/// <summary>
/// Draw the list of alerts grouped by severity.
/// </summary>
private void DrawAlerts(List<Alert> alerts)
{
GUILayout.BeginHorizontal();
GUILayout.Space(10);
using (new EditorGUILayout.VerticalScope("box"))
{
DrawAlertsOfType(alerts, Severity.Error);
DrawAlertsOfType(alerts, Severity.Warning);
DrawAlertsOfType(alerts, Severity.Info);
}
GUILayout.Space(5);
GUILayout.EndHorizontal();
}
private void DrawAlertsOfType(List<Alert> alerts, Severity severity)
{
var alertsOfType = alerts.Where(alert => alert.Severity == severity).ToList();
foreach (var alert in alertsOfType)
{
DrawAlert(alert);
}
}
/// <summary>
/// Draw a single alert.
/// </summary>
private void DrawAlert(Alert alert)
{
using (new EditorGUILayout.HorizontalScope())
{
using (new EditorGUILayout.VerticalScope(GUILayout.Width(20)))
{
GUILayout.Space(2);
GUILayout.Label(GetSeverityIcon(alert.Severity), GUILayout.Width(20), GUILayout.Height(20));
}
using (new EditorGUILayout.VerticalScope())
{
GUILayout.Label(alert.Title, headerLabelStyle);
EditorGUILayout.LabelField(alert.Message, wrapTextLabelStyle);
if (MaxSdkUtils.IsValidString(alert.Url))
{
if (GUILayout.Button(new GUIContent(alert.Url), linkLabelStyle))
{
Application.OpenURL(alert.Url);
}
}
GUILayout.Space(2);
}
}
GUILayout.Space(10);
}
/// <summary>
/// Draws the headers for a table.
/// </summary>
@@ -372,7 +482,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (drawAction)
{
GUILayout.FlexibleSpace();
GUILayout.Button("Actions", headerLabelStyle, fieldWidth);
GUILayout.Button("Actions", headerLabelStyle, FieldWidth);
GUILayout.Space(5);
}
}
@@ -508,16 +618,24 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (network.RequiresUpdate)
{
GUILayout.Label(new GUIContent {image = alertIcon, tooltip = "Adapter not compatible, please update to the latest version."}, iconStyle);
GUILayout.Label(new GUIContent {image = errorIcon, tooltip = "Adapter not compatible, please update to the latest version."}, iconStyle);
}
else if ((network.Name.Equals("ADMOB_NETWORK") || network.Name.Equals("GOOGLE_AD_MANAGER_NETWORK")) && shouldShowGoogleWarning)
{
GUILayout.Label(new GUIContent {image = warningIcon, tooltip = "You may see unexpected errors if you use different versions of the AdMob and Google Ad Manager adapter SDKs."}, iconStyle);
}
GUI.enabled = networkButtonsEnabled && isActionEnabled;
if (GUILayout.Button(new GUIContent(action), fieldWidth))
if (network.IsCurrentlyInstalling)
{
action = "Installing...";
}
GUI.enabled = networkButtonsEnabled && isActionEnabled && !network.IsCurrentlyInstalling;
if (GUILayout.Button(new GUIContent(action), FieldWidth))
{
// Only show "Installing..." if the plugin is in the Assets folder
// Manifest edits don't trigger import callbacks, and UPM resolution locks the UI anyway.
network.IsCurrentlyInstalling = !AppLovinIntegrationManager.IsPluginInPackageManager;
AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(network, true));
}
@@ -527,9 +645,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUI.enabled = networkButtonsEnabled && isInstalled;
if (GUILayout.Button(new GUIContent {image = uninstallIcon, tooltip = "Uninstall"}, iconStyle))
{
EditorUtility.DisplayProgressBar("Integration Manager", "Deleting " + network.Name + "...", 0.5f);
AppLovinPackageManager.RemoveNetwork(network);
EditorUtility.ClearProgressBar();
AppLovinPackageManager.UpdateCurrentVersions(network);
UpdateShouldShowGoogleWarningIfNeeded();
}
GUI.enabled = true;
@@ -582,7 +700,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private void DrawUpgradeAllButton()
{
GUI.enabled = NetworksRequireUpgrade();
if (GUILayout.Button(new GUIContent("Upgrade All"), upgradeAllButtonFieldWidth))
if (GUILayout.Button(new GUIContent("Upgrade All"), UpgradeAllButtonFieldWidth))
{
AppLovinEditorCoroutine.StartCoroutine(UpgradeAllNetworks());
}
@@ -591,7 +709,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(10);
}
#if UNITY_2019_2_OR_NEWER
private void DrawPluginMigrationHelper()
{
GUILayout.BeginHorizontal();
@@ -618,7 +735,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
"Are you sure you want to migrate the SDK and adapters to UPM? This action will move both the MAX SDK and its adapters.", "Yes", "No"))
{
var deleteExternalDependencyManager = false;
if (Directory.Exists(externalDependencyManagerPath))
if (Directory.Exists(ExternalDependencyManagerPath))
{
deleteExternalDependencyManager = EditorUtility.DisplayDialog("External Dependency Manager Detected",
"Our plugin includes the External Dependency Manager via the Unity Package Manager. Would you like us to automatically remove the existing External Dependency Manager folder, or would you prefer to manage it manually?", "Remove Automatically", "Manage Manually");
@@ -638,7 +755,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(5);
GUILayout.EndHorizontal();
}
#endif
private void DrawQualityServiceSettings()
{
@@ -652,20 +768,20 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(4);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
EditorGUILayout.HelpBox(qualityServiceRequiresGradleBuildErrorMsg, MessageType.Warning);
EditorGUILayout.HelpBox(QualityServiceRequiresGradleBuildErrorMsg, MessageType.Warning);
GUILayout.Space(4);
GUILayout.EndHorizontal();
GUILayout.Space(4);
}
AppLovinSettings.Instance.SdkKey = DrawTextField("AppLovin SDK Key", AppLovinSettings.Instance.SdkKey, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption);
AppLovinSettings.Instance.SdkKey = DrawTextField("AppLovin SDK Key", AppLovinSettings.Instance.SdkKey, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
GUILayout.Button("You can find your SDK key here: ", wrapTextLabelStyle, GUILayout.Width(185)); // Setting a fixed width since Unity adds arbitrary padding at the end leaving a space between link and text.
if (GUILayout.Button(new GUIContent(appLovinSdkKeyLink), linkLabelStyle))
if (GUILayout.Button(new GUIContent(AppLovinSdkKeyLink), linkLabelStyle))
{
Application.OpenURL(appLovinSdkKeyLink);
Application.OpenURL(AppLovinSdkKeyLink);
}
GUILayout.FlexibleSpace();
@@ -750,9 +866,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(4);
if (GUILayout.Button(new GUIContent(documentationTermsAndPrivacyPolicyFlow), linkLabelStyle))
if (GUILayout.Button(new GUIContent(DocumentationTermsAndPrivacyPolicyFlow), linkLabelStyle))
{
Application.OpenURL(documentationTermsAndPrivacyPolicyFlow);
Application.OpenURL(DocumentationTermsAndPrivacyPolicyFlow);
}
GUILayout.Space(4);
@@ -760,8 +876,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(8);
AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl = DrawTextField("Privacy Policy URL", AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl = DrawTextField("Terms of Service URL (optional)", AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl = DrawTextField("Privacy Policy URL", AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl = DrawTextField("Terms of Service URL (optional)", AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
GUILayout.Space(4);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR = GUILayout.Toggle(AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR, " Show Terms and Privacy Policy Flow when in GDPR Regions");
GUILayout.EndHorizontal();
GUILayout.Space(4);
GUILayout.BeginHorizontal();
@@ -778,7 +900,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(4);
GUILayout.Space(4);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn = DrawTextField("User Tracking Usage Description", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn = DrawTextField("User Tracking Usage Description", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
@@ -788,13 +910,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (AppLovinInternalSettings.Instance.UserTrackingUsageLocalizationEnabled)
{
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans = DrawTextField("Chinese, Simplified (zh-Hans)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant = DrawTextField("Chinese, Traditional (zh-Hant)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr = DrawTextField("French (fr)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe = DrawTextField("German (de)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa = DrawTextField("Japanese (ja)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo = DrawTextField("Korean (ko)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs = DrawTextField("Spanish (es)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans = DrawTextField("Chinese, Simplified (zh-Hans)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant = DrawTextField("Chinese, Traditional (zh-Hant)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr = DrawTextField("French (fr)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe = DrawTextField("German (de)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa = DrawTextField("Japanese (ja)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo = DrawTextField("Korean (ko)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs = DrawTextField("Spanish (es)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
GUILayout.Space(4);
GUILayout.BeginHorizontal();
@@ -812,9 +934,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(4);
if (GUILayout.Button(new GUIContent(userTrackingUsageDescriptionDocsLink), linkLabelStyle))
if (GUILayout.Button(new GUIContent(UserTrackingUsageDescriptionDocsLink), linkLabelStyle))
{
Application.OpenURL(userTrackingUsageDescriptionDocsLink);
Application.OpenURL(UserTrackingUsageDescriptionDocsLink);
}
GUILayout.Space(4);
@@ -842,10 +964,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(10);
using (new EditorGUILayout.VerticalScope("box"))
{
GUILayout.Space(5);
AppLovinSettings.Instance.SetAttributionReportEndpoint = DrawOtherSettingsToggle(AppLovinSettings.Instance.SetAttributionReportEndpoint, " Set Advertising Attribution Report Endpoint in Info.plist (iOS only)");
GUILayout.Space(5);
AppLovinSettings.Instance.AddApsSkAdNetworkIds = DrawOtherSettingsToggle(AppLovinSettings.Instance.AddApsSkAdNetworkIds, " Add Amazon Publisher Services SKAdNetworkID's");
GUILayout.Space(5);
var autoUpdateEnabled = DrawOtherSettingsToggle(EditorPrefs.GetBool(AppLovinAutoUpdater.KeyAutoUpdateEnabled, true), " Enable Auto Update", "Checks for AppLovin MAX plugin updates and notifies you when an update is available.");
EditorPrefs.SetBool(AppLovinAutoUpdater.KeyAutoUpdateEnabled, autoUpdateEnabled);
@@ -853,8 +971,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var verboseLoggingEnabled = DrawOtherSettingsToggle(EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, false), " Enable Verbose Logging");
EditorPrefs.SetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, verboseLoggingEnabled);
GUILayout.Space(5);
AppLovinSettings.Instance.CustomGradleVersionUrl = DrawTextField("Custom Gradle Version URL", AppLovinSettings.Instance.CustomGradleVersionUrl, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: customGradleVersionTooltip);
AppLovinSettings.Instance.CustomGradleToolsVersion = DrawTextField("Custom Gradle Tools Version", AppLovinSettings.Instance.CustomGradleToolsVersion, GUILayout.Width(privacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: customGradleToolsVersionTooltip);
AppLovinSettings.Instance.CustomGradleVersionUrl = DrawTextField("Custom Gradle Version URL", AppLovinSettings.Instance.CustomGradleVersionUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: CustomGradleVersionTooltip);
AppLovinSettings.Instance.CustomGradleToolsVersion = DrawTextField("Custom Gradle Tools Version", AppLovinSettings.Instance.CustomGradleToolsVersion, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: CustomGradleToolsVersionTooltip);
EditorGUILayout.HelpBox("This will overwrite the gradle build tools version in your base gradle template.", MessageType.Info);
}
@@ -906,7 +1024,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
private void DrawCollapsableSection(string keyShowDetails, string label, Action drawContent)
private void DrawCollapsibleSection(string keyShowDetails, string label, Action drawContent)
{
EditorGUILayout.BeginHorizontal();
var showDetails = DrawExpandCollapseButton(keyShowDetails);
@@ -923,8 +1041,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private bool DrawExpandCollapseButton(string keyShowDetails)
{
var showDetails = EditorPrefs.GetBool(keyShowDetails, true);
var buttonText = showDetails ? collapseButtonText : expandButtonText;
if (GUILayout.Button(buttonText, collapseButtonWidthOption))
var buttonText = showDetails ? CollapseButtonText : ExpandButtonText;
if (GUILayout.Button(buttonText, CollapseButtonWidthOption))
{
EditorPrefs.SetBool(keyShowDetails, !showDetails);
}
@@ -938,15 +1056,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private void CalculateFieldWidth()
{
var currentWidth = position.width;
var availableWidth = currentWidth - actionFieldWidth - 80; // NOTE: Magic number alert. This is the sum of all the spacing the fields and other UI elements.
var networkLabelWidth = Math.Max(networkFieldMinWidth, availableWidth * networkFieldWidthPercentage);
var availableWidth = currentWidth - ActionFieldWidth - 80; // NOTE: Magic number alert. This is the sum of all the spacing the fields and other UI elements.
var networkLabelWidth = Math.Max(NetworkFieldMinWidth, availableWidth * NetworkFieldWidthPercentage);
networkWidthOption = GUILayout.Width(networkLabelWidth);
var versionLabelWidth = Math.Max(versionFieldMinWidth, availableWidth * versionFieldWidthPercentage);
var versionLabelWidth = Math.Max(VersionFieldMinWidth, availableWidth * VersionFieldWidthPercentage);
versionWidthOption = GUILayout.Width(versionLabelWidth);
const int textFieldOtherUiElementsWidth = 55; // NOTE: Magic number alert. This is the sum of all the spacing the fields and other UI elements.
var availableUserDescriptionTextFieldWidth = currentWidth - privacySettingLabelWidth - textFieldOtherUiElementsWidth;
var availableUserDescriptionTextFieldWidth = currentWidth - PrivacySettingLabelWidth - textFieldOtherUiElementsWidth;
privacySettingFieldWidthOption = GUILayout.Width(availableUserDescriptionTextFieldWidth);
}
@@ -978,25 +1096,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}));
}
/// <summary>
/// Callback method that will be called with progress updates when the plugin is being downloaded.
/// </summary>
public static void OnDownloadPluginProgress(string pluginName, float progress, bool done)
private void OnImportPackageStarted(Network network)
{
// Download is complete. Clear progress bar.
if (done)
{
EditorUtility.ClearProgressBar();
}
// Download is in progress, update progress bar.
else
{
if (EditorUtility.DisplayCancelableProgressBar(windowTitle, string.Format("Downloading {0} plugin...", pluginName), progress))
{
AppLovinIntegrationManager.Instance.CancelDownload();
EditorUtility.ClearProgressBar();
}
}
network.IsCurrentlyInstalling = false;
}
private void OnImportPackageCompleted(Network network)
@@ -1019,7 +1121,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var googleAdManagerNetwork = networks.FirstOrDefault(foundNetwork => foundNetwork.Name.Equals("GOOGLE_AD_MANAGER_NETWORK"));
if (googleNetwork != null && googleAdManagerNetwork != null &&
!string.IsNullOrEmpty(googleNetwork.CurrentVersions.Unity) && !string.IsNullOrEmpty(googleAdManagerNetwork.CurrentVersions.Unity) &&
MaxSdkUtils.IsValidString(googleNetwork.CurrentVersions.Unity) && MaxSdkUtils.IsValidString(googleAdManagerNetwork.CurrentVersions.Unity) &&
!googleNetwork.CurrentVersions.HasEqualSdkVersions(googleAdManagerNetwork.CurrentVersions))
{
shouldShowGoogleWarning = true;
@@ -1042,7 +1144,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
var comparison = network.CurrentToLatestVersionComparisonResult;
// A newer version is available
if (!string.IsNullOrEmpty(network.CurrentVersions.Unity) && comparison == MaxSdkUtils.VersionComparisonResult.Lesser)
if (MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && comparison == MaxSdkUtils.VersionComparisonResult.Lesser)
{
yield return AppLovinPackageManager.AddNetwork(network, false);
}
@@ -1063,7 +1165,33 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (pluginData == null || pluginData.AppLovinMax.CurrentVersions == null) return false;
var networks = pluginData.MediatedNetworks;
return networks.Any(network => !string.IsNullOrEmpty(network.CurrentVersions.Unity) && network.CurrentToLatestVersionComparisonResult == MaxSdkUtils.VersionComparisonResult.Lesser);
return networks.Any(network => MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && network.CurrentToLatestVersionComparisonResult == MaxSdkUtils.VersionComparisonResult.Lesser);
}
/// <summary>
/// Takes in an int representing the count of an alert and returns it as a string or "9+" if greater than 9.
/// </summary>
private string AlertCountToString(int count)
{
return count > 9 ? "9+" : count.ToString();
}
/// <summary>
/// Returns the icon for the given severity type.
/// </summary>
private Texture2D GetSeverityIcon(Severity severity)
{
switch (severity)
{
case Severity.Info:
return infoIcon;
case Severity.Warning:
return warningIcon;
case Severity.Error:
return errorIcon;
default:
return infoIcon;
}
}
#endregion
@@ -1,5 +1,5 @@
//
// AppLovinInternalSettigns.cs
// AppLovinInternalSettings.cs
// AppLovin User Engagement Unity Plugin
//
// Created by Santosh Bagadi on 9/15/22.
@@ -22,18 +22,19 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
private static AppLovinInternalSettings instance;
public const string DefaultUserTrackingDescriptionEn = "This uses device info for more personalized ads and content";
public const string DefaultUserTrackingDescriptionDe = "Dies benutzt Gerätinformationen für relevantere Werbeinhalte";
public const string DefaultUserTrackingDescriptionEs = "Esto utiliza la información del dispositivo para anuncios y contenido más personalizados";
public const string DefaultUserTrackingDescriptionFr = "Cela permet d'utiliser les informations du téléphone pour afficher des contenus publicitaires plus pertinents.";
public const string DefaultUserTrackingDescriptionJa = "これはユーザーデータをもとに、より関連性の高い広告コンテンツをお客様に提供します";
public const string DefaultUserTrackingDescriptionKo = "보다 개인화된 광고 및 콘텐츠를 위해 기기 정보를 사용합니다.";
public const string DefaultUserTrackingDescriptionZhHans = "我们使用设备信息来提供个性化的广告和内容。";
public const string DefaultUserTrackingDescriptionZhHant = "我們使用設備信息來提供個性化的廣告和內容。";
private const string DefaultUserTrackingDescriptionEn = "This uses device info for more personalized ads and content";
private const string DefaultUserTrackingDescriptionDe = "Dies benutzt Gerätinformationen für relevantere Werbeinhalte";
private const string DefaultUserTrackingDescriptionEs = "Esto utiliza la información del dispositivo para anuncios y contenido más personalizados";
private const string DefaultUserTrackingDescriptionFr = "Cela permet d'utiliser les informations du téléphone pour afficher des contenus publicitaires plus pertinents.";
private const string DefaultUserTrackingDescriptionJa = "これはユーザーデータをもとに、より関連性の高い広告コンテンツをお客様に提供します";
private const string DefaultUserTrackingDescriptionKo = "보다 개인화된 광고 및 콘텐츠를 위해 기기 정보를 사용합니다.";
private const string DefaultUserTrackingDescriptionZhHans = "我们使用设备信息来提供个性化的广告和内容。";
private const string DefaultUserTrackingDescriptionZhHant = "我們使用設備信息來提供個性化的廣告和內容。";
[SerializeField] private bool consentFlowEnabled;
[SerializeField] private string consentFlowPrivacyPolicyUrl = string.Empty;
[SerializeField] private string consentFlowTermsOfServiceUrl = string.Empty;
[SerializeField] private bool shouldShowTermsAndPrivacyPolicyAlertInGDPR;
[SerializeField] private bool overrideDefaultUserTrackingUsageDescriptions;
[SerializeField] private MaxSdkBase.ConsentFlowUserGeography debugUserGeography;
[SerializeField] private string userTrackingUsageDescriptionEn = string.Empty;
@@ -140,9 +141,18 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
set { consentFlowTermsOfServiceUrl = value; }
}
/// <summary>
/// Whether or not to show the Terms and Privacy Policy alert in GDPR regions prior to presenting the CMP prompt.
/// </summary>
public bool ShouldShowTermsAndPrivacyPolicyAlertInGDPR
{
get { return shouldShowTermsAndPrivacyPolicyAlertInGDPR; }
set { shouldShowTermsAndPrivacyPolicyAlertInGDPR = value; }
}
/// <summary>
/// A User Tracking Usage Description in English to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see <see href="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// </summary>
public string UserTrackingUsageDescriptionEn
{
@@ -185,7 +195,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// Whether or not to localize User Tracking Usage Description.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public bool UserTrackingUsageLocalizationEnabled
{
@@ -226,7 +236,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in German to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionDe
{
@@ -236,7 +246,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in Spanish to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionEs
{
@@ -246,7 +256,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in French to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionFr
{
@@ -256,7 +266,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in Japanese to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionJa
{
@@ -266,7 +276,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in Korean to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionKo
{
@@ -276,7 +286,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in Chinese (Simplified) to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionZhHans
{
@@ -286,7 +296,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <summary>
/// A User Tracking Usage Description in Chinese (Traditional) to be shown to users when requesting permission to use data for tracking.
/// For more information see <see cref="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
/// </summary>
public string UserTrackingUsageDescriptionZhHant
{
@@ -11,7 +11,7 @@ using UnityEngine;
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
public class AppLovinMenuItems
public static class AppLovinMenuItems
{
/**
* The special characters at the end represent a shortcut for this action.
@@ -31,7 +31,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
[MenuItem("AppLovin/Documentation")]
private static void Documentation()
{
Application.OpenURL("https://developers.applovin.com/en/unity/overview/integration");
Application.OpenURL("https://support.axon.ai/en/max/unity/overview/integration");
}
[MenuItem("AppLovin/Contact Us")]
@@ -23,29 +23,22 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public interface IPackageManagerClient
{
List<string> GetInstalledMediationNetworks();
IEnumerator AddNetwork(Network network, bool showImport);
void RemoveNetwork(Network network);
}
public static class AppLovinPackageManager
{
#if UNITY_2019_2_OR_NEWER
private static readonly IPackageManagerClient _upmPackageManager = new AppLovinUpmPackageManager();
#endif
private static readonly IPackageManagerClient _assetsPackageManager = new AppLovinAssetsPackageManager();
private const string AppLovinMediationAmazonAdapterDependenciesPath = "Amazon/Scripts/Mediations/AppLovinMediation/Editor/Dependencies.xml";
private static bool _migrationPromptShown;
private static readonly IPackageManagerClient _upmPackageManager = new AppLovinUpmPackageManager();
private static readonly IPackageManagerClient _assetsPackageManager = new AppLovinAssetsPackageManager();
private static IPackageManagerClient PackageManagerClient
{
get
{
#if UNITY_2019_2_OR_NEWER
return AppLovinIntegrationManager.IsPluginInPackageManager ? _upmPackageManager : _assetsPackageManager;
#else
return _assetsPackageManager;
#endif
}
}
@@ -107,8 +100,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <returns>A list of the installed mediation network names.</returns>
internal static List<string> GetInstalledMediationNetworks()
{
var installedNetworks = PackageManagerClient.GetInstalledMediationNetworks();
if (AppLovinSettings.Instance.AddApsSkAdNetworkIds)
var installedNetworks = new List<string>();
var installedNetworksInAssets = AppLovinAssetsPackageManager.GetInstalledMediationNetworks();
installedNetworks.AddRange(installedNetworksInAssets);
var installedNetworksInPackages = AppLovinUpmPackageManager.GetInstalledMediationNetworks();
installedNetworks.AddRange(installedNetworksInPackages);
if (IsAmazonAppLovinAdapterInstalled())
{
installedNetworks.Add("AmazonAdMarketplace");
}
@@ -148,7 +147,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <returns>The exported path of the MAX plugin asset or an empty list if the asset is not found.</returns>
private static List<string> GetAssetPathListForExportPath(string exportPath)
{
var assetLabelToFind = "l:al_max_export_path-" + exportPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var assetLabelToFind = "l:al_max_export_path-" + MaxSdkUtils.NormalizeToUnityPath(exportPath);
var assetGuids = AssetDatabase.FindAssets(assetLabelToFind);
var assetPaths = new List<string>();
@@ -167,6 +166,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
internal static void UpdateCurrentVersions(Network network)
{
var assetPaths = GetAssetPathListForExportPath(network.DependenciesFilePath);
if (HasDuplicateAdapters(assetPaths))
{
ShowDeleteDuplicateAdapterPrompt(network);
}
var currentVersions = GetCurrentVersions(assetPaths);
network.CurrentVersions = currentVersions;
@@ -204,12 +208,12 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
// If adapter is indeed installed, compare the current (installed) and the latest (from db) versions, so that we can determine if the publisher is on an older, current or a newer version of the adapter.
// If the publisher is on a newer version of the adapter than the db version, that means they are on a beta version.
if (!string.IsNullOrEmpty(currentVersions.Unity))
if (MaxSdkUtils.IsValidString(currentVersions.Unity))
{
network.CurrentToLatestVersionComparisonResult = AppLovinIntegrationManagerUtils.CompareUnityMediationVersions(currentVersions.Unity, network.LatestVersions.Unity);
}
if (!string.IsNullOrEmpty(network.CurrentVersions.Unity) && AppLovinAutoUpdater.MinAdapterVersions.ContainsKey(network.Name))
if (MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && AppLovinAutoUpdater.MinAdapterVersions.ContainsKey(network.Name))
{
var comparisonResult = AppLovinIntegrationManagerUtils.CompareUnityMediationVersions(network.CurrentVersions.Unity, AppLovinAutoUpdater.MinAdapterVersions[network.Name]);
// Requires update if current version is lower than the min required version.
@@ -223,6 +227,64 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
/// <summary>
/// Checks whether a network has duplicate adapters installed in both the Assets folder and via UPM.
/// </summary>
/// <param name="dependencyPaths">The list of paths to the dependencies.xml files</param>
/// <returns><c>True</c> if there are adapters in both the Assets folder and installed via UPM</returns>
private static bool HasDuplicateAdapters(List<string> dependencyPaths)
{
var inPackagesFolder = dependencyPaths.Any(path => path.Contains("Packages"));
var inAssetsFolder = dependencyPaths.Any(path => path.Contains("Assets"));
return inPackagesFolder && inAssetsFolder;
}
/// <summary>
/// Displays a prompt informing the user that duplicate adapters were detected
/// and allows them to choose which version to keep.
/// </summary>
/// <param name="network">The network that has duplicate adapters installed.</param>
private static void ShowDeleteDuplicateAdapterPrompt(Network network)
{
var keepAssetsAdapter = EditorUtility.DisplayDialog("Duplicate Adapters Detected",
"The " + network.DisplayName + " adapter is installed in both the Assets folder and via UPM. Please choose which version to keep.",
"Keep Assets Folder Version",
"Keep UPM Version");
DeleteDuplicateAdapter(network, keepAssetsAdapter);
}
/// <summary>
/// Removes a duplicate adapter by either deleting it from the Assets folder
/// or uninstalling it from the Unity Package Manager (UPM).
/// </summary>
/// <param name="network">The network for which the duplicate adapter is being removed.</param>
/// <param name="keepAssetsAdapter">If <c>true</c>, retains the adapter in the Assets folder and removes the UPM version;
/// otherwise, deletes the adapter from the Assets folder.</param>
internal static void DeleteDuplicateAdapter(Network network, bool keepAssetsAdapter)
{
// Skip duplicate removal logic for our plugin.
if (network.Name.Equals("APPLOVIN_NETWORK")) return;
if (keepAssetsAdapter)
{
var appLovinManifest = AppLovinUpmManifest.Load();
AppLovinUpmPackageManager.RemovePackages(network, appLovinManifest);
appLovinManifest.Save();
}
else
{
foreach (var pluginFilePath in network.PluginFilePaths)
{
var filePath = Path.Combine(AppLovinIntegrationManager.MediationDirectory, pluginFilePath.Replace("MaxSdk/Mediation/", ""));
FileUtil.DeleteFileOrDirectory(filePath);
FileUtil.DeleteFileOrDirectory(filePath + ".meta");
}
}
AppLovinUpmPackageManager.ResolvePackageManager();
}
/// <summary>
/// Gets the current versions for a given network's dependency file paths. UPM will have multiple paths
/// for each network - one each for iOS and Android.
@@ -327,24 +389,19 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
#if UNITY_2019_2_OR_NEWER
/// <summary>
/// Show the adapter migration prompt if it hasn't been shown yet.
/// Check for the Amazon AppLovin adapter in the project.
/// </summary>
private static void ShowAdapterMigrationPrompt()
/// <returns>Whether the AppLovin Adapter is installed through the Amazon SDK.</returns>
private static bool IsAmazonAppLovinAdapterInstalled()
{
if (_migrationPromptShown) return;
string[] dependenciesFiles = AssetDatabase.FindAssets("t:TextAsset Dependencies", new[] {"Assets"})
.Select(AssetDatabase.GUIDToAssetPath)
.ToArray();
_migrationPromptShown = true;
var migrateAdapters = EditorUtility.DisplayDialog("Adapter Detected in Mediation Folder",
"It appears that you have an adapter in the Mediation folder while AppLovin's plugin is installed via UPM. This could potentially break the integration or cause unexpected errors. Would you like to automatically migrate all your adapters to UPM?", "Yes", "No");
if (migrateAdapters)
{
AppLovinPluginMigrationHelper.MigrateAdapters(PluginData, AppLovinUpmManifest.Load());
}
// Use regex to search for Amazon and then AppLovin in the file paths of the dependencies.xml files.
return dependenciesFiles.Any(filePath => filePath.Contains(AppLovinMediationAmazonAdapterDependenciesPath));
}
#endif
/// <summary>
/// Refresh assets and update current versions after a slight delay to allow for Client.Resolve to finish.
@@ -360,7 +417,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
#endregion
}
#if UNITY_2019_2_OR_NEWER
public class AppLovinUpmPackageManager : IPackageManagerClient
{
public const string PackageNamePrefixAppLovin = "com.applovin.mediation.ads";
@@ -374,7 +430,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private static MethodInfo packageManagerResolveMethod;
#endif
public List<string> GetInstalledMediationNetworks()
public static List<string> GetInstalledMediationNetworks()
{
// Return empty list if we failed to get the package list
var packageCollection = GetPackageCollectionSync(TimeoutFetchPackageCollectionSeconds);
@@ -396,6 +452,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var appLovinManifest = AppLovinUpmManifest.Load();
AddPackages(network, appLovinManifest);
appLovinManifest.Save();
// Remove any versions of the adapter in the Assets folder
AppLovinPackageManager.DeleteDuplicateAdapter(network, false);
ResolvePackageManager();
yield break;
@@ -428,7 +487,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="network">The network to add.</param>
/// <param name="appLovinManifest">The AppLovinUpmManifest instance to edit</param>
private static void RemovePackages(Network network, AppLovinUpmManifest appLovinManifest)
internal static void RemovePackages(Network network, AppLovinUpmManifest appLovinManifest)
{
foreach (var packageInfo in network.Packages)
{
@@ -526,13 +585,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
#endif
public class AppLovinAssetsPackageManager : IPackageManagerClient
{
public List<string> GetInstalledMediationNetworks()
public static List<string> GetInstalledMediationNetworks()
{
var maxMediationDirectory = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, "MaxSdk/Mediation/");
var maxMediationDirectory = AppLovinIntegrationManager.MediationDirectory;
if (!Directory.Exists(maxMediationDirectory)) return new List<string>();
var mediationNetworkDirectories = Directory.GetDirectories(maxMediationDirectory);
@@ -4,7 +4,6 @@ using System.Linq;
using UnityEditor;
using UnityEngine;
#if UNITY_2019_2_OR_NEWER
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
/// <summary>
@@ -20,6 +19,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string OpenUpmRegistryUrl = "https://package.openupm.com";
private static readonly List<string> OpenUpmRegistryScopes = new List<string>() {"com.google.external-dependency-manager"};
private static List<string> betaNetworkPluginFilePaths = new List<string>();
/// <summary>
/// Attempts to move the Unity plugin to UPM by adding the AppLovin scoped registry and dependencies to the manifest.
/// </summary>
@@ -50,6 +51,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
internal static void MigrateAdapters(PluginData pluginData, AppLovinUpmManifest appLovinManifest)
{
var allNetworks = pluginData.MediatedNetworks.Concat(pluginData.PartnerMicroSdks).ToArray();
betaNetworkPluginFilePaths.Clear();
// Add every currently installed network and separate it by android and iOS.
foreach (var network in allNetworks)
@@ -57,6 +59,12 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var currentVersion = network.CurrentVersions != null ? network.CurrentVersions.Unity : "";
if (string.IsNullOrEmpty(currentVersion)) continue;
if (currentVersion.Contains("beta"))
{
betaNetworkPluginFilePaths.AddRange(network.PluginFilePaths);
continue;
}
AppLovinUpmPackageManager.AddPackages(network, appLovinManifest);
}
}
@@ -97,15 +105,62 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var appLovinResourcesDirectory = Path.Combine(pluginPath, "Resources");
var appLovinSettingsTempPath = Path.Combine(Path.GetTempPath(), "AppLovinSettings.asset");
var appLovinMediationTempPath = Path.Combine(Path.GetTempPath(), "Mediation");
// Move the AppLovinSettings.asset file to a temp directory, delete the plugin directory, then move the settings file back.
File.Copy(appLovinSettingsPath, appLovinSettingsTempPath, true);
// Ensure there aren't any errors when moving the files due to the directories/files already existing.
FileUtil.DeleteFileOrDirectory(appLovinSettingsTempPath);
FileUtil.DeleteFileOrDirectory(appLovinMediationTempPath);
var mediationAssetsDir = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, "MaxSdk/Mediation");
// Move the AppLovinSettings.asset file and any beta adapters to a temporary directory.
File.Move(appLovinSettingsPath, appLovinSettingsTempPath);
var adapterSaved = MoveBetaAdaptersIfNeeded(mediationAssetsDir, appLovinMediationTempPath);
// Move the meta file if the adapter was saved to save the asset labels
if (adapterSaved)
{
File.Move(mediationAssetsDir, appLovinMediationTempPath + ".meta");
}
// Delete the plugin directory and then move the AppLovinSettings.asset and beta adapters back.
FileUtil.DeleteFileOrDirectory(pluginPath);
Directory.CreateDirectory(appLovinResourcesDirectory);
File.Move(appLovinSettingsTempPath, appLovinSettingsPath);
MoveBetaAdaptersIfNeeded(appLovinMediationTempPath, mediationAssetsDir);
if (adapterSaved)
{
File.Move(appLovinMediationTempPath + ".meta", mediationAssetsDir);
}
FileUtil.DeleteFileOrDirectory(appLovinMediationTempPath);
}
/// <summary>
/// Moves the beta adapters from a source mediation directory to a destination mediation directory.
/// </summary>
/// <param name="sourceMediationDirectory">The directory containing the beta adapters to be moved.</param>
/// <param name="destinationMediationDirectory">The target directory where the beta adapters should be moved.</param>
private static bool MoveBetaAdaptersIfNeeded(string sourceMediationDirectory, string destinationMediationDirectory)
{
if (betaNetworkPluginFilePaths.Count == 0 || !Directory.Exists(sourceMediationDirectory)) return false;
var movedAdapter = false;
Directory.CreateDirectory(destinationMediationDirectory);
foreach (var pluginFilePath in betaNetworkPluginFilePaths)
{
var sourceDirectory = Path.Combine(sourceMediationDirectory, Path.GetFileName(pluginFilePath));
var destinationDirectory = Path.Combine(destinationMediationDirectory, Path.GetFileName(pluginFilePath));
if (Directory.Exists(sourceDirectory))
{
Directory.Move(sourceDirectory, destinationDirectory);
movedAdapter = true;
}
}
return movedAdapter;
}
#endregion
}
}
#endif
@@ -24,13 +24,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public class AppLovinPostProcessAndroid : IPostGenerateGradleAndroidProject
{
#if UNITY_2019_3_OR_NEWER
private const string PropertyAndroidX = "android.useAndroidX";
private const string PropertyJetifier = "android.enableJetifier";
private const string EnableProperty = "=true";
#endif
private const string PropertyDexingArtifactTransform = "android.enableDexingArtifactTransform";
private const string DisableProperty = "=false";
private const string KeyMetaDataAppLovinVerboseLoggingOn = "applovin.sdk.verbose_logging";
private const string KeyMetaDataGoogleApplicationId = "com.google.android.gms.ads.APPLICATION_ID";
@@ -44,16 +40,12 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string AppLovinSettingsFileName = "applovin_settings.json";
private const string KeyTermsFlowSettings = "terms_flow_settings";
private const string KeyTermsFlowEnabled = "terms_flow_enabled";
private const string KeyTermsFlowTermsOfService = "terms_flow_terms_of_service";
private const string KeyTermsFlowPrivacyPolicy = "terms_flow_privacy_policy";
private const string KeySdkKey = "sdk_key";
private const string KeyConsentFlowSettings = "consent_flow_settings";
private const string KeyConsentFlowEnabled = "consent_flow_enabled";
private const string KeyConsentFlowTermsOfService = "consent_flow_terms_of_service";
private const string KeyConsentFlowPrivacyPolicy = "consent_flow_privacy_policy";
private const string KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR = "consent_flow_show_terms_and_privacy_policy_alert_in_gdpr";
private const string KeyConsentFlowDebugUserGeography = "consent_flow_debug_user_geography";
private const string KeyRenderOutsideSafeArea = "render_outside_safe_area";
@@ -74,15 +66,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public void OnPostGenerateGradleAndroidProject(string path)
{
#if UNITY_2019_3_OR_NEWER
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var gradlePropertiesPath = Path.Combine(path, "../gradle.properties");
var gradleWrapperPropertiesPath = Path.Combine(path, "../gradle/wrapper/gradle-wrapper.properties");
#else
var rootGradleBuildFilePath = Path.Combine(path, "build.gradle");
var gradlePropertiesPath = Path.Combine(path, "gradle.properties");
var gradleWrapperPropertiesPath = Path.Combine(path, "gradle/wrapper/gradle-wrapper.properties");
#endif
UpdateGradleVersionsIfNeeded(gradleWrapperPropertiesPath, rootGradleBuildFilePath);
@@ -93,26 +79,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
var lines = File.ReadAllLines(gradlePropertiesPath);
#if UNITY_2019_3_OR_NEWER
// Add all properties except AndroidX, Jetifier, and DexingArtifactTransform since they may already exist. We will re-add them below.
gradlePropertiesUpdated.AddRange(lines.Where(line => !line.Contains(PropertyAndroidX) && !line.Contains(PropertyJetifier) && !line.Contains(PropertyDexingArtifactTransform)));
#else
// Add all properties except DexingArtifactTransform since it may already exist. We will re-add it below.
gradlePropertiesUpdated.AddRange(lines.Where(line => !line.Contains(PropertyDexingArtifactTransform)));
#endif
gradlePropertiesUpdated.AddRange(lines.Where(line => !line.Contains(PropertyAndroidX) && !line.Contains(PropertyJetifier)));
}
#if UNITY_2019_3_OR_NEWER
// Enable AndroidX and Jetifier properties
gradlePropertiesUpdated.Add(PropertyAndroidX + EnableProperty);
gradlePropertiesUpdated.Add(PropertyJetifier + EnableProperty);
#endif
// `DexingArtifactTransform` has been removed in Gradle 8+ which is the default Gradle version for Unity 6.
#if !UNITY_6000_0_OR_NEWER
// Disable dexing using artifact transform (it causes issues for ExoPlayer with Gradle plugin 3.5.0+)
gradlePropertiesUpdated.Add(PropertyDexingArtifactTransform + DisableProperty);
#endif
try
{
@@ -130,7 +103,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public int callbackOrder
{
get { return int.MaxValue; }
get { return AppLovinPreProcess.CallbackOrder; }
}
private static void ProcessAndroidManifest(string path)
@@ -301,7 +274,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
else
{
MaxSdkLogger.E("Failed to set distribution URL");
MaxSdkLogger.UserError("Failed to set distribution URL");
}
}
@@ -311,7 +284,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// Unity 2022.3+ requires Gradle Plugin version 7.1.2+.
if (MaxSdkUtils.CompareVersions(customGradleToolsVersion, "7.1.2") == MaxSdkUtils.VersionComparisonResult.Lesser)
{
MaxSdkLogger.E("Failed to set gradle plugin version. Unity 2022.3+ requires gradle plugin version 7.1.2+");
MaxSdkLogger.UserError("Failed to set gradle plugin version. Unity 2022.3+ requires gradle plugin version 7.1.2+");
return;
}
@@ -322,7 +295,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
else
{
MaxSdkLogger.E("Failed to set gradle library version");
MaxSdkLogger.UserError("Failed to set gradle library version");
}
var newGradleVersionLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("id 'com.android.application' version '{0}' apply false", customGradleToolsVersion));
@@ -335,7 +308,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
else
{
MaxSdkLogger.E("Failed to set gradle plugin version");
MaxSdkLogger.UserError("Failed to set gradle plugin version");
}
}
}
@@ -383,6 +356,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
consentFlowSettings[KeyConsentFlowTermsOfService] = termsOfServiceUrl;
}
consentFlowSettings[KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR] = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
@@ -22,20 +22,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
#if UNITY_2019_3_OR_NEWER
// On Unity 2019.3+, the path returned is the path to the unityLibrary's module.
// The AppLovin Quality Service buildscript closure related lines need to be added to the root build.gradle file.
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var rootSettingsGradleFilePath = Path.Combine(path, "../settings.gradle");
// For 2022.2 and newer and 2021.3.41+
var qualityServiceAdded = AddPluginToRootGradleBuildFile(rootGradleBuildFilePath);
var appLovinRepositoryAdded = AddAppLovinRepository(rootSettingsGradleFilePath);
// For 2021.3.40 and older and 2022.0 - 2022.1.x
var buildScriptChangesAdded = AddQualityServiceBuildScriptLines(rootGradleBuildFilePath);
var failedToAddPlugin = !buildScriptChangesAdded && !(qualityServiceAdded && appLovinRepositoryAdded);
var failedToAddPlugin = !AddQualityServiceToRootGradleFile(path);
if (failedToAddPlugin)
{
MaxSdkLogger.UserWarning("Failed to add AppLovin Quality Service plugin to the gradle project.");
@@ -44,12 +31,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// The plugin needs to be added to the application module (named launcher)
var applicationGradleBuildFilePath = Path.Combine(path, "../launcher/build.gradle");
#else
// If Gradle template is enabled, we would have already updated the plugin.
if (AppLovinIntegrationManager.GradleTemplateEnabled) return;
var applicationGradleBuildFilePath = Path.Combine(path, "build.gradle");
#endif
if (!File.Exists(applicationGradleBuildFilePath))
{
@@ -62,7 +43,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public int callbackOrder
{
get { return int.MaxValue; }
get { return CallbackOrder; }
}
}
}
@@ -9,18 +9,14 @@
#if UNITY_IOS || UNITY_IPHONE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AppLovinMax.Internal;
using UnityEditor;
using UnityEditor.Callbacks;
#if UNITY_2019_3_OR_NEWER
using UnityEditor.iOS.Xcode.Extensions;
#endif
using UnityEditor.iOS.Xcode;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityEngine.Networking;
@@ -36,9 +32,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
private const string OutputFileName = "AppLovinQualityServiceSetup.rb";
#if !UNITY_2019_3_OR_NEWER
private const string UnityMainTargetName = "Unity-iPhone";
#endif
// Use a priority of 90 to have AppLovin embed frameworks after Pods are installed (EDM finishes installing Pods at priority 60) and before Firebase Crashlytics runs their scripts (at priority 100).
private const int AppLovinEmbedFrameworksPriority = 90;
@@ -61,6 +54,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string KeyConsentFlowEnabled = "ConsentFlowEnabled";
private const string KeyConsentFlowTermsOfService = "ConsentFlowTermsOfService";
private const string KeyConsentFlowPrivacyPolicy = "ConsentFlowPrivacyPolicy";
private const string KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR = "ConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR";
private const string KeyConsentFlowDebugUserGeography = "ConsentFlowDebugUserGeography";
private const string KeyAppLovinSdkKeyToRemove = "AppLovinSdkKey";
@@ -73,7 +67,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// 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.
[PostProcessBuild(AppLovinPreProcess.CallbackOrder)] // We want to run Quality Service script last.
public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
{
if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
@@ -94,49 +88,40 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
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"))
var webRequestConfig = new WebRequestConfig()
{
unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
unityWebRequest.downloadHandler = downloadHandler;
unityWebRequest.uploadHandler = uploadHandler;
var operation = unityWebRequest.SendWebRequest();
DownloadHandler = new DownloadHandlerFile(outputFilePath),
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
EndPoint = "https://api2.safedk.com/v1/build/ios_setup2",
RequestType = WebRequestType.Post,
};
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
webRequestConfig.Headers.Add("Content-Type", "application/json");
#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;
}
var maxWebRequest = new MaxWebRequest(webRequestConfig);
// 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);
var webResponse = maxWebRequest.SendSync();
if (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("AppLovin Quality Service installation failed. Failed to download script with error: " + webResponse.ErrorMessage);
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);
}
[PostProcessBuild(AppLovinEmbedFrameworksPriority)]
@@ -146,13 +131,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
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);
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
@@ -179,23 +160,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var dynamicLibraryPathsToEmbed = GetDynamicLibraryPathsToEmbed(podsDirectory, buildPath);
if (dynamicLibraryPathsToEmbed == null || dynamicLibraryPathsToEmbed.Count == 0) return;
#if UNITY_2019_3_OR_NEWER
foreach (var dynamicLibraryPath in dynamicLibraryPathsToEmbed)
{
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
}
/// <summary>
@@ -255,7 +224,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var pluginData = AppLovinIntegrationManager.LoadPluginDataSync();
if (pluginData == null)
{
MaxSdkLogger.E("Failed to load plugin data. Dynamic libraries will not be embedded.");
MaxSdkLogger.UserError("Failed to load plugin data. Dynamic libraries will not be embedded.");
return null;
}
@@ -315,13 +284,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var currentIosVersion = network.CurrentVersions.Ios;
if (string.IsNullOrEmpty(currentIosVersion)) return false;
var minIosVersion = libraryToEmbed.MinVersion;
var maxIosVersion = libraryToEmbed.MaxVersion;
var greaterThanOrEqualToMinVersion = string.IsNullOrEmpty(minIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, minIosVersion) != MaxSdkUtils.VersionComparisonResult.Lesser;
var lessThanOrEqualToMaxVersion = string.IsNullOrEmpty(maxIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, maxIosVersion) != MaxSdkUtils.VersionComparisonResult.Greater;
return greaterThanOrEqualToMinVersion && lessThanOrEqualToMaxVersion;
return MaxSdkUtils.IsVersionInRange(currentIosVersion, libraryToEmbed.MinVersion, libraryToEmbed.MaxVersion);
}
private static List<string> GetDynamicLibraryPathsInProjectToEmbed(string podsDirectory, List<string> dynamicLibrariesToEmbed)
@@ -446,9 +409,19 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
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");
// Some publishers may configure these settings in their own post-processing scripts.
// Only set them if they haven't already been defined to avoid overwriting publisher-defined values.
var enableModules = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES");
if (string.IsNullOrEmpty(enableModules))
{
project.SetBuildProperty(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES", "YES");
}
var alwaysEmbedSwiftLibraries = project.GetBuildPropertyForAnyConfig(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES");
if (string.IsNullOrEmpty(alwaysEmbedSwiftLibraries))
{
project.SetBuildProperty(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
}
}
private static void CreateSwiftFile(string swiftFilePath)
@@ -466,14 +439,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
[PostProcessBuild(int.MaxValue)]
[PostProcessBuild(AppLovinPreProcess.CallbackOrder)]
public static void MaxPostProcessPlist(BuildTarget buildTarget, string path)
{
var plistPath = Path.Combine(path, "Info.plist");
var plist = new PlistDocument();
plist.ReadFromFile(plistPath);
SetAttributionReportEndpointIfNeeded(plist);
RemoveAttributionReportEndpointIfNeeded(plist);
EnableVerboseLoggingIfNeeded(plist);
AddGoogleApplicationIdIfNeeded(plist);
@@ -485,23 +458,16 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
plist.WriteToFile(plistPath);
}
private static void SetAttributionReportEndpointIfNeeded(PlistDocument plist)
private static void RemoveAttributionReportEndpointIfNeeded(PlistDocument plist)
{
if (AppLovinSettings.Instance.SetAttributionReportEndpoint)
{
plist.root.SetString("NSAdvertisingAttributionReportEndpoint", AppLovinAdvertisingAttributionEndpoint);
}
else
{
PlistElement attributionReportEndPoint;
plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
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");
}
}
// We no longer support this feature. Check if we had previously set the attribution endpoint and un-set it.
if (attributionReportEndPoint == null || !AppLovinAdvertisingAttributionEndpoint.Equals(attributionReportEndPoint.AsString())) return;
MaxSdkLogger.UserWarning("Global SKAdNetwork postback forwarding is no longer supported by AppLovin. Removing AppLovin Advertising Attribution Endpoint from Info.plist.");
plist.root.values.Remove("NSAdvertisingAttributionReportEndpoint");
}
private static void EnableVerboseLoggingIfNeeded(PlistDocument plist)
@@ -569,13 +535,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
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);
var guid = project.AddFile(AppLovinSettingsPlistFileName, AppLovinSettingsPlistFileName);
project.AddFileToBuild(unityMainTargetGuid, guid);
project.WriteToFile(projectPath);
}
@@ -606,6 +568,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
consentFlowInfoRoot.SetString(KeyConsentFlowTermsOfService, termsOfServiceUrl);
}
var shouldShowTermsAndPrivacyPolicyAlertInGdpr = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
consentFlowInfoRoot.SetBoolean(KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR, shouldShowTermsAndPrivacyPolicyAlertInGdpr);
var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
{
@@ -679,36 +644,33 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var installedNetworks = AppLovinPackageManager.GetInstalledMediationNetworks();
var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
var adNetworks = string.Join(",", installedNetworks.ToArray());
if (!string.IsNullOrEmpty(adNetworks))
if (MaxSdkUtils.IsValidString(adNetworks))
{
uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
}
using (var unityWebRequest = UnityWebRequest.Get(uriBuilder.ToString()))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
EndPoint = uriBuilder.ToString()
};
#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();
}
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
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 (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("Failed to retrieve SKAdNetwork IDs with error: " + webResponse.ErrorMessage);
return new SkAdNetworkData();
}
try
{
return JsonUtility.FromJson<SkAdNetworkData>(webResponse.ResponseMessage);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse data '" + webResponse.ResponseMessage + "' with exception: " + exception);
return new SkAdNetworkData();
}
}
@@ -16,7 +16,10 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
public abstract class AppLovinPreProcess
{
// Use a slightly lower value than max value so pubs have the option to run a post process script after ours.
internal const int CallbackOrder = int.MaxValue - 10;
private const string AppLovinDependenciesFileExportPath = "MaxSdk/AppLovin/Editor/Dependencies.xml";
private const string ElementNameDependencies = "dependencies";
private static readonly XmlWriterSettings DependenciesFileXmlWriterSettings = new XmlWriterSettings
{
@@ -26,115 +29,154 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
NewLineHandling = NewLineHandling.Replace
};
private static string AppLovinDependenciesFilePath
protected static string AppLovinDependenciesFilePath
{
get { return AppLovinIntegrationManager.IsPluginInPackageManager ? Path.Combine("Assets", AppLovinDependenciesFileExportPath) : MaxSdkUtils.GetAssetPathForExportPath(AppLovinDependenciesFileExportPath); }
}
/// <summary>
/// Creates a Dependencies.xml file under Assets/MaxSdk/AppLovin/Editor/ directory if the plugin is in the package manager.
/// This is needed since the Dependencies.xml file in the package manager is immutable.
/// Gets the AppLovin Dependencies.xml file. If `createIfNotExists` is true, a new file will be created if one does not exist.
/// </summary>
protected static void CreateAppLovinDependenciesFileIfNeeded()
{
if (!AppLovinIntegrationManager.IsPluginInPackageManager) return;
var dependenciesFilePath = AppLovinDependenciesFilePath;
if (File.Exists(dependenciesFilePath)) return;
var directory = Path.GetDirectoryName(dependenciesFilePath);
Directory.CreateDirectory(directory);
var dependencies = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("dependencies",
new XElement("androidPackages", ""),
new XElement("iosPods", "")
)
);
using (var xmlWriter = XmlWriter.Create(dependenciesFilePath, DependenciesFileXmlWriterSettings))
{
dependencies.Save(xmlWriter);
}
}
/// <summary>
/// Adds a string into AppLovin's Dependencies.xml file inside the containerElementString if it doesn't exist
/// </summary>
/// <param name="lineToAdd">The line you want to add into the xml file</param>
/// <param name="containerElementString">The root XML element under which to add the line. For example, to add a new dependency to Android, pass in "androidPackages"</param>
protected static void TryAddStringToDependencyFile(string lineToAdd, string containerElementString)
/// <param name="path">The path to the AppLovin Dependencies.xml file</param>
/// <param name="createIfNotExists">Whether to create a new Dependencies.xml file if one does not exist</param>
/// <returns></returns>
protected static XDocument GetAppLovinDependenciesFile(string path, bool createIfNotExists = false)
{
try
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
var dependencies = XDocument.Load(dependenciesFilePath);
// Get the container where we are going to insert the line
var containerElement = dependencies.Descendants(containerElementString).FirstOrDefault();
if (containerElement == null)
if (File.Exists(path))
{
MaxSdkLogger.E(containerElementString + " not found in Dependencies.xml file");
return;
return XDocument.Load(path);
}
var elementToAdd = XElement.Parse(lineToAdd);
// Check if the xml file doesn't already contain the string.
if (containerElement.Elements().Any(element => XNode.DeepEquals(element, elementToAdd))) return;
// Append the new element to the container element
containerElement.Add(elementToAdd);
using (var xmlWriter = XmlWriter.Create(dependenciesFilePath, DependenciesFileXmlWriterSettings))
if (createIfNotExists)
{
dependencies.Save(xmlWriter);
return new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ElementNameDependencies));
}
}
catch (Exception exception)
{
MaxSdkLogger.UserWarning("Google CMP will not function. Unable to add string to dependency file due to exception: " + exception.Message);
MaxSdkLogger.UserError("Unable to load Dependencies file due to exception: " + exception.Message);
}
return null;
}
/// <summary>
/// Updates a dependency if it exists, otherwise adds a new dependency.
/// </summary>
/// <param name="dependenciesDocument">The dependencies document we are writing to</param>
/// <param name="parentTag">The parent tag that we want to search for the dependency. For example, to add a new dependency to Android, pass in "androidPackages"</param>
/// <param name="elementTag">The element we are looking to update/add. For example, to add a new dependency to Android, pass in "androidPackage"</param>
/// <param name="matchAttribute">The attribute name we want in the dependency. For example, to add something to the spec attribute, pass in "spec" </param>
/// <param name="matchValuePrefix">The attribute value prefix we are looking to replace. For example, "com.google.android.ump:user-messaging-platform"</param>
/// <param name="newDependency">The new dependency we want to add.</param>
protected static void AddOrUpdateDependency(
XDocument dependenciesDocument,
string parentTag,
string elementTag,
string matchAttribute,
string matchValuePrefix,
XElement newDependency)
{
var parentElement = dependenciesDocument.Root.Element(parentTag);
if (parentElement == null)
{
parentElement = new XElement(parentTag);
dependenciesDocument.Root.Add(parentElement);
}
// Check if a dependency exists that matches the attributes name and value
var existingElement = parentElement.Elements(elementTag)
.FirstOrDefault(element =>
{
var attr = element.Attribute(matchAttribute);
return attr != null && attr.Value.StartsWith(matchValuePrefix, StringComparison.OrdinalIgnoreCase);
});
if (existingElement != null)
{
foreach (var attr in newDependency.Attributes())
{
existingElement.SetAttributeValue(attr.Name, attr.Value);
}
}
else
{
parentElement.Add(newDependency);
}
}
/// <summary>
/// Removes a string from AppLovin's Dependencies.xml file inside the containerElementString if it exists
/// Removes a dependency from an xml file.
/// </summary>
/// <param name="lineToRemove">The line you want to remove from the xml file</param>
/// <param name="containerElementString">The root XML element from which to remove the line. For example, to remove an Android dependency, pass in "androidPackages"</param>
protected static void TryRemoveStringFromDependencyFile(string lineToRemove, string containerElementString)
/// <param name="doc">The xml file to remove a dependency from</param>
/// <param name="parentTag">The parent tag that we want to search for the dependency to remove. For example: "androidPackages"</param>
/// <param name="elementTag">The element we are looking to remove. For example: "androidPackage"</param>
/// <param name="matchAttribute">The attribute name we want to remove. For example: "spec" </param>
/// <param name="matchValuePrefix">The attribute value prefix we are looking to replace. For example: "com.google.android.ump:user-messaging-platform"</param>
/// <returns>True if the dependency was removed successfully, otherwise return false.</returns>
protected static bool RemoveDependency(
XDocument doc,
string parentTag,
string elementTag,
string matchAttribute,
string matchValuePrefix)
{
var root = doc.Root;
if (root == null) return false;
var parentElement = root.Element(parentTag);
if (parentElement == null) return false;
XElement toRemove = null;
foreach (var e in parentElement.Elements(elementTag))
{
var attr = e.Attribute(matchAttribute);
if (attr != null && attr.Value.StartsWith(matchValuePrefix))
{
toRemove = e;
break;
}
}
if (toRemove == null) return false;
toRemove.Remove();
return true;
}
/// <summary>
/// Saves an xml file.
/// </summary>
/// <param name="doc">The document to save</param>
/// <param name="path">The path to the document to save</param>
/// <returns>Returns true if the file was saved successfully</returns>
protected static bool SaveDependenciesFile(XDocument doc, string path)
{
try
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
if (!File.Exists(dependenciesFilePath)) return;
var dependencies = XDocument.Load(dependenciesFilePath);
var containerElement = dependencies.Descendants(containerElementString).FirstOrDefault();
if (containerElement == null)
// Ensure directory exists before saving the file
var directory = Path.GetDirectoryName(path);
if (MaxSdkUtils.IsValidString(directory))
{
MaxSdkLogger.E(containerElementString + " not found in Dependencies.xml file");
return;
// Does nothing if directory already exists
Directory.CreateDirectory(directory);
}
// Check if the dependency line exists.
var elementToFind = XElement.Parse(lineToRemove);
var existingElement = containerElement.Elements().FirstOrDefault(element => XNode.DeepEquals(element, elementToFind));
if (existingElement == null) return;
existingElement.Remove();
using (var xmlWriter = XmlWriter.Create(dependenciesFilePath, DependenciesFileXmlWriterSettings))
using (var xmlWriter = XmlWriter.Create(path, DependenciesFileXmlWriterSettings))
{
dependencies.Save(xmlWriter);
doc.Save(xmlWriter);
return true;
}
}
catch (Exception exception)
{
MaxSdkLogger.UserWarning("Unable to remove string from dependency file due to exception: " + exception.Message);
MaxSdkLogger.UserError("Unable to save Dependencies file due to exception: " + exception.Message);
}
return false;
}
}
}
@@ -8,6 +8,7 @@
#if UNITY_ANDROID
using System.Xml.Linq;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
@@ -18,9 +19,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public class AppLovinPreProcessAndroid : AppLovinProcessGradleBuildFile, IPreprocessBuildWithReport
{
private const string UmpLegacyDependencyLine = "<androidPackage spec=\"com.google.android.ump:user-messaging-platform:2.1.0\" />";
private const string UmpDependencyLine = "<androidPackage spec=\"com.google.android.ump:user-messaging-platform:2.+\" />";
private const string AndroidPackagesContainerElementString = "androidPackages";
private const string ElementNameAndroidPackages = "androidPackages";
private const string ElementNameAndroidPackage = "androidPackage";
private const string AttributeNameSpec = "spec";
private const string UmpDependencyPackage = "com.google.android.ump:user-messaging-platform:";
private const string UmpDependencyVersion = "4.0.0";
public void OnPreprocessBuild(BuildReport report)
{
@@ -33,34 +36,74 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// We can only process gradle template file here. If it is not available, we will try again in post build on Unity IDEs newer than 2018_2 (see AppLovinPostProcessGradleProject).
if (!AppLovinIntegrationManager.GradleTemplateEnabled) return;
#if UNITY_2019_3_OR_NEWER
// The publisher could be migrating from older Unity versions to 2019_3 or newer.
// If so, we should delete the plugin from the template. The plugin will be added to the project's application module in the post processing script (AppLovinPostProcessGradleProject).
RemoveAppLovinQualityServiceOrSafeDkPlugin(AppLovinIntegrationManager.GradleTemplatePath);
#else
AddAppLovinQualityServicePlugin(AppLovinIntegrationManager.GradleTemplatePath);
#endif
}
private static void AddGoogleCmpDependencyIfNeeded()
{
// Remove the legacy fixed UMP version if it exists, we'll add the dependency with a dynamic version below.
TryRemoveStringFromDependencyFile(UmpLegacyDependencyLine, AndroidPackagesContainerElementString);
if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
{
CreateAppLovinDependenciesFileIfNeeded();
TryAddStringToDependencyFile(UmpDependencyLine, AndroidPackagesContainerElementString);
var umpPackage = new XElement(ElementNameAndroidPackage,
new XAttribute(AttributeNameSpec, UmpDependencyPackage + UmpDependencyVersion));
var success = AddOrUpdateAndroidDependency(UmpDependencyPackage, umpPackage );
if (!success)
{
MaxSdkLogger.UserWarning("Google CMP will not function. Unable to add user-messaging-platform dependency.");
}
}
else
{
TryRemoveStringFromDependencyFile(UmpDependencyLine, AndroidPackagesContainerElementString);
RemoveAndroidDependency(UmpDependencyPackage);
}
}
/// <summary>
/// Adds or updates an Android dependency in the AppLovin Dependencies.xml file.
/// </summary>
/// <param name="package">The package that we are trying to update</param>
/// <param name="newDependency">The new dependency to add if it doesn't exist</param>
/// <returns>Returns true if the file was successfully edited</returns>
private static bool AddOrUpdateAndroidDependency(string package, XElement newDependency)
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath, AppLovinIntegrationManager.IsPluginInPackageManager);
if (dependenciesDocument == null) return false;
AddOrUpdateDependency(dependenciesDocument,
ElementNameAndroidPackages,
ElementNameAndroidPackage,
AttributeNameSpec,
package,
newDependency);
return SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
}
/// <summary>
/// Removed an android dependency from the AppLovin Dependencies.xml file.
/// </summary>
/// <param name="package">The package to remove</param>
private static void RemoveAndroidDependency(string package)
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath);
if (dependenciesDocument == null) return;
var removed = RemoveDependency(dependenciesDocument,
ElementNameAndroidPackages,
ElementNameAndroidPackage,
AttributeNameSpec,
package);
if (!removed) return;
SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
}
public int callbackOrder
{
get { return int.MaxValue; }
get { return CallbackOrder; }
}
}
}
@@ -8,6 +8,7 @@
#if UNITY_IOS
using System.Xml.Linq;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
@@ -20,29 +21,77 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
AddGoogleCmpDependencyIfNeeded();
}
private const string UmpLegacyDependencyLine = "<iosPod name=\"GoogleUserMessagingPlatform\" version=\"2.1.0\" />";
private const string UmpDependencyLine = "<iosPod name=\"GoogleUserMessagingPlatform\" version=\"~&gt; 2.1\" />";
private const string IosPodsContainerElementString = "iosPods";
private const string ElementNameIosPods = "iosPods";
private const string ElementNameIosPod = "iosPod";
private const string AttributeNameName = "name";
private const string AttributeNameVersion = "version";
private const string UmpDependencyPod = "GoogleUserMessagingPlatform";
private const string UmpDependencyVersion = "~> 3.1";
private static void AddGoogleCmpDependencyIfNeeded()
{
// Remove the legacy fixed UMP version if it exists, we'll add the dependency with a dynamic version below.
TryRemoveStringFromDependencyFile(UmpLegacyDependencyLine, IosPodsContainerElementString);
if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
{
CreateAppLovinDependenciesFileIfNeeded();
TryAddStringToDependencyFile(UmpDependencyLine, IosPodsContainerElementString);
var umpDependency = new XElement(ElementNameIosPod,
new XAttribute(AttributeNameName, UmpDependencyPod),
new XAttribute(AttributeNameVersion, UmpDependencyVersion));
var success = AddOrUpdateIosDependency(UmpDependencyPod, umpDependency);
if (!success)
{
MaxSdkLogger.UserWarning("Google CMP will not function. Unable to add GoogleUserMessagingPlatform dependency.");
}
}
else
{
TryRemoveStringFromDependencyFile(UmpDependencyLine, IosPodsContainerElementString);
RemoveIosDependency(UmpDependencyPod);
}
}
/// <summary>
/// Adds or updates an iOS pod in the AppLovin Dependencies.xml file.
/// </summary>
/// <param name="pod">The pod that we are trying to update</param>
/// <param name="newDependency">The new dependency to add if it doesn't exist</param>
/// <returns>Returns true if the file was successfully edited</returns>
private static bool AddOrUpdateIosDependency(string pod, XElement newDependency)
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath, AppLovinIntegrationManager.IsPluginInPackageManager);
if (dependenciesDocument == null) return false;
AddOrUpdateDependency(dependenciesDocument,
ElementNameIosPods,
ElementNameIosPod,
AttributeNameName,
pod,
newDependency);
return SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
}
/// <summary>
/// Removed an iOS pod from the AppLovin Dependencies.xml file.
/// </summary>
/// <param name="pod">The pod to remove</param>
private static void RemoveIosDependency(string pod)
{
var dependenciesFilePath = AppLovinDependenciesFilePath;
var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath);
if (dependenciesDocument == null) return;
var removed = RemoveDependency(dependenciesDocument,
ElementNameIosPods,
ElementNameIosPod,
AttributeNameName,
pod);
if (!removed) return;
SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
}
public int callbackOrder
{
get { return int.MaxValue; }
get { return CallbackOrder; }
}
}
}
@@ -6,22 +6,21 @@
#if UNITY_ANDROID
using System.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditorInternal;
using AppLovinMax.Internal;
using UnityEngine;
using UnityEngine.Networking;
using Debug = UnityEngine.Debug;
using UnityEngine.PlayerLoop;
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
[Serializable]
public class AppLovinQualityServiceData
{
// ReSharper disable once InconsistentNaming - Need to keep name for response data
public string api_key;
}
@@ -57,6 +56,60 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string SafeDkLegacyMavenRepo = "http://download.safedk.com";
private const string SafeDkLegacyDependencyClassPath = "com.safedk:SafeDKGradlePlugin:";
/// <summary>
/// Adds the Quality Service plugin to the root gradle file.
/// </summary>
/// <param name="path">The path to the unityLibrary's module.</param>
/// <returns>True if the plugin was added successfully, otherwise return false</returns>
protected static bool AddQualityServiceToRootGradleFile(string path)
{
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var shouldAddQualityServiceToDependencies = ShouldAddQualityServiceToDependencies(rootGradleBuildFilePath);
if (shouldAddQualityServiceToDependencies)
{
// Add the Quality Service Plugin to the dependencies block in the root build.gradle file
return AddQualityServiceBuildScriptLines(rootGradleBuildFilePath);
}
// Add the Quality Service Plugin to the plugin block in the root build.gradle file
var rootSettingsGradleFilePath = Path.Combine(path, "../settings.gradle");
var qualityServiceAdded = AddPluginToRootGradleBuildFile(rootGradleBuildFilePath);
var appLovinRepositoryAdded = AddAppLovinRepository(rootSettingsGradleFilePath);
return qualityServiceAdded && appLovinRepositoryAdded;
}
/// <summary>
/// Determines whether the AppLovin Quality Service plugin should be added to the
/// dependencies block in the root build.gradle file or to the plugins block.
///
/// Gradle's required structure for including plugins varies by version:
/// - Older versions of Gradle require the plugin to be added to the dependencies block.
/// Example:
/// dependencies {
/// classpath 'com.android.tools.build:gradle:4.0.1'
/// classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'
/// }
///
/// - Newer versions of gradle require the plugin to be added to the plugins block.
/// Example:
/// plugins {
/// id 'com.android.application' version '7.4.2' apply false
/// id 'com.android.library' version '7.4.2' apply false
/// id 'com.applovin.quality' version '+' apply false
/// }
///
/// Since Unity projects may use custom Gradle versions depending on the Unity version or
/// user modifications, this check ensures proper integration of the AppLovin plugin.
/// </summary>
/// <param name="rootGradleBuildFile">The path to project's root build.gradle file.</param>
/// <returns><c>true</c> if the file contains a `dependencies` block, indicating an older Gradle version</returns>
private static bool ShouldAddQualityServiceToDependencies(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
return lines.Any(line => TokenBuildScriptDependencies.IsMatch(line));
}
/// <summary>
/// Updates the provided Gradle script to add Quality Service plugin.
/// </summary>
@@ -87,11 +140,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var outputLines = GenerateUpdatedBuildFileLines(
sanitizedLines,
apiKey,
#if UNITY_2019_3_OR_NEWER
false // On Unity 2019.3+, the buildscript closure related lines will to be added to the root build.gradle file.
#else
true
#endif
false // The buildscript closure related lines will to be added to the root build.gradle file.
);
// outputLines can be null if we couldn't add the plugin.
if (outputLines == null) return;
@@ -122,10 +171,10 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="rootGradleBuildFile">The path to project's root build.gradle file.</param>
/// <returns><c>true</c> when the plugin was added successfully.</returns>
protected bool AddPluginToRootGradleBuildFile(string rootGradleBuildFile)
private static bool AddPluginToRootGradleBuildFile(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
// Check if the plugin is already added to the file.
var pluginAdded = lines.Any(line => line.Contains(QualityServicePluginRoot));
if (pluginAdded) return true;
@@ -182,7 +231,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="settingsGradleFile">The path to the project's settings.gradle file.</param>
/// <returns><c>true</c> if the repository was added successfully.</returns>
protected bool AddAppLovinRepository(string settingsGradleFile)
private static bool AddAppLovinRepository(string settingsGradleFile)
{
var lines = File.ReadLines(settingsGradleFile).ToList();
var outputLines = new List<string>();
@@ -244,7 +293,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return true;
}
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Adds the necessary AppLovin Quality Service dependency and maven repo lines to the provided root build.gradle file.
/// Sample build.gradle file after adding quality service:
@@ -265,7 +313,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="rootGradleBuildFile">The root build.gradle file path</param>
/// <returns><c>true</c> if the build script lines were applied correctly.</returns>
protected bool AddQualityServiceBuildScriptLines(string rootGradleBuildFile)
private static bool AddQualityServiceBuildScriptLines(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
var outputLines = GenerateUpdatedBuildFileLines(lines, null, true);
@@ -307,46 +355,35 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
Console.WriteLine(exception);
}
}
#endif
private static AppLovinQualityServiceData RetrieveQualityServiceData(string sdkKey)
{
var postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey);
var bodyRaw = Encoding.UTF8.GetBytes(postJson);
// Upload handler is automatically disposed when UnityWebRequest is disposed
var uploadHandler = new UploadHandlerRaw(bodyRaw);
uploadHandler.contentType = "application/json";
using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/cred"))
var webRequestConfig = new WebRequestConfig()
{
unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
unityWebRequest.uploadHandler = uploadHandler;
unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
EndPoint = "https://api2.safedk.com/v1/build/cred",
RequestType = WebRequestType.Post,
};
var operation = unityWebRequest.SendWebRequest();
webRequestConfig.Headers.Add("Content-Type", "application/json");
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError("Failed to retrieve API Key for SDK Key: " + sdkKey + "with error: " + unityWebRequest.error);
return new AppLovinQualityServiceData();
}
if (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("Failed to retrieve API Key for SDK Key: " + sdkKey + "with error: " + webResponse.ErrorMessage);
return new AppLovinQualityServiceData();
}
try
{
return JsonUtility.FromJson<AppLovinQualityServiceData>(unityWebRequest.downloadHandler.text);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse API Key." + exception);
return new AppLovinQualityServiceData();
}
try
{
return JsonUtility.FromJson<AppLovinQualityServiceData>(webResponse.ResponseMessage);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse API Key." + exception);
return new AppLovinQualityServiceData();
}
}
@@ -413,7 +450,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private static List<string> GenerateUpdatedBuildFileLines(List<string> lines, string apiKey, bool addBuildScriptLines)
{
var addPlugin = !string.IsNullOrEmpty(apiKey);
// Check if the plugin exists, if so, update the SDK Key.
var pluginExists = lines.Any(line => TokenAppLovinPlugin.IsMatch(line));
return pluginExists ? UpdateExistingPlugin(lines, apiKey) : AddPluginAndBuildScript(lines, apiKey, addBuildScriptLines);
}
private static List<string> UpdateExistingPlugin(List<string> lines, string apiKey)
{
// A sample of the template file.
// ...
// allprojects {
@@ -434,149 +477,162 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// **DEPS**}
// ...
var outputLines = new List<string>();
// Check if the plugin exists, if so, update the SDK Key.
var pluginExists = lines.Any(line => TokenAppLovinPlugin.IsMatch(line));
if (pluginExists)
var pluginMatched = false;
var insideAppLovinClosure = false;
var updatedApiKey = false;
var mavenRepoUpdated = false;
var dependencyClassPathUpdated = false;
foreach (var line in lines)
{
var pluginMatched = false;
var insideAppLovinClosure = false;
var updatedApiKey = false;
var mavenRepoUpdated = false;
var dependencyClassPathUpdated = false;
foreach (var line in lines)
// Bintray maven repo is no longer being used. Update to s3 maven repo with regex check
if (!mavenRepoUpdated && (line.Contains(QualityServiceBintrayMavenRepo) || line.Contains(QualityServiceNoRegexMavenRepo)))
{
// Bintray maven repo is no longer being used. Update to s3 maven repo with regex check
if (!mavenRepoUpdated && (line.Contains(QualityServiceBintrayMavenRepo) || line.Contains(QualityServiceNoRegexMavenRepo)))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
mavenRepoUpdated = true;
continue;
}
// We no longer use version specific dependency class path. Just use + for version to always pull the latest.
if (!dependencyClassPathUpdated && line.Contains(QualityServiceDependencyClassPathV3))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
dependencyClassPathUpdated = true;
continue;
}
if (!pluginMatched && line.Contains(QualityServicePlugin))
{
insideAppLovinClosure = true;
pluginMatched = true;
}
if (insideAppLovinClosure && line.Contains("}"))
{
insideAppLovinClosure = false;
}
// Update the API key.
if (insideAppLovinClosure && !updatedApiKey && TokenApiKey.IsMatch(line))
{
outputLines.Add(string.Format(QualityServiceApiKey, apiKey));
updatedApiKey = true;
}
// Keep adding the line until we find and update the plugin.
else
{
outputLines.Add(line);
}
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
mavenRepoUpdated = true;
continue;
}
}
// Plugin hasn't been added yet, add it.
else
{
var buildScriptClosureDepth = 0;
var insideBuildScriptClosure = false;
var buildScriptMatched = false;
var qualityServiceRepositoryAdded = false;
var qualityServiceDependencyClassPathAdded = false;
var qualityServicePluginAdded = false;
foreach (var line in lines)
// We no longer use version specific dependency class path. Just use + for version to always pull the latest.
if (!dependencyClassPathUpdated && line.Contains(QualityServiceDependencyClassPathV3))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
dependencyClassPathUpdated = true;
continue;
}
if (!pluginMatched && line.Contains(QualityServicePlugin))
{
insideAppLovinClosure = true;
pluginMatched = true;
}
if (insideAppLovinClosure && line.Contains("}"))
{
insideAppLovinClosure = false;
}
// Update the API key.
if (insideAppLovinClosure && !updatedApiKey && TokenApiKey.IsMatch(line))
{
outputLines.Add(string.Format(QualityServiceApiKey, apiKey));
updatedApiKey = true;
}
// Keep adding the line until we find and update the plugin.
else
{
// Add the line to the output lines.
outputLines.Add(line);
// Check if we need to add the build script lines and add them.
if (addBuildScriptLines)
{
if (!buildScriptMatched && line.Contains(BuildScriptMatcher))
{
buildScriptMatched = true;
insideBuildScriptClosure = true;
}
// Match the parenthesis to track if we are still inside the buildscript closure.
if (insideBuildScriptClosure)
{
if (line.Contains("{"))
{
buildScriptClosureDepth++;
}
if (line.Contains("}"))
{
buildScriptClosureDepth--;
}
if (buildScriptClosureDepth == 0)
{
insideBuildScriptClosure = false;
// There may be multiple buildscript closures and we need to keep looking until we added both the repository and classpath.
buildScriptMatched = qualityServiceRepositoryAdded && qualityServiceDependencyClassPathAdded;
}
}
if (insideBuildScriptClosure)
{
// Add the build script dependency repositories.
if (!qualityServiceRepositoryAdded && TokenBuildScriptRepositories.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
qualityServiceRepositoryAdded = true;
}
// Add the build script dependencies.
else if (!qualityServiceDependencyClassPathAdded && TokenBuildScriptDependencies.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
qualityServiceDependencyClassPathAdded = true;
}
}
}
// Check if we need to add the plugin and add it.
if (addPlugin)
{
// Add the plugin.
if (!qualityServicePluginAdded && TokenApplicationPlugin.IsMatch(line))
{
outputLines.Add(QualityServiceApplyPlugin);
outputLines.AddRange(GenerateAppLovinPluginClosure(apiKey));
qualityServicePluginAdded = true;
}
}
}
if ((addBuildScriptLines && (!qualityServiceRepositoryAdded || !qualityServiceDependencyClassPathAdded)) || (addPlugin && !qualityServicePluginAdded))
{
return null;
}
}
return outputLines;
}
private static List<string> AddPluginAndBuildScript(List<string> lines, string apiKey, bool addBuildScriptLines)
{
var shouldAddPlugin = MaxSdkUtils.IsValidString(apiKey);
if (shouldAddPlugin)
{
lines = AddPlugin(lines, apiKey);
if (lines == null) return null;
}
if (!addBuildScriptLines) return lines;
lines = AddBuildScript(lines);
return lines;
}
private static List<string> AddBuildScript(List<string> lines)
{
var outputLines = new List<string>();
var buildScriptClosureDepth = 0;
var insideBuildScriptClosure = false;
var buildScriptMatched = false;
var qualityServiceRepositoryAdded = false;
var qualityServiceDependencyClassPathAdded = false;
foreach (var line in lines)
{
// Add the line to the output lines.
outputLines.Add(line);
if (!buildScriptMatched && line.Contains(BuildScriptMatcher))
{
buildScriptMatched = true;
insideBuildScriptClosure = true;
}
// Match the parenthesis to track if we are still inside the buildscript closure.
if (insideBuildScriptClosure)
{
if (line.Contains("{"))
{
buildScriptClosureDepth++;
}
if (line.Contains("}"))
{
buildScriptClosureDepth--;
}
if (buildScriptClosureDepth == 0)
{
insideBuildScriptClosure = false;
// There may be multiple buildscript closures and we need to keep looking until we added both the repository and classpath.
buildScriptMatched = qualityServiceRepositoryAdded && qualityServiceDependencyClassPathAdded;
}
}
if (insideBuildScriptClosure)
{
// Add the build script dependency repositories.
if (!qualityServiceRepositoryAdded && TokenBuildScriptRepositories.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
qualityServiceRepositoryAdded = true;
}
// Add the build script dependencies.
else if (!qualityServiceDependencyClassPathAdded && TokenBuildScriptDependencies.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
qualityServiceDependencyClassPathAdded = true;
}
}
}
if (!qualityServiceRepositoryAdded || !qualityServiceDependencyClassPathAdded)
{
return null;
}
return outputLines;
}
private static List<string> AddPlugin(List<string> lines, string apiKey)
{
var outputLines = new List<string>();
var qualityServicePluginAdded = false;
foreach (var line in lines)
{
outputLines.Add(line);
// Add the plugin.
if (qualityServicePluginAdded || !TokenApplicationPlugin.IsMatch(line)) continue;
outputLines.Add(QualityServiceApplyPlugin);
outputLines.AddRange(GenerateAppLovinPluginClosure(apiKey));
qualityServicePluginAdded = true;
}
return qualityServicePluginAdded ? outputLines : null;
}
public static string GetFormattedBuildScriptLine(string buildScriptLine)
{
#if UNITY_2022_2_OR_NEWER
return " "
#elif UNITY_2019_3_OR_NEWER
return " "
#else
return " "
return " "
#endif
+ buildScriptLine;
}
@@ -21,16 +21,13 @@ using UnityEngine.Serialization;
/// </summary>
public class AppLovinSettings : ScriptableObject
{
public const string SettingsExportPath = "MaxSdk/Resources/AppLovinSettings.asset";
private const string SettingsExportPath = "MaxSdk/Resources/AppLovinSettings.asset";
private static AppLovinSettings instance;
[SerializeField] private bool qualityServiceEnabled = true;
[SerializeField] private string sdkKey;
[SerializeField] private bool setAttributionReportEndpoint;
[SerializeField] private bool addApsSkAdNetworkIds;
[SerializeField] private string customGradleVersionUrl;
[SerializeField] private string customGradleToolsVersion;
@@ -60,16 +57,15 @@ public class AppLovinSettings : ScriptableObject
return instance;
}
// If there is no existing AppLovinSettings asset, create one in the default location
string settingsFilePath;
// The settings file should be under the Assets/ folder so that it can be version controlled and cannot be overriden when updating.
// If the plugin is outside the Assets folder, create the settings asset at the default location.
// If the plugin is outside the Assets folder or if there is no existing AppLovinSettings asset, create the settings asset at the default location.
if (AppLovinIntegrationManager.IsPluginInPackageManager)
{
// Note: Can't use absolute path when calling `CreateAsset`. Should use relative path to Assets/ directory.
settingsFilePath = Path.Combine("Assets", SettingsExportPath);
// Note: Can't use absolute path when calling `CreateAsset`. Should use path relative to Assets/ directory.
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine("Assets", SettingsExportPath));
var maxSdkDir = Path.Combine(Application.dataPath, "MaxSdk");
var maxSdkDir = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(Application.dataPath, "MaxSdk"));
if (!Directory.Exists(maxSdkDir))
{
Directory.CreateDirectory(maxSdkDir);
@@ -77,7 +73,7 @@ public class AppLovinSettings : ScriptableObject
}
else
{
settingsFilePath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath);
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath));
}
var settingsDir = Path.GetDirectoryName(settingsFilePath);
@@ -117,24 +113,6 @@ public class AppLovinSettings : ScriptableObject
set { Instance.sdkKey = value; }
}
/// <summary>
/// Whether or not to set `NSAdvertisingAttributionReportEndpoint` in Info.plist.
/// </summary>
public bool SetAttributionReportEndpoint
{
get { return Instance.setAttributionReportEndpoint; }
set { Instance.setAttributionReportEndpoint = value; }
}
/// <summary>
/// Whether or not to add Amazon Publisher Services SKAdNetworkID's.
/// </summary>
public bool AddApsSkAdNetworkIds
{
get { return Instance.addApsSkAdNetworkIds; }
set { Instance.addApsSkAdNetworkIds = value; }
}
/// <summary>
/// A URL to set the distributionUrl in the gradle-wrapper.properties file (ex: https\://services.gradle.org/distributions/gradle-6.9.3-bin.zip)
/// </summary>
@@ -1,4 +1,3 @@
#if UNITY_2019_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
@@ -189,4 +188,3 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
#endregion
}
}
#endif