ball 项目提交
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// AppLovinAutoUpdater.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 1/27/20.
|
||||
// Copyright © 2020 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles auto updates for AppLovin MAX plugin.
|
||||
/// </summary>
|
||||
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.
|
||||
private static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
private static readonly int SecondsInADay = (int) TimeSpan.FromDays(1).TotalSeconds;
|
||||
|
||||
// TODO: Make this list dynamic.
|
||||
public static readonly Dictionary<string, string> MinAdapterVersions = new Dictionary<string, string>()
|
||||
{
|
||||
{"ADMOB_NETWORK", "android_23.3.0.1_ios_11.9.0.1"},
|
||||
{"BIDMACHINE_NETWORK", "android_3.0.1.1_ios_3.0.0.0.1"},
|
||||
{"CHARTBOOST_NETWORK", "android_9.7.0.3_ios_9.7.0.2"},
|
||||
{"FACEBOOK_MEDIATE", "android_6.17.0.1_ios_6.15.2.1"},
|
||||
{"FYBER_NETWORK", "android_8.3.1.1_ios_8.3.2.1"},
|
||||
{"GOOGLE_AD_MANAGER_NETWORK", "android_23.3.0.1_ios_11.9.0.1"},
|
||||
{"HYPRMX_NETWORK", "android_6.4.2.1_ios_6.4.1.0.1"},
|
||||
{"INMOBI_NETWORK", "android_10.7.7.1_ios_10.7.5.1"},
|
||||
{"IRONSOURCE_NETWORK", "android_8.3.0.0.2_ios_8.3.0.0.1"},
|
||||
{"LINE_NETWORK", "android_2024.8.27.1_ios_2.8.20240827.1"},
|
||||
{"MINTEGRAL_NETWORK", "android_16.8.51.1_ios_7.7.2.0.1"},
|
||||
{"MOBILEFUSE_NETWORK", "android_1.7.6.1_ios_1.7.6.1"},
|
||||
{"MOLOCO_NETWORK", "android_3.1.0.1_ios_3.1.3.1"},
|
||||
{"MYTARGET_NETWORK", "android_5.22.1.1_ios_5.21.7.1"},
|
||||
{"PUBMATIC_NETWORK", "android_3.9.0.2_ios_3.9.0.2"},
|
||||
{"SMAATO_NETWORK", "android_22.7.0.1_ios_22.8.4.1"},
|
||||
{"TIKTOK_NETWORK", "android_6.2.0.5.2_ios_6.2.0.7.2"},
|
||||
{"UNITY_NETWORK", "android_4.12.2.1_ios_4.12.2.1"},
|
||||
{"VERVE_NETWORK", "android_3.0.4.1_ios_3.0.4.1"},
|
||||
{"VUNGLE_NETWORK", "android_7.4.1.1_ios_7.4.1.1"},
|
||||
{"YANDEX_NETWORK", "android_7.4.0.1_ios_2.18.0.1"},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a new version of the plugin is available and prompts the user to update if one is available.
|
||||
/// </summary>
|
||||
public static void Update()
|
||||
{
|
||||
var now = (int) (DateTime.UtcNow - EpochTime).TotalSeconds;
|
||||
if (EditorPrefs.HasKey(KeyLastUpdateCheckTime))
|
||||
{
|
||||
var elapsedTime = now - EditorPrefs.GetInt(KeyLastUpdateCheckTime);
|
||||
|
||||
// Check if we have checked for a new version in the last 24 hrs and skip update if we have.
|
||||
if (elapsedTime < SecondsInADay) return;
|
||||
}
|
||||
|
||||
// Update last checked time.
|
||||
EditorPrefs.SetInt(KeyLastUpdateCheckTime, now);
|
||||
|
||||
// Load the plugin data
|
||||
AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.LoadPluginData(data =>
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
ShowPluginUpdateDialogIfNeeded(data);
|
||||
ShowNetworkAdaptersUpdateDialogIfNeeded(data.MediatedNetworks);
|
||||
ShowGoogleNetworkAdaptersUpdateDialogIfNeeded(data.MediatedNetworks);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void ShowPluginUpdateDialogIfNeeded(PluginData data)
|
||||
{
|
||||
// Check if publisher has disabled auto update.
|
||||
if (!EditorPrefs.GetBool(KeyAutoUpdateEnabled, true)) return;
|
||||
|
||||
// Check if the current and latest version are the same or if the publisher is on a newer version (on beta). If so, skip update.
|
||||
var comparison = data.AppLovinMax.CurrentToLatestVersionComparisonResult;
|
||||
if (comparison == MaxSdkUtils.VersionComparisonResult.Equal || comparison == MaxSdkUtils.VersionComparisonResult.Greater) return;
|
||||
|
||||
// A new version of the plugin is available. Show a dialog to the publisher.
|
||||
var option = EditorUtility.DisplayDialogComplex(
|
||||
"AppLovin MAX Plugin Update",
|
||||
"A new version of AppLovin MAX plugin is available for download. Update now?",
|
||||
"Download",
|
||||
"Not Now",
|
||||
"Don't Ask Again");
|
||||
|
||||
if (option == 0) // Download
|
||||
{
|
||||
MaxSdkLogger.UserDebug("Downloading plugin...");
|
||||
AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.DownloadPlugin(data.AppLovinMax));
|
||||
}
|
||||
else if (option == 1) // Not Now
|
||||
{
|
||||
// Do nothing
|
||||
MaxSdkLogger.UserDebug("Update postponed.");
|
||||
}
|
||||
else if (option == 2) // Don't Ask Again
|
||||
{
|
||||
MaxSdkLogger.UserDebug("Auto Update disabled. You can enable it again from the AppLovin Integration Manager");
|
||||
EditorPrefs.SetBool(KeyAutoUpdateEnabled, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowNetworkAdaptersUpdateDialogIfNeeded(Network[] networks)
|
||||
{
|
||||
var networksToUpdate = networks.Where(network => network.RequiresUpdate).ToList();
|
||||
|
||||
// If all networks are above the required version, do nothing.
|
||||
if (networksToUpdate.Count <= 0) return;
|
||||
|
||||
// We found a few adapters that are not compatible with the current SDK, show alert.
|
||||
var message = "The following network adapters are not compatible with the current version of AppLovin MAX Plugin:\n";
|
||||
foreach (var networkName in networksToUpdate)
|
||||
{
|
||||
message += "\n- ";
|
||||
message += networkName.DisplayName + " (Requires " + MinAdapterVersions[networkName.Name] + " or newer)";
|
||||
}
|
||||
|
||||
message += "\n\nPlease update them to the latest versions to avoid any issues.";
|
||||
|
||||
AppLovinIntegrationManager.ShowBuildFailureDialog(message);
|
||||
}
|
||||
|
||||
private static void ShowGoogleNetworkAdaptersUpdateDialogIfNeeded(Network[] networks)
|
||||
{
|
||||
// AdMob and GAM use the same SDKs so their adapters should use the same underlying SDK version.
|
||||
var googleNetwork = networks.FirstOrDefault(network => network.Name.Equals("ADMOB_NETWORK"));
|
||||
var googleAdManagerNetwork = networks.FirstOrDefault(network => network.Name.Equals("GOOGLE_AD_MANAGER_NETWORK"));
|
||||
|
||||
// If both AdMob and GAM are not integrated, do nothing.
|
||||
if (googleNetwork == null || string.IsNullOrEmpty(googleNetwork.CurrentVersions.Unity) ||
|
||||
googleAdManagerNetwork == null || string.IsNullOrEmpty(googleAdManagerNetwork.CurrentVersions.Unity)) return;
|
||||
|
||||
var isAndroidVersionCompatible = GoogleNetworkAdaptersCompatible(googleNetwork.CurrentVersions.Android, googleAdManagerNetwork.CurrentVersions.Android, "19.8.0.0");
|
||||
var isIosVersionCompatible = GoogleNetworkAdaptersCompatible(googleNetwork.CurrentVersions.Ios, googleAdManagerNetwork.CurrentVersions.Ios, "8.0.0.0");
|
||||
|
||||
if (isAndroidVersionCompatible && isIosVersionCompatible) return;
|
||||
|
||||
var message = "You may see unexpected errors if you use different versions of the AdMob and Google Ad Manager adapter SDKs. " +
|
||||
"AdMob and Google Ad Manager share the same SDKs.\n\n" +
|
||||
"You can be sure that you are using the same SDK for both if the first three numbers in each adapter version match.";
|
||||
|
||||
AppLovinIntegrationManager.ShowBuildFailureDialog(message);
|
||||
}
|
||||
|
||||
private static bool GoogleNetworkAdaptersCompatible(string googleVersion, string googleAdManagerVersion, string breakingVersion)
|
||||
{
|
||||
var googleResult = MaxSdkUtils.CompareVersions(googleVersion, breakingVersion);
|
||||
var googleAdManagerResult = MaxSdkUtils.CompareVersions(googleAdManagerVersion, breakingVersion);
|
||||
|
||||
// If one is less than the breaking version and the other is not, they are not compatible.
|
||||
if (googleResult == MaxSdkUtils.VersionComparisonResult.Lesser &&
|
||||
googleAdManagerResult != MaxSdkUtils.VersionComparisonResult.Lesser) return false;
|
||||
|
||||
if (googleAdManagerResult == MaxSdkUtils.VersionComparisonResult.Lesser &&
|
||||
googleResult != MaxSdkUtils.VersionComparisonResult.Lesser) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bcdd5226273242c5bd9ec79568202e6
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// AppLovinBuildPostProcessor.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 10/30/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_IOS || UNITY_IPHONE
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to run command line tools.
|
||||
///
|
||||
/// TODO: Currently only supports shell (Linux). Add support for Windows machines.
|
||||
/// </summary>
|
||||
public static class AppLovinCommandLine
|
||||
{
|
||||
/// <summary>
|
||||
/// Result obtained by running a command line command.
|
||||
/// </summary>
|
||||
public class Result
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard output stream from command line.
|
||||
/// </summary>
|
||||
public string StandardOutput;
|
||||
|
||||
/// <summary>
|
||||
/// Standard error stream from command line.
|
||||
/// </summary>
|
||||
public string StandardError;
|
||||
|
||||
/// <summary>
|
||||
/// Exit code returned from command line.
|
||||
/// </summary>
|
||||
public int ExitCode;
|
||||
|
||||
/// <summary>
|
||||
/// The description of the result that can be used for error logging.
|
||||
/// </summary>
|
||||
public string Message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a command line tool using the provided <see cref="toolPath"/> and <see cref="arguments"/>.
|
||||
/// </summary>
|
||||
/// <param name="toolPath">The tool path to run</param>
|
||||
/// <param name="arguments">The arguments to be passed to the command line tool</param>
|
||||
/// <param name="workingDirectory">The directory from which to run this command.</param>
|
||||
/// <returns></returns>
|
||||
public static Result Run(string toolPath, string arguments, string workingDirectory)
|
||||
{
|
||||
var stdoutFileName = Path.GetTempFileName();
|
||||
var stderrFileName = Path.GetTempFileName();
|
||||
|
||||
var process = new Process();
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
process.StartInfo.CreateNoWindow = false;
|
||||
process.StartInfo.RedirectStandardInput = false;
|
||||
process.StartInfo.RedirectStandardOutput = false;
|
||||
process.StartInfo.RedirectStandardError = false;
|
||||
|
||||
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||
process.StartInfo.FileName = "bash";
|
||||
process.StartInfo.Arguments = string.Format("-l -c '\"{0}\" {1} 1> {2} 2> {3}'", toolPath, arguments, stdoutFileName, stderrFileName);
|
||||
process.Start();
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
var stdout = File.ReadAllText(stdoutFileName);
|
||||
var stderr = File.ReadAllText(stderrFileName);
|
||||
|
||||
File.Delete(stdoutFileName);
|
||||
File.Delete(stderrFileName);
|
||||
|
||||
var result = new Result();
|
||||
result.StandardOutput = stdout;
|
||||
result.StandardError = stderr;
|
||||
result.ExitCode = process.ExitCode;
|
||||
|
||||
var messagePrefix = result.ExitCode == 0 ? "Command executed successfully" : "Failed to run command";
|
||||
result.Message = string.Format("{0}: '{1} {2}'\nstdout: {3}\nstderr: {4}\nExit code: {5}", messagePrefix, toolPath, arguments, stdout, stderr, process.ExitCode);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d0aa55f9a7d2440f871dfb256372a33
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// AppLovinEditorCoroutine.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 7/25/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// A coroutine that can update based on editor application update.
|
||||
/// </summary>
|
||||
public class AppLovinEditorCoroutine
|
||||
{
|
||||
/// <summary>
|
||||
/// Keeps track of the coroutine currently running.
|
||||
/// </summary>
|
||||
private IEnumerator enumerator;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of coroutines that have yielded to the current enumerator.
|
||||
/// </summary>
|
||||
private readonly List<IEnumerator> history = new List<IEnumerator>();
|
||||
|
||||
private AppLovinEditorCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and starts a coroutine.
|
||||
/// </summary>
|
||||
/// <param name="enumerator">The coroutine to be started</param>
|
||||
/// <returns>The coroutine that has been started.</returns>
|
||||
public static AppLovinEditorCoroutine StartCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
var coroutine = new AppLovinEditorCoroutine(enumerator);
|
||||
coroutine.Start();
|
||||
return coroutine;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the coroutine.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (EditorApplication.update == null) return;
|
||||
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
}
|
||||
|
||||
private void OnEditorUpdate()
|
||||
{
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
// If there is a coroutine to yield for inside the coroutine, add the initial one to history and continue the second one
|
||||
if (enumerator.Current is IEnumerator)
|
||||
{
|
||||
history.Add(enumerator);
|
||||
enumerator = (IEnumerator) enumerator.Current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current coroutine has ended, check if we have more coroutines in history to be run.
|
||||
if (history.Count == 0)
|
||||
{
|
||||
// No more coroutines to run, stop updating.
|
||||
Stop();
|
||||
}
|
||||
// Step out and finish the code in the coroutine that yielded to it
|
||||
else
|
||||
{
|
||||
var index = history.Count - 1;
|
||||
enumerator = history[index];
|
||||
history.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95747c688378548eeb92aeb528ac96ff
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// MaxInitialization.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Thomas So on 5/24/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public class AppLovinInitialize
|
||||
{
|
||||
private static readonly List<string> ObsoleteNetworks = new List<string>
|
||||
{
|
||||
"AdColony",
|
||||
"Criteo",
|
||||
"Nend",
|
||||
"Snap",
|
||||
"Tapjoy",
|
||||
"VerizonAds",
|
||||
"VoodooAds"
|
||||
};
|
||||
|
||||
private static readonly List<string> ObsoleteFileExportPathsToDelete = new List<string>
|
||||
{
|
||||
// The `MaxSdk/Scripts/Editor` folder contents have been moved into `MaxSdk/Scripts/IntegrationManager/Editor`.
|
||||
"MaxSdk/Scripts/Editor",
|
||||
"MaxSdk/Scripts/Editor.meta",
|
||||
|
||||
// The `EventSystemChecker` has been renamed to `MaxEventSystemChecker`.
|
||||
"MaxSdk/Scripts/EventSystemChecker.cs",
|
||||
"MaxSdk/Scripts/EventSystemChecker.cs.meta",
|
||||
|
||||
// Google AdMob adapter pre/post process scripts. The logic has been migrated to the main plugin.
|
||||
"MaxSdk/Mediation/Google/Editor/MaxGoogleInitialize.cs",
|
||||
"MaxSdk/Mediation/Google/Editor/MaxGoogleInitialize.cs.meta",
|
||||
"MaxSdk/Mediation/Google/Editor/MaxMediationGoogleUtils.cs",
|
||||
"MaxSdk/Mediation/Google/Editor/MaxMediationGoogleUtils.cs.meta",
|
||||
"MaxSdk/Mediation/Google/Editor/PostProcessor.cs",
|
||||
"MaxSdk/Mediation/Google/Editor/PostProcessor.cs.meta",
|
||||
"MaxSdk/Mediation/Google/Editor/PreProcessor.cs",
|
||||
"MaxSdk/Mediation/Google/Editor/PreProcessor.cs.meta",
|
||||
"MaxSdk/Mediation/Google/Editor/MaxSdk.Mediation.Google.Editor.asmdef",
|
||||
"MaxSdk/Mediation/Google/MaxSdk.Mediation.Google.Editor.asmdef.meta",
|
||||
"Plugins/Android/MaxMediationGoogle.androidlib",
|
||||
"Plugins/Android/MaxMediationGoogle.androidlib.meta",
|
||||
|
||||
// Google Ad Manager adapter pre/post process scripts. The logic has been migrated to the main plugin.
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/MaxGoogleAdManagerInitialize.cs",
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/MaxGoogleAdManagerInitialize.cs.meta",
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/PostProcessor.cs",
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/PostProcessor.cs.meta",
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/MaxSdk.Mediation.GoogleAdManager.Editor.asmdef",
|
||||
"MaxSdk/Mediation/GoogleAdManager/Editor/MaxSdk.Mediation.GoogleAdManager.Editor.asmdef.meta",
|
||||
"Plugins/Android/MaxMediationGoogleAdManager.androidlib",
|
||||
"Plugins/Android/MaxMediationGoogleAdManager.androidlib.meta",
|
||||
|
||||
// The `VariableService` has been removed.
|
||||
"MaxSdk/Scripts/MaxVariableServiceAndroid.cs",
|
||||
"MaxSdk/Scripts/MaxVariableServiceAndroid.cs.meta",
|
||||
"MaxSdk/Scripts/MaxVariableServiceiOS.cs",
|
||||
"MaxSdk/Scripts/MaxVariableServiceiOS.cs.meta",
|
||||
"MaxSdk/Scripts/MaxVariableServiceUnityEditor.cs",
|
||||
"MaxSdk/Scripts/MaxVariableServiceUnityEditor.cs.meta",
|
||||
|
||||
// The `MaxSdk/Scripts/Editor` folder contents have been moved into `MaxSdk/Scripts/IntegrationManager/Editor`.
|
||||
"MaxSdk/Version.md",
|
||||
"MaxSdk/Version.md.meta",
|
||||
|
||||
// The alert_icon.png has been renamed to error_icon.png.
|
||||
"MaxSdk/Resources/Images/alert_icon.png"
|
||||
|
||||
// TODO: Add MaxTargetingData and MaxUserSegment when the plugin has enough traction.
|
||||
};
|
||||
|
||||
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"))
|
||||
{
|
||||
MaxSdkLogger.UserError("Detected iOS project version less than iOS 9 - The AppLovin MAX SDK WILL NOT WORK ON < iOS9!!!");
|
||||
}
|
||||
#endif
|
||||
|
||||
var isPluginInPackageManager = AppLovinIntegrationManager.IsPluginInPackageManager;
|
||||
if (!isPluginInPackageManager)
|
||||
{
|
||||
var changesMade = false;
|
||||
foreach (var obsoleteFileExportPathToDelete in ObsoleteFileExportPathsToDelete)
|
||||
{
|
||||
var pathToDelete = MaxSdkUtils.GetAssetPathForExportPath(obsoleteFileExportPathToDelete);
|
||||
if (CheckExistence(pathToDelete))
|
||||
{
|
||||
MaxSdkLogger.UserDebug("Deleting obsolete file '" + pathToDelete + "' that is no longer needed.");
|
||||
FileUtil.DeleteFileOrDirectory(pathToDelete);
|
||||
changesMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
var pluginParentDir = AppLovinIntegrationManager.PluginParentDirectory;
|
||||
// Check if any obsolete networks are installed
|
||||
foreach (var obsoleteNetwork in ObsoleteNetworks)
|
||||
{
|
||||
var networkDir = Path.Combine(pluginParentDir, "MaxSdk/Mediation/" + obsoleteNetwork);
|
||||
if (CheckExistence(networkDir))
|
||||
{
|
||||
MaxSdkLogger.UserDebug("Deleting obsolete network " + obsoleteNetwork + " from path " + networkDir + "...");
|
||||
FileUtil.DeleteFileOrDirectory(networkDir);
|
||||
FileUtil.DeleteFileOrDirectory(networkDir + ".meta");
|
||||
changesMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh UI
|
||||
if (changesMade)
|
||||
{
|
||||
AssetDatabase.Refresh();
|
||||
MaxSdkLogger.UserDebug("Obsolete networks and files removed.");
|
||||
}
|
||||
}
|
||||
|
||||
AppLovinAutoUpdater.Update();
|
||||
}
|
||||
|
||||
private static bool CheckExistence(string location)
|
||||
{
|
||||
return File.Exists(location) ||
|
||||
Directory.Exists(location) ||
|
||||
(location.EndsWith("/*") && Directory.Exists(Path.GetDirectoryName(location)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b7dce7fe193a4058bc51d9f4d3a2aed
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,511 @@
|
||||
//
|
||||
// MaxIntegrationManager.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 6/1/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using AppLovinMax.Internal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
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]
|
||||
public class Network
|
||||
{
|
||||
//
|
||||
// Sample network data:
|
||||
//
|
||||
// {
|
||||
// "Name": "adcolony",
|
||||
// "DisplayName": "AdColony",
|
||||
// "DownloadUrl": "https://bintray.com/applovin/Unity-Mediation-Packages/download_file?file_path=AppLovin-AdColony-Adapters-Android-3.3.10.1-iOS-3.3.7.2.unitypackage",
|
||||
// "PluginFileName": "AppLovin-AdColony-Adapters-Android-3.3.10.1-iOS-3.3.7.2.unitypackage",
|
||||
// "DependenciesFilePath": "MaxSdk/Mediation/AdColony/Editor/Dependencies.xml",
|
||||
// "LatestVersions" : {
|
||||
// "Unity": "android_3.3.10.1_ios_3.3.7.2",
|
||||
// "Android": "3.3.10.1",
|
||||
// "Ios": "3.3.7.2"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
// ReSharper disable InconsistentNaming - Consistent with JSON data.
|
||||
public string Name;
|
||||
public string DisplayName;
|
||||
public string DownloadUrl;
|
||||
public string DependenciesFilePath;
|
||||
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;
|
||||
[NonSerialized] public bool IsCurrentlyInstalling;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DynamicLibraryToEmbed
|
||||
{
|
||||
// ReSharper disable InconsistentNaming - Consistent with JSON data.
|
||||
public string PodName;
|
||||
public string[] FrameworkNames;
|
||||
|
||||
// Min and max versions are inclusive, so if the adapter is the min or max version, the xcframework will get embedded.
|
||||
public string MinVersion;
|
||||
public string MaxVersion;
|
||||
|
||||
public DynamicLibraryToEmbed(string podName, string[] frameworkNames, string minVersion, string maxVersion)
|
||||
{
|
||||
PodName = podName;
|
||||
FrameworkNames = frameworkNames;
|
||||
MinVersion = minVersion;
|
||||
MaxVersion = maxVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Severity
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Alert
|
||||
{
|
||||
public string SeverityType;
|
||||
public string Title;
|
||||
public string Message;
|
||||
public string Url;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
public override bool Equals(object value)
|
||||
{
|
||||
var versions = value as Versions;
|
||||
|
||||
return versions != null
|
||||
&& Unity.Equals(versions.Unity)
|
||||
&& (Android == null || Android.Equals(versions.Android))
|
||||
&& (Ios == null || Ios.Equals(versions.Ios));
|
||||
}
|
||||
|
||||
public bool HasEqualSdkVersions(Versions versions)
|
||||
{
|
||||
return versions != null
|
||||
&& AdapterSdkVersion(Android).Equals(AdapterSdkVersion(versions.Android))
|
||||
&& AdapterSdkVersion(Ios).Equals(AdapterSdkVersion(versions.Ios));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return new {unity = Unity, android = Android, ios = Ios}.GetHashCode();
|
||||
}
|
||||
|
||||
private static string AdapterSdkVersion(string adapterVersion)
|
||||
{
|
||||
if (string.IsNullOrEmpty(adapterVersion)) return "";
|
||||
|
||||
var index = adapterVersion.LastIndexOf(".", StringComparison.Ordinal);
|
||||
return index > 0 ? adapterVersion.Substring(0, index) : adapterVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A manager class for MAX integration manager window.
|
||||
/// </summary>
|
||||
public class AppLovinIntegrationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to be called when a plugin package's import is started.
|
||||
/// </summary>
|
||||
internal delegate void ImportPackageStartedCallback(Network network);
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
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 const string PluginDataEndpoint = "https://unity.applovin.com/max/1.0/integration_manager_info?plugin_version={0}";
|
||||
|
||||
private static string externalDependencyManagerVersion;
|
||||
|
||||
internal static ImportPackageStartedCallback OnImportPackageStartedCallback;
|
||||
internal static ImportPackageCompletedCallback OnImportPackageCompletedCallback;
|
||||
|
||||
private MaxWebRequest maxWebRequest;
|
||||
private Network importingNetwork;
|
||||
|
||||
/// <summary>
|
||||
/// An Instance of the Integration manager.
|
||||
/// </summary>
|
||||
public static AppLovinIntegrationManager Instance
|
||||
{
|
||||
get { return instance; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parent directory path where the MaxSdk plugin directory is placed.
|
||||
/// </summary>
|
||||
public static string PluginParentDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
// 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.
|
||||
return maxSdkScriptAssetPath.Replace(MaxSdkAssetExportPath, "")
|
||||
.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
public static bool IsPluginInPackageManager
|
||||
{
|
||||
get { return PluginParentDirectory.StartsWith("Packages"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not gradle build system is enabled.
|
||||
/// </summary>
|
||||
public static bool GradleBuildEnabled
|
||||
{
|
||||
get { return GetEditorUserBuildSetting("androidBuildSystem", "").ToString().Equals("Gradle"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not Gradle template is enabled.
|
||||
/// </summary>
|
||||
public static bool GradleTemplateEnabled
|
||||
{
|
||||
get { return GradleBuildEnabled && File.Exists(GradleTemplatePath); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the Quality Service settings can be processed which requires Gradle template enabled or Unity IDE newer than version 2018_2.
|
||||
/// </summary>
|
||||
public static bool CanProcessAndroidQualityServiceSettings
|
||||
{
|
||||
get { return GradleTemplateEnabled || GradleBuildEnabled; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The External Dependency Manager version obtained dynamically.
|
||||
/// </summary>
|
||||
public static string ExternalDependencyManagerVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MaxSdkUtils.IsValidString(externalDependencyManagerVersion)) return externalDependencyManagerVersion;
|
||||
|
||||
try
|
||||
{
|
||||
var versionHandlerVersionNumberType = Type.GetType("Google.VersionHandlerVersionNumber, Google.VersionHandlerImpl");
|
||||
externalDependencyManagerVersion = versionHandlerVersionNumberType.GetProperty("Value").GetValue(null, null).ToString();
|
||||
}
|
||||
#pragma warning disable 0168
|
||||
catch (Exception ignored)
|
||||
#pragma warning restore 0168
|
||||
{
|
||||
externalDependencyManagerVersion = "Failed to get version.";
|
||||
}
|
||||
|
||||
return externalDependencyManagerVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private AppLovinIntegrationManager()
|
||||
{
|
||||
AssetDatabase.importPackageStarted += packageName =>
|
||||
{
|
||||
if (!IsImportingNetwork(packageName)) return;
|
||||
|
||||
CallImportPackageStartedCallback(importingNetwork);
|
||||
};
|
||||
|
||||
// Add asset import callbacks.
|
||||
AssetDatabase.importPackageCompleted += packageName =>
|
||||
{
|
||||
if (!IsImportingNetwork(packageName)) return;
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
CallImportPackageCompletedCallback(importingNetwork);
|
||||
importingNetwork = null;
|
||||
};
|
||||
|
||||
AssetDatabase.importPackageCancelled += packageName =>
|
||||
{
|
||||
if (!IsImportingNetwork(packageName)) return;
|
||||
|
||||
importingNetwork = null;
|
||||
};
|
||||
|
||||
AssetDatabase.importPackageFailed += (packageName, errorMessage) =>
|
||||
{
|
||||
if (!IsImportingNetwork(packageName)) return;
|
||||
|
||||
MaxSdkLogger.UserError(errorMessage);
|
||||
importingNetwork = null;
|
||||
};
|
||||
}
|
||||
|
||||
static AppLovinIntegrationManager() { }
|
||||
|
||||
public static PluginData LoadPluginDataSync()
|
||||
{
|
||||
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
|
||||
var webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
EndPoint = url,
|
||||
};
|
||||
|
||||
var maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
var webResponse = maxWebRequest.SendSync();
|
||||
|
||||
return CreatePluginDataFromWebResponse(webResponse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the plugin data to be display by integration manager window.
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback to be called once the plugin data download completes.</param>
|
||||
public IEnumerator LoadPluginData(Action<PluginData> callback)
|
||||
{
|
||||
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
|
||||
var webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
EndPoint = url,
|
||||
};
|
||||
|
||||
maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
yield return maxWebRequest.Send(webResponse =>
|
||||
{
|
||||
var pluginData = CreatePluginDataFromWebResponse(webResponse);
|
||||
callback(pluginData);
|
||||
});
|
||||
}
|
||||
|
||||
private static PluginData CreatePluginDataFromWebResponse(WebResponse webResponse)
|
||||
{
|
||||
if (!webResponse.IsSuccess)
|
||||
{
|
||||
MaxSdkLogger.E("Failed to load plugin data. Please check your internet connection.");
|
||||
return null;
|
||||
}
|
||||
|
||||
PluginData pluginData;
|
||||
try
|
||||
{
|
||||
pluginData = JsonUtility.FromJson<PluginData>(webResponse.ResponseMessage);
|
||||
AppLovinPackageManager.PluginData = pluginData;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
pluginData = null;
|
||||
}
|
||||
|
||||
if (pluginData == null) return null;
|
||||
|
||||
// Get current version of the plugin
|
||||
var appLovinMax = pluginData.AppLovinMax;
|
||||
AppLovinPackageManager.UpdateCurrentVersions(appLovinMax);
|
||||
|
||||
// Get current versions for all the mediation networks.
|
||||
foreach (var network in pluginData.MediatedNetworks)
|
||||
{
|
||||
AppLovinPackageManager.UpdateCurrentVersions(network);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the plugin file for a given network.
|
||||
/// </summary>
|
||||
/// <param name="network">Network for which to download the current version.</param>
|
||||
/// <param name="showImport">Whether or not to show the import window when downloading. Defaults to <c>true</c>.</param>
|
||||
/// <returns></returns>
|
||||
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 webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
DownloadHandler = new DownloadHandlerFile(path),
|
||||
EndPoint = network.DownloadUrl
|
||||
};
|
||||
|
||||
maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
yield return maxWebRequest.Send(webResponse =>
|
||||
{
|
||||
if (webResponse.IsSuccess)
|
||||
{
|
||||
importingNetwork = network;
|
||||
AssetDatabase.ImportPackage(path, showImport);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to download plugin package: " + webResponse.ErrorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the plugin download if one is in progress.
|
||||
/// </summary>
|
||||
public void CancelDownload()
|
||||
{
|
||||
if (maxWebRequest == null) return;
|
||||
|
||||
maxWebRequest.Abort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a dialog to the user with the given message and logs the error message to console.
|
||||
/// </summary>
|
||||
/// <param name="message">The failure message to be shown to the user.</param>
|
||||
public static void ShowBuildFailureDialog(string message)
|
||||
{
|
||||
var openIntegrationManager = EditorUtility.DisplayDialog("AppLovin MAX", message, "Open Integration Manager", "Dismiss");
|
||||
if (openIntegrationManager)
|
||||
{
|
||||
AppLovinIntegrationManagerWindow.ShowManager();
|
||||
}
|
||||
|
||||
MaxSdkLogger.UserError(message);
|
||||
}
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether or not the given package name is the currently importing package.
|
||||
/// </summary>
|
||||
/// <param name="packageName">The name of the package that needs to be checked.</param>
|
||||
/// <returns>true if the importing package matches the given package name.</returns>
|
||||
private bool IsImportingNetwork(string packageName)
|
||||
{
|
||||
// Note: The pluginName doesn't have the '.unitypackage' extension included in its name but the pluginFileName does. So using Contains instead of Equals.
|
||||
return importingNetwork != null && GetPluginFileName(importingNetwork).Contains(packageName);
|
||||
}
|
||||
|
||||
private static void CallImportPackageStartedCallback(Network network)
|
||||
{
|
||||
if (OnImportPackageStartedCallback == null) return;
|
||||
|
||||
OnImportPackageStartedCallback(network);
|
||||
}
|
||||
|
||||
private static void CallImportPackageCompletedCallback(Network network)
|
||||
{
|
||||
if (OnImportPackageCompletedCallback == null) return;
|
||||
|
||||
OnImportPackageCompletedCallback(network);
|
||||
}
|
||||
|
||||
private static object GetEditorUserBuildSetting(string name, object defaultValue)
|
||||
{
|
||||
var editorUserBuildSettingsType = typeof(EditorUserBuildSettings);
|
||||
var property = editorUserBuildSettingsType.GetProperty(name);
|
||||
if (property != null)
|
||||
{
|
||||
var value = property.GetValue(null, null);
|
||||
if (value != null) return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static string GetPluginFileName(Network network)
|
||||
{
|
||||
return network.Name.ToLowerInvariant() + "_" + network.LatestVersions.Unity + ".unitypackage";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5b874c1e65274159bcc0dc15d9458cc
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
public static class AppLovinIntegrationManagerUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares AppLovin MAX Unity mediation adapter plugin versions. Returns <see cref="MaxSdkUtils.VersionComparisonResult.Lesser"/>, <see cref="MaxSdkUtils.VersionComparisonResult.Equal"/>,
|
||||
/// or <see cref="MaxSdkUtils.VersionComparisonResult.Greater"/> as the first version is less than, equal to, or greater than the second.
|
||||
///
|
||||
/// If a version for a specific platform is only present in one of the provided versions, the one that contains it is considered newer.
|
||||
/// </summary>
|
||||
/// <param name="versionA">The first version to be compared.</param>
|
||||
/// <param name="versionB">The second version to be compared.</param>
|
||||
/// <returns>
|
||||
/// <see cref="MaxSdkUtils.VersionComparisonResult.Lesser"/> if versionA is less than versionB.
|
||||
/// <see cref="MaxSdkUtils.VersionComparisonResult.Equal"/> if versionA and versionB are equal.
|
||||
/// <see cref="MaxSdkUtils.VersionComparisonResult.Greater"/> if versionA is greater than versionB.
|
||||
/// </returns>
|
||||
internal static MaxSdkUtils.VersionComparisonResult CompareUnityMediationVersions(string versionA, string versionB)
|
||||
{
|
||||
if (versionA.Equals(versionB)) return MaxSdkUtils.VersionComparisonResult.Equal;
|
||||
|
||||
// Unity version would be of format: android_w.x.y.z_ios_a.b.c.d
|
||||
// For Android only versions it would be: android_w.x.y.z
|
||||
// For iOS only version it would be: ios_a.b.c.d
|
||||
|
||||
// After splitting into their respective components, the versions would be at the odd indices.
|
||||
var versionAComponents = versionA.Split('_').ToList();
|
||||
var versionBComponents = versionB.Split('_').ToList();
|
||||
|
||||
var androidComparison = MaxSdkUtils.VersionComparisonResult.Equal;
|
||||
if (versionA.Contains("android") && versionB.Contains("android"))
|
||||
{
|
||||
var androidVersionA = versionAComponents[1];
|
||||
var androidVersionB = versionBComponents[1];
|
||||
androidComparison = MaxSdkUtils.CompareVersions(androidVersionA, androidVersionB);
|
||||
|
||||
// Remove the Android version component so that iOS versions can be processed.
|
||||
versionAComponents.RemoveRange(0, 2);
|
||||
versionBComponents.RemoveRange(0, 2);
|
||||
}
|
||||
else if (versionA.Contains("android"))
|
||||
{
|
||||
androidComparison = MaxSdkUtils.VersionComparisonResult.Greater;
|
||||
|
||||
// Remove the Android version component so that iOS versions can be processed.
|
||||
versionAComponents.RemoveRange(0, 2);
|
||||
}
|
||||
else if (versionB.Contains("android"))
|
||||
{
|
||||
androidComparison = MaxSdkUtils.VersionComparisonResult.Lesser;
|
||||
|
||||
// Remove the Android version component so that iOS version can be processed.
|
||||
versionBComponents.RemoveRange(0, 2);
|
||||
}
|
||||
|
||||
var iosComparison = MaxSdkUtils.VersionComparisonResult.Equal;
|
||||
if (versionA.Contains("ios") && versionB.Contains("ios"))
|
||||
{
|
||||
var iosVersionA = versionAComponents[1];
|
||||
var iosVersionB = versionBComponents[1];
|
||||
iosComparison = MaxSdkUtils.CompareVersions(iosVersionA, iosVersionB);
|
||||
}
|
||||
else if (versionA.Contains("ios"))
|
||||
{
|
||||
iosComparison = MaxSdkUtils.VersionComparisonResult.Greater;
|
||||
}
|
||||
else if (versionB.Contains("ios"))
|
||||
{
|
||||
iosComparison = MaxSdkUtils.VersionComparisonResult.Lesser;
|
||||
}
|
||||
|
||||
// If either one of the Android or iOS version is greater, the entire version should be greater.
|
||||
return (androidComparison == MaxSdkUtils.VersionComparisonResult.Greater || iosComparison == MaxSdkUtils.VersionComparisonResult.Greater) ? MaxSdkUtils.VersionComparisonResult.Greater : MaxSdkUtils.VersionComparisonResult.Lesser;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96efb4dba39eb48d2a60afab69786e6a
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84c61b4c05e114cec91df447d1b585f8
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// AppLovinInternalSettings.cs
|
||||
// AppLovin User Engagement Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 9/15/22.
|
||||
// Copyright © 2022 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ScriptableObject"/> representing the AppLovin internal settings that can be set in the Integration Manager Window.
|
||||
///
|
||||
/// The scriptable object asset is saved under ProjectSettings as <c>AppLovinInternalSettings.json</c>.
|
||||
/// </summary>
|
||||
public class AppLovinInternalSettings : ScriptableObject
|
||||
{
|
||||
private static AppLovinInternalSettings instance;
|
||||
|
||||
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;
|
||||
[SerializeField] private bool userTrackingUsageLocalizationEnabled;
|
||||
[SerializeField] private string userTrackingUsageDescriptionDe = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionEs = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionFr = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionJa = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionKo = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionZhHans = string.Empty;
|
||||
[SerializeField] private string userTrackingUsageDescriptionZhHant = string.Empty;
|
||||
|
||||
private const string SettingsFilePath = "ProjectSettings/AppLovinInternalSettings.json";
|
||||
|
||||
public static AppLovinInternalSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance != null) return instance;
|
||||
|
||||
instance = CreateInstance<AppLovinInternalSettings>();
|
||||
|
||||
var projectRootPath = Path.GetDirectoryName(Application.dataPath);
|
||||
var settingsFilePath = Path.Combine(projectRootPath, SettingsFilePath);
|
||||
if (!File.Exists(settingsFilePath))
|
||||
{
|
||||
instance.Save();
|
||||
return instance;
|
||||
}
|
||||
|
||||
var settingsJson = File.ReadAllText(settingsFilePath);
|
||||
if (string.IsNullOrEmpty(settingsJson))
|
||||
{
|
||||
instance.Save();
|
||||
return instance;
|
||||
}
|
||||
|
||||
JsonUtility.FromJsonOverwrite(settingsJson, instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var settingsJson = JsonUtility.ToJson(instance);
|
||||
try
|
||||
{
|
||||
var projectRootPath = Path.GetDirectoryName(Application.dataPath);
|
||||
var settingsFilePath = Path.Combine(projectRootPath, SettingsFilePath);
|
||||
File.WriteAllText(settingsFilePath, settingsJson);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to save internal settings.");
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not AppLovin Consent Flow is enabled.
|
||||
/// </summary>
|
||||
public bool ConsentFlowEnabled
|
||||
{
|
||||
get { return consentFlowEnabled; }
|
||||
set
|
||||
{
|
||||
var previousValue = consentFlowEnabled;
|
||||
consentFlowEnabled = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
// If the value didn't change, we don't need to update anything.
|
||||
if (previousValue) return;
|
||||
|
||||
UserTrackingUsageDescriptionEn = DefaultUserTrackingDescriptionEn;
|
||||
UserTrackingUsageLocalizationEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsentFlowPrivacyPolicyUrl = string.Empty;
|
||||
ConsentFlowTermsOfServiceUrl = string.Empty;
|
||||
UserTrackingUsageDescriptionEn = string.Empty;
|
||||
UserTrackingUsageLocalizationEnabled = false;
|
||||
OverrideDefaultUserTrackingUsageDescriptions = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A URL pointing to the Privacy Policy for the app to be shown when prompting the user for consent.
|
||||
/// </summary>
|
||||
public string ConsentFlowPrivacyPolicyUrl
|
||||
{
|
||||
get { return consentFlowPrivacyPolicyUrl; }
|
||||
set { consentFlowPrivacyPolicyUrl = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional URL pointing to the Terms of Service for the app to be shown when prompting the user for consent.
|
||||
/// </summary>
|
||||
public string ConsentFlowTermsOfServiceUrl
|
||||
{
|
||||
get { return consentFlowTermsOfServiceUrl; }
|
||||
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 href="https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription">Apple's documentation</see>.
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionEn
|
||||
{
|
||||
get { return userTrackingUsageDescriptionEn; }
|
||||
set { userTrackingUsageDescriptionEn = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional string to set debug user geography
|
||||
/// </summary>
|
||||
public MaxSdkBase.ConsentFlowUserGeography DebugUserGeography
|
||||
{
|
||||
get { return debugUserGeography; }
|
||||
set { debugUserGeography = value; }
|
||||
}
|
||||
|
||||
public bool OverrideDefaultUserTrackingUsageDescriptions
|
||||
{
|
||||
get { return overrideDefaultUserTrackingUsageDescriptions; }
|
||||
set
|
||||
{
|
||||
var previousValue = overrideDefaultUserTrackingUsageDescriptions;
|
||||
overrideDefaultUserTrackingUsageDescriptions = value;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
if (!previousValue) return;
|
||||
|
||||
UserTrackingUsageDescriptionEn = DefaultUserTrackingDescriptionEn;
|
||||
UserTrackingUsageDescriptionDe = DefaultUserTrackingDescriptionDe;
|
||||
UserTrackingUsageDescriptionEs = DefaultUserTrackingDescriptionEs;
|
||||
UserTrackingUsageDescriptionFr = DefaultUserTrackingDescriptionFr;
|
||||
UserTrackingUsageDescriptionJa = DefaultUserTrackingDescriptionJa;
|
||||
UserTrackingUsageDescriptionKo = DefaultUserTrackingDescriptionKo;
|
||||
UserTrackingUsageDescriptionZhHans = DefaultUserTrackingDescriptionZhHans;
|
||||
UserTrackingUsageDescriptionZhHant = DefaultUserTrackingDescriptionZhHant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to localize User Tracking Usage Description.
|
||||
/// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public bool UserTrackingUsageLocalizationEnabled
|
||||
{
|
||||
get { return userTrackingUsageLocalizationEnabled; }
|
||||
set
|
||||
{
|
||||
var previousValue = userTrackingUsageLocalizationEnabled;
|
||||
userTrackingUsageLocalizationEnabled = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
// If the value didn't change, don't do anything
|
||||
if (previousValue) return;
|
||||
|
||||
// Don't set the default values if they are being overriden.
|
||||
if (OverrideDefaultUserTrackingUsageDescriptions) return;
|
||||
|
||||
UserTrackingUsageDescriptionDe = DefaultUserTrackingDescriptionDe;
|
||||
UserTrackingUsageDescriptionEs = DefaultUserTrackingDescriptionEs;
|
||||
UserTrackingUsageDescriptionFr = DefaultUserTrackingDescriptionFr;
|
||||
UserTrackingUsageDescriptionJa = DefaultUserTrackingDescriptionJa;
|
||||
UserTrackingUsageDescriptionKo = DefaultUserTrackingDescriptionKo;
|
||||
UserTrackingUsageDescriptionZhHans = DefaultUserTrackingDescriptionZhHans;
|
||||
UserTrackingUsageDescriptionZhHant = DefaultUserTrackingDescriptionZhHant;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserTrackingUsageDescriptionDe = string.Empty;
|
||||
UserTrackingUsageDescriptionEs = string.Empty;
|
||||
UserTrackingUsageDescriptionFr = string.Empty;
|
||||
UserTrackingUsageDescriptionJa = string.Empty;
|
||||
UserTrackingUsageDescriptionKo = string.Empty;
|
||||
UserTrackingUsageDescriptionZhHans = string.Empty;
|
||||
UserTrackingUsageDescriptionZhHant = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionDe
|
||||
{
|
||||
get { return userTrackingUsageDescriptionDe; }
|
||||
set { userTrackingUsageDescriptionDe = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionEs
|
||||
{
|
||||
get { return userTrackingUsageDescriptionEs; }
|
||||
set { userTrackingUsageDescriptionEs = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionFr
|
||||
{
|
||||
get { return userTrackingUsageDescriptionFr; }
|
||||
set { userTrackingUsageDescriptionFr = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionJa
|
||||
{
|
||||
get { return userTrackingUsageDescriptionJa; }
|
||||
set { userTrackingUsageDescriptionJa = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionKo
|
||||
{
|
||||
get { return userTrackingUsageDescriptionKo; }
|
||||
set { userTrackingUsageDescriptionKo = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionZhHans
|
||||
{
|
||||
get { return userTrackingUsageDescriptionZhHans; }
|
||||
set { userTrackingUsageDescriptionZhHans = value; }
|
||||
}
|
||||
|
||||
/// <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 Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
|
||||
/// </summary>
|
||||
public string UserTrackingUsageDescriptionZhHant
|
||||
{
|
||||
get { return userTrackingUsageDescriptionZhHant; }
|
||||
set { userTrackingUsageDescriptionZhHant = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65c51e21887ae42c2839962fb9585e9f
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// MaxMenuItems.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 5/27/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
public static class AppLovinMenuItems
|
||||
{
|
||||
/**
|
||||
* The special characters at the end represent a shortcut for this action.
|
||||
*
|
||||
* % - ctrl on Windows, cmd on macOS
|
||||
* # - shift
|
||||
* & - alt
|
||||
*
|
||||
* So, (shift + cmd/ctrl + i) will launch the integration manager
|
||||
*/
|
||||
[MenuItem("AppLovin/Integration Manager %#i")]
|
||||
private static void IntegrationManager()
|
||||
{
|
||||
ShowIntegrationManager();
|
||||
}
|
||||
|
||||
[MenuItem("AppLovin/Documentation")]
|
||||
private static void Documentation()
|
||||
{
|
||||
Application.OpenURL("https://developers.applovin.com/en/unity/overview/integration");
|
||||
}
|
||||
|
||||
[MenuItem("AppLovin/Contact Us")]
|
||||
private static void ContactUs()
|
||||
{
|
||||
Application.OpenURL("https://www.applovin.com/contact/");
|
||||
}
|
||||
|
||||
[MenuItem("AppLovin/About")]
|
||||
private static void About()
|
||||
{
|
||||
Application.OpenURL("https://www.applovin.com/about/");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/AppLovin Integration Manager")]
|
||||
private static void AssetsIntegrationManager()
|
||||
{
|
||||
ShowIntegrationManager();
|
||||
}
|
||||
|
||||
private static void ShowIntegrationManager()
|
||||
{
|
||||
AppLovinIntegrationManagerWindow.ShowManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02c2d277874f649d18a59d382420bf65
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,626 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if !UNITY_2020_1_OR_NEWER
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using System.Xml.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
[Serializable]
|
||||
public class PackageInfo
|
||||
{
|
||||
// ReSharper disable InconsistentNaming - For JSON Deserialization
|
||||
public string Name;
|
||||
public string Version;
|
||||
}
|
||||
|
||||
public interface IPackageManagerClient
|
||||
{
|
||||
IEnumerator AddNetwork(Network network, bool showImport);
|
||||
void RemoveNetwork(Network network);
|
||||
}
|
||||
|
||||
public static class AppLovinPackageManager
|
||||
{
|
||||
private const string AppLovinMediationAmazonAdapterDependenciesPath = "Amazon/Scripts/Mediations/AppLovinMediation/Editor/Dependencies.xml";
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
private static readonly IPackageManagerClient _upmPackageManager = new AppLovinUpmPackageManager();
|
||||
#endif
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
internal static PluginData PluginData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether or not an adapter with the given version or newer exists.
|
||||
/// </summary>
|
||||
/// <param name="adapterName">The name of the network (the root adapter folder name in "MaxSdk/Mediation/" folder.</param>
|
||||
/// <param name="iosVersion">The min iOS adapter version to check for. Can be <c>null</c> if we want to check for any version.</param>
|
||||
/// <param name="androidVersion">The min android adapter version to check for. Can be <c>null</c> if we want to check for any version.</param>
|
||||
/// <returns><c>true</c> if an adapter with the min version is installed.</returns>
|
||||
internal static bool IsAdapterInstalled(string adapterName, string iosVersion = null, string androidVersion = null)
|
||||
{
|
||||
var dependencyFilePathList = GetAssetPathListForExportPath("MaxSdk/Mediation/" + adapterName + "/Editor/Dependencies.xml");
|
||||
if (dependencyFilePathList.Count <= 0) return false;
|
||||
|
||||
var currentVersion = GetCurrentVersions(dependencyFilePathList);
|
||||
if (iosVersion != null)
|
||||
{
|
||||
var iosVersionComparison = MaxSdkUtils.CompareVersions(currentVersion.Ios, iosVersion);
|
||||
if (iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Lesser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (androidVersion != null)
|
||||
{
|
||||
var androidVersionComparison = MaxSdkUtils.CompareVersions(currentVersion.Android, androidVersion);
|
||||
if (androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Lesser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an adapter is installed using the plugin data.
|
||||
/// </summary>
|
||||
/// <param name="pluginData">The plugin data to check for the adapter</param>
|
||||
/// <param name="adapterName">The name of the network.</param>
|
||||
/// <returns>Whether an adapter is installed in the plugin data</returns>
|
||||
internal static bool IsAdapterInstalled(PluginData pluginData, string adapterName)
|
||||
{
|
||||
var network = pluginData.MediatedNetworks.Where(mediatedNetwork => mediatedNetwork.Name.Equals(adapterName)).ToList().FirstOrDefault();
|
||||
var networkVersion = network != null ? network.CurrentVersions : null;
|
||||
var currentVersion = networkVersion != null ? networkVersion.Unity : "";
|
||||
|
||||
return MaxSdkUtils.IsValidString(currentVersion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mediation networks that are currently installed in the project. If using UPM, checks
|
||||
/// for networks in Packages folder and Mediation folder in case a custom adapter was added to the project.
|
||||
/// </summary>
|
||||
/// <returns>A list of the installed mediation network names.</returns>
|
||||
internal static List<string> GetInstalledMediationNetworks()
|
||||
{
|
||||
var installedNetworks = new List<string>();
|
||||
var installedNetworksInAssets = AppLovinAssetsPackageManager.GetInstalledMediationNetworks();
|
||||
installedNetworks.AddRange(installedNetworksInAssets);
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
var installedNetworksInPackages = AppLovinUpmPackageManager.GetInstalledMediationNetworks();
|
||||
installedNetworks.AddRange(installedNetworksInPackages);
|
||||
#endif
|
||||
|
||||
if (IsAmazonAppLovinAdapterInstalled())
|
||||
{
|
||||
installedNetworks.Add("AmazonAdMarketplace");
|
||||
}
|
||||
|
||||
return installedNetworks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a network to the project.
|
||||
/// </summary>
|
||||
/// <param name="network">The network to add.</param>
|
||||
/// <param name="showImport">Whether to show the import window (only for non UPM)</param>
|
||||
internal static IEnumerator AddNetwork(Network network, bool showImport)
|
||||
{
|
||||
yield return PackageManagerClient.AddNetwork(network, showImport);
|
||||
|
||||
AppLovinEditorCoroutine.StartCoroutine(RefreshAssetsAtEndOfFrame(network));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a network from the project.
|
||||
/// </summary>
|
||||
/// <param name="network">The network to remove.</param>
|
||||
internal static void RemoveNetwork(Network network)
|
||||
{
|
||||
PackageManagerClient.RemoveNetwork(network);
|
||||
|
||||
AppLovinEditorCoroutine.StartCoroutine(RefreshAssetsAtEndOfFrame(network));
|
||||
}
|
||||
|
||||
#region Utility
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all asset paths for a given MAX plugin export path.
|
||||
/// </summary>
|
||||
/// <param name="exportPath">The actual exported path of the asset.</param>
|
||||
/// <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-" + MaxSdkUtils.NormalizeToUnityPath(exportPath);
|
||||
var assetGuids = AssetDatabase.FindAssets(assetLabelToFind);
|
||||
|
||||
var assetPaths = new List<string>();
|
||||
foreach (var assetGuid in assetGuids)
|
||||
{
|
||||
assetPaths.Add(AssetDatabase.GUIDToAssetPath(assetGuid));
|
||||
}
|
||||
|
||||
return assetPaths.Count <= 0 ? new List<string>() : assetPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CurrentVersion fields for a given network data object.
|
||||
/// </summary>
|
||||
/// <param name="network">Network for which to update the current versions.</param>
|
||||
internal static void UpdateCurrentVersions(Network network)
|
||||
{
|
||||
var assetPaths = GetAssetPathListForExportPath(network.DependenciesFilePath);
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
if (HasDuplicateAdapters(assetPaths))
|
||||
{
|
||||
ShowDeleteDuplicateAdapterPrompt(network);
|
||||
}
|
||||
#endif
|
||||
|
||||
var currentVersions = GetCurrentVersions(assetPaths);
|
||||
network.CurrentVersions = currentVersions;
|
||||
|
||||
// If AppLovin mediation plugin, get the version from MaxSdk and the latest and current version comparison.
|
||||
if (network.Name.Equals("APPLOVIN_NETWORK"))
|
||||
{
|
||||
network.CurrentVersions.Unity = MaxSdk.Version;
|
||||
|
||||
var unityVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Unity, network.LatestVersions.Unity);
|
||||
var androidVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Android, network.LatestVersions.Android);
|
||||
var iosVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Ios, network.LatestVersions.Ios);
|
||||
|
||||
// Overall version is same if all the current and latest (from db) versions are same.
|
||||
if (unityVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal &&
|
||||
androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal &&
|
||||
iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal)
|
||||
{
|
||||
network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Equal;
|
||||
}
|
||||
// One of the installed versions is newer than the latest versions which means that the publisher is on a beta version.
|
||||
else if (unityVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater ||
|
||||
androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater ||
|
||||
iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater)
|
||||
{
|
||||
network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Greater;
|
||||
}
|
||||
// We have a new version available if all Android, iOS and Unity has a newer version available in db.
|
||||
else
|
||||
{
|
||||
network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Lesser;
|
||||
}
|
||||
}
|
||||
// For all other mediation adapters, get the version comparison using their Unity versions.
|
||||
else
|
||||
{
|
||||
// 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 (MaxSdkUtils.IsValidString(currentVersions.Unity))
|
||||
{
|
||||
network.CurrentToLatestVersionComparisonResult = AppLovinIntegrationManagerUtils.CompareUnityMediationVersions(currentVersions.Unity, network.LatestVersions.Unity);
|
||||
}
|
||||
|
||||
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.
|
||||
network.RequiresUpdate = comparisonResult < 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset value so that the Integration manager can hide the alert icon once adapter is updated.
|
||||
network.RequiresUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
/// <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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="dependencyPaths">A list of dependency file paths to extract current versions from.</param>
|
||||
/// <returns>Current versions of a given network's dependency files.</returns>
|
||||
private static Versions GetCurrentVersions(List<string> dependencyPaths)
|
||||
{
|
||||
var currentVersions = new Versions();
|
||||
foreach (var dependencyPath in dependencyPaths)
|
||||
{
|
||||
GetCurrentVersion(currentVersions, dependencyPath);
|
||||
}
|
||||
|
||||
if (currentVersions.Android != null && currentVersions.Ios != null)
|
||||
{
|
||||
currentVersions.Unity = "android_" + currentVersions.Android + "_ios_" + currentVersions.Ios;
|
||||
}
|
||||
else if (currentVersions.Android != null)
|
||||
{
|
||||
currentVersions.Unity = "android_" + currentVersions.Android;
|
||||
}
|
||||
else if (currentVersions.Ios != null)
|
||||
{
|
||||
currentVersions.Unity = "ios_" + currentVersions.Ios;
|
||||
}
|
||||
|
||||
return currentVersions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the current version of a network from its dependency.xml file.
|
||||
/// </summary>
|
||||
/// <param name="currentVersions">The Versions object we are using.</param>
|
||||
/// <param name="dependencyPath">The path to the dependency.xml file.</param>
|
||||
private static void GetCurrentVersion(Versions currentVersions, string dependencyPath)
|
||||
{
|
||||
XDocument dependency;
|
||||
try
|
||||
{
|
||||
dependency = XDocument.Load(dependencyPath);
|
||||
}
|
||||
#pragma warning disable 0168
|
||||
catch (IOException exception)
|
||||
#pragma warning restore 0168
|
||||
{
|
||||
// Couldn't find the dependencies file. The plugin is not installed.
|
||||
return;
|
||||
}
|
||||
|
||||
// <dependencies>
|
||||
// <androidPackages>
|
||||
// <androidPackage spec="com.applovin.mediation:network_name-adapter:1.2.3.4" />
|
||||
// </androidPackages>
|
||||
// <iosPods>
|
||||
// <iosPod name="AppLovinMediationNetworkNameAdapter" version="2.3.4.5" />
|
||||
// </iosPods>
|
||||
// </dependencies>
|
||||
string androidVersion = null;
|
||||
string iosVersion = null;
|
||||
var dependenciesElement = dependency.Element("dependencies");
|
||||
if (dependenciesElement != null)
|
||||
{
|
||||
var androidPackages = dependenciesElement.Element("androidPackages");
|
||||
if (androidPackages != null)
|
||||
{
|
||||
var adapterPackage = androidPackages.Descendants().FirstOrDefault(element => element.Name.LocalName.Equals("androidPackage")
|
||||
&& element.FirstAttribute.Name.LocalName.Equals("spec")
|
||||
&& element.FirstAttribute.Value.StartsWith("com.applovin"));
|
||||
if (adapterPackage != null)
|
||||
{
|
||||
androidVersion = adapterPackage.FirstAttribute.Value.Split(':').Last();
|
||||
// Hack alert: Some Android versions might have square brackets to force a specific version. Remove them if they are detected.
|
||||
if (androidVersion.StartsWith("["))
|
||||
{
|
||||
androidVersion = androidVersion.Trim('[', ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var iosPods = dependenciesElement.Element("iosPods");
|
||||
if (iosPods != null)
|
||||
{
|
||||
var adapterPod = iosPods.Descendants().FirstOrDefault(element => element.Name.LocalName.Equals("iosPod")
|
||||
&& element.FirstAttribute.Name.LocalName.Equals("name")
|
||||
&& element.FirstAttribute.Value.StartsWith("AppLovin"));
|
||||
if (adapterPod != null)
|
||||
{
|
||||
iosVersion = adapterPod.Attributes().First(attribute => attribute.Name.LocalName.Equals("version")).Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (androidVersion != null)
|
||||
{
|
||||
currentVersions.Android = androidVersion;
|
||||
}
|
||||
|
||||
if (iosVersion != null)
|
||||
{
|
||||
currentVersions.Ios = iosVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for the Amazon AppLovin adapter in the project.
|
||||
/// </summary>
|
||||
/// <returns>Whether the AppLovin Adapter is installed through the Amazon SDK.</returns>
|
||||
private static bool IsAmazonAppLovinAdapterInstalled()
|
||||
{
|
||||
string[] dependenciesFiles = AssetDatabase.FindAssets("t:TextAsset Dependencies", new[] {"Assets"})
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.ToArray();
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh assets and update current versions after a slight delay to allow for Client.Resolve to finish.
|
||||
/// </summary>
|
||||
/// <param name="network">The network that was just installed/removed.</param>
|
||||
private static IEnumerator RefreshAssetsAtEndOfFrame(Network network)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
UpdateCurrentVersions(network);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
public class AppLovinUpmPackageManager : IPackageManagerClient
|
||||
{
|
||||
public const string PackageNamePrefixAppLovin = "com.applovin.mediation.ads";
|
||||
private const string PackageNamePrefixNetwork = "com.applovin.mediation.adapters";
|
||||
private const string PackageNamePrefixDsp = "com.applovin.mediation.dsp";
|
||||
|
||||
private const float TimeoutFetchPackageCollectionSeconds = 10f;
|
||||
|
||||
#if !UNITY_2020_1_OR_NEWER
|
||||
private static Type packageManagerClientType;
|
||||
private static MethodInfo packageManagerResolveMethod;
|
||||
#endif
|
||||
|
||||
public static List<string> GetInstalledMediationNetworks()
|
||||
{
|
||||
// Return empty list if we failed to get the package list
|
||||
var packageCollection = GetPackageCollectionSync(TimeoutFetchPackageCollectionSeconds);
|
||||
if (packageCollection == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
return packageCollection.Where(package => package.name.StartsWith(PackageNamePrefixNetwork) || package.name.StartsWith(PackageNamePrefixDsp))
|
||||
.SelectMany(package => package.keywords)
|
||||
.Where(keyword => keyword.StartsWith("dir:"))
|
||||
.Select(keyword => keyword.Replace("dir:", ""))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerator AddNetwork(Network network, bool showImport)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public void RemoveNetwork(Network network)
|
||||
{
|
||||
var appLovinManifest = AppLovinUpmManifest.Load();
|
||||
RemovePackages(network, appLovinManifest);
|
||||
appLovinManifest.Save();
|
||||
ResolvePackageManager();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a network's packages to the package manager removes any beta version that exists
|
||||
/// </summary>
|
||||
/// <param name="network">The network to add.</param>
|
||||
/// <param name="appLovinManifest">The AppLovinUpmManifest instance to edit</param>
|
||||
internal static void AddPackages(Network network, AppLovinUpmManifest appLovinManifest)
|
||||
{
|
||||
foreach (var packageInfo in network.Packages)
|
||||
{
|
||||
appLovinManifest.AddPackageDependency(packageInfo.Name, packageInfo.Version);
|
||||
RemoveBetaPackage(packageInfo.Name, appLovinManifest);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a network's packages from the package manager
|
||||
/// </summary>
|
||||
/// <param name="network">The network to add.</param>
|
||||
/// <param name="appLovinManifest">The AppLovinUpmManifest instance to edit</param>
|
||||
internal static void RemovePackages(Network network, AppLovinUpmManifest appLovinManifest)
|
||||
{
|
||||
foreach (var packageInfo in network.Packages)
|
||||
{
|
||||
appLovinManifest.RemovePackageDependency(packageInfo.Name);
|
||||
RemoveBetaPackage(packageInfo.Name, appLovinManifest);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the beta version of a package name
|
||||
/// </summary>
|
||||
/// <param name="packageName">The name of the package to remove a beta for</param>
|
||||
/// <param name="appLovinManifest">The AppLovinUpmManifest instance to edit</param>
|
||||
private static void RemoveBetaPackage(string packageName, AppLovinUpmManifest appLovinManifest)
|
||||
{
|
||||
var prefix = "";
|
||||
if (packageName.Contains(PackageNamePrefixNetwork))
|
||||
{
|
||||
prefix = PackageNamePrefixNetwork;
|
||||
}
|
||||
else if (packageName.Contains(PackageNamePrefixDsp))
|
||||
{
|
||||
prefix = PackageNamePrefixDsp;
|
||||
}
|
||||
else if (packageName.Contains(PackageNamePrefixAppLovin))
|
||||
{
|
||||
prefix = PackageNamePrefixAppLovin;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var betaPackageName = packageName.Replace(prefix, prefix + ".beta");
|
||||
appLovinManifest.RemovePackageDependency(betaPackageName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the Unity Package Manager so any changes made to the manifest.json file are reflected in the Unity Editor.
|
||||
/// </summary>
|
||||
internal static void ResolvePackageManager()
|
||||
{
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
Client.Resolve();
|
||||
#else
|
||||
packageManagerClientType = packageManagerClientType ?? typeof(Client);
|
||||
if (packageManagerClientType != null)
|
||||
{
|
||||
packageManagerResolveMethod = packageManagerResolveMethod ?? packageManagerClientType.GetMethod("Resolve", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
|
||||
if (packageManagerResolveMethod != null)
|
||||
{
|
||||
packageManagerResolveMethod.Invoke(null, null);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PackageCollection from the Unity Package Manager synchronously.
|
||||
/// </summary>
|
||||
/// <param name="timeoutSeconds">How long to wait before exiting with a timeout error</param>
|
||||
/// <returns></returns>
|
||||
private static PackageCollection GetPackageCollectionSync(float timeoutSeconds = -1)
|
||||
{
|
||||
var request = Client.List();
|
||||
|
||||
// Just wait till the request is complete
|
||||
var now = DateTime.Now;
|
||||
while (!request.IsCompleted)
|
||||
{
|
||||
// Wait indefinitely if there is no timeout set.
|
||||
if (timeoutSeconds < 0) continue;
|
||||
|
||||
var delta = DateTime.Now - now;
|
||||
if (delta.TotalSeconds > timeoutSeconds)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to list UPM packages: Timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!request.IsCompleted)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (request.Status >= StatusCode.Failure)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to list packages: " + request.Error.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (request.Status == StatusCode.Success) ? request.Result : null;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public class AppLovinAssetsPackageManager : IPackageManagerClient
|
||||
{
|
||||
public static List<string> GetInstalledMediationNetworks()
|
||||
{
|
||||
var maxMediationDirectory = AppLovinIntegrationManager.MediationDirectory;
|
||||
if (!Directory.Exists(maxMediationDirectory)) return new List<string>();
|
||||
|
||||
var mediationNetworkDirectories = Directory.GetDirectories(maxMediationDirectory);
|
||||
return mediationNetworkDirectories.Select(Path.GetFileName).ToList();
|
||||
}
|
||||
|
||||
public IEnumerator AddNetwork(Network network, bool showImport)
|
||||
{
|
||||
yield return AppLovinIntegrationManager.Instance.DownloadPlugin(network, showImport);
|
||||
}
|
||||
|
||||
public void RemoveNetwork(Network network)
|
||||
{
|
||||
foreach (var pluginFilePath in network.PluginFilePaths)
|
||||
{
|
||||
var filePath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, pluginFilePath);
|
||||
FileUtil.DeleteFileOrDirectory(filePath);
|
||||
FileUtil.DeleteFileOrDirectory(filePath + ".meta");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69faa9dfd9aac483daa24261a3e11206
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Moves our SDK Unity Plugin from under the Assets folder to the Unity Package Manager.
|
||||
/// </summary>
|
||||
public static class AppLovinPluginMigrationHelper
|
||||
{
|
||||
private const string ApplovinRegistryName = "AppLovin MAX Unity";
|
||||
private const string ApplovinRegistryUrl = "https://unity.packages.applovin.com/";
|
||||
private static readonly List<string> AppLovinRegistryScopes = new List<string>() {"com.applovin.mediation.ads", "com.applovin.mediation.adapters", "com.applovin.mediation.dsp"};
|
||||
|
||||
private const string OpenUpmRegistryName = "package.openupm.com";
|
||||
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>
|
||||
/// <param name="pluginData">The Unity Plugin data for our sdk and mediation adapters.</param>
|
||||
/// <param name="deleteExternalDependencyManager">Whether to delete the EDM folder under "Assets"</param>
|
||||
internal static void MigrateToUnityPackageManager(PluginData pluginData, bool deleteExternalDependencyManager)
|
||||
{
|
||||
MaxSdkLogger.UserDebug("Moving AppLovin Unity Plugin to package manager");
|
||||
|
||||
if (deleteExternalDependencyManager)
|
||||
{
|
||||
DeleteExternalDependencyManager();
|
||||
}
|
||||
|
||||
var appLovinManifest = AppLovinUpmManifest.Load();
|
||||
|
||||
MigrateAdapters(pluginData, appLovinManifest);
|
||||
MigratePlugin(pluginData, appLovinManifest);
|
||||
|
||||
appLovinManifest.Save();
|
||||
AppLovinUpmPackageManager.ResolvePackageManager();
|
||||
DeletePluginFiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add all currently installed networks to the manifest.
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the AppLovin scoped registry to the manifest if it doesn't exist. Otherwise update it.
|
||||
/// </summary>
|
||||
private static void MigratePlugin(PluginData pluginData, AppLovinUpmManifest appLovinManifest)
|
||||
{
|
||||
appLovinManifest.AddOrUpdateRegistry(ApplovinRegistryName, ApplovinRegistryUrl, AppLovinRegistryScopes);
|
||||
appLovinManifest.AddOrUpdateRegistry(OpenUpmRegistryName, OpenUpmRegistryUrl, OpenUpmRegistryScopes);
|
||||
|
||||
var appLovinVersion = pluginData.AppLovinMax.LatestVersions.Unity;
|
||||
appLovinManifest.AddPackageDependency(AppLovinUpmPackageManager.PackageNamePrefixAppLovin, appLovinVersion);
|
||||
}
|
||||
|
||||
#region Utility
|
||||
|
||||
/// <summary>
|
||||
/// Delete the external dependency manager folder from the project.
|
||||
/// </summary>
|
||||
private static void DeleteExternalDependencyManager()
|
||||
{
|
||||
var externalDependencyManagerPath = Path.Combine(Application.dataPath, "ExternalDependencyManager");
|
||||
FileUtil.DeleteFileOrDirectory(externalDependencyManagerPath);
|
||||
FileUtil.DeleteFileOrDirectory(externalDependencyManagerPath + ".meta");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all the files in the plugin directory except the AppLovinSettings.asset file.
|
||||
/// </summary>
|
||||
private static void DeletePluginFiles()
|
||||
{
|
||||
if (AppLovinIntegrationManager.IsPluginInPackageManager) return;
|
||||
|
||||
var pluginPath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, "MaxSdk");
|
||||
var appLovinSettingsPath = Path.Combine(pluginPath, "Resources/AppLovinSettings.asset");
|
||||
|
||||
var appLovinResourcesDirectory = Path.Combine(pluginPath, "Resources");
|
||||
var appLovinSettingsTempPath = Path.Combine(Path.GetTempPath(), "AppLovinSettings.asset");
|
||||
var appLovinMediationTempPath = Path.Combine(Path.GetTempPath(), "Mediation");
|
||||
|
||||
// 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
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11288612632de49b99708cdee436692c
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,497 @@
|
||||
//
|
||||
// MaxPostProcessBuildAndroid.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 4/10/20.
|
||||
// Copyright © 2020 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using AppLovinMax.ThirdParty.MiniJson;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Android;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// A post processor used to update the Android project once it is generated.
|
||||
/// </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";
|
||||
private const string KeyMetaDataGoogleOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
|
||||
private const string KeyMetaDataGoogleOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
|
||||
|
||||
private const string KeyMetaDataMobileFuseAutoInit = "com.mobilefuse.sdk.disable_auto_init";
|
||||
private const string KeyMetaDataMyTargetAutoInit = "com.my.target.autoInitMode";
|
||||
|
||||
private const string KeyMetaDataAppLovinSdkKey = "applovin.sdk.key";
|
||||
|
||||
private const string AppLovinSettingsFileName = "applovin_settings.json";
|
||||
|
||||
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";
|
||||
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
// To match "'com.android.library' version '7.3.1'" line in build.gradle
|
||||
private static readonly Regex TokenGradleVersionLibrary = new Regex(".*id ['\"]com\\.android\\.library['\"] version");
|
||||
private static readonly Regex TokenGradleVersion = new Regex(".*id ['\"]com\\.android\\.application['\"] version");
|
||||
#else
|
||||
// To match "classpath 'com.android.tools.build:gradle:4.0.1'" line in build.gradle
|
||||
private static readonly Regex TokenGradleVersion = new Regex(".*classpath ['\"]com\\.android\\.tools\\.build:gradle:.*");
|
||||
#endif
|
||||
|
||||
// To match "distributionUrl=..." in gradle-wrapper.properties file
|
||||
private static readonly Regex TokenDistributionUrl = new Regex(".*distributionUrl.*");
|
||||
|
||||
private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
|
||||
|
||||
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);
|
||||
|
||||
var gradlePropertiesUpdated = new List<string>();
|
||||
|
||||
// If the gradle properties file already exists, make sure to add any previous properties.
|
||||
if (File.Exists(gradlePropertiesPath))
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
File.WriteAllText(gradlePropertiesPath, string.Join("\n", gradlePropertiesUpdated.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to enable AndroidX and Jetifier. gradle.properties file write failed.");
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
|
||||
ProcessAndroidManifest(path);
|
||||
AddSdkSettings(path);
|
||||
}
|
||||
|
||||
public int callbackOrder
|
||||
{
|
||||
get { return AppLovinPreProcess.CallbackOrder; }
|
||||
}
|
||||
|
||||
private static void ProcessAndroidManifest(string path)
|
||||
{
|
||||
var manifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");
|
||||
XDocument manifest;
|
||||
try
|
||||
{
|
||||
manifest = XDocument.Load(manifestPath);
|
||||
}
|
||||
#pragma warning disable 0168
|
||||
catch (IOException exception)
|
||||
#pragma warning restore 0168
|
||||
{
|
||||
MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the `manifest` element.
|
||||
var elementManifest = manifest.Element("manifest");
|
||||
if (elementManifest == null)
|
||||
{
|
||||
MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var elementApplication = elementManifest.Element("application");
|
||||
if (elementApplication == null)
|
||||
{
|
||||
MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var metaDataElements = elementApplication.Descendants().Where(element => element.Name.LocalName.Equals("meta-data"));
|
||||
|
||||
EnableVerboseLoggingIfNeeded(elementApplication);
|
||||
AddGoogleApplicationIdIfNeeded(elementApplication, metaDataElements);
|
||||
AddGoogleOptimizationFlagsIfNeeded(elementApplication, metaDataElements);
|
||||
DisableAutoInitIfNeeded(elementApplication, metaDataElements);
|
||||
RemoveSdkKeyIfNeeded(metaDataElements);
|
||||
|
||||
// Save the updated manifest file.
|
||||
manifest.Save(manifestPath);
|
||||
}
|
||||
|
||||
private static void EnableVerboseLoggingIfNeeded(XElement elementApplication)
|
||||
{
|
||||
var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, false);
|
||||
|
||||
var descendants = elementApplication.Descendants();
|
||||
var verboseLoggingMetaData = descendants.FirstOrDefault(descendant => descendant.FirstAttribute != null &&
|
||||
descendant.FirstAttribute.Name.LocalName.Equals("name") &&
|
||||
descendant.FirstAttribute.Value.Equals(KeyMetaDataAppLovinVerboseLoggingOn) &&
|
||||
descendant.LastAttribute != null &&
|
||||
descendant.LastAttribute.Name.LocalName.Equals("value"));
|
||||
|
||||
// check if applovin.sdk.verbose_logging meta data exists.
|
||||
if (verboseLoggingMetaData != null)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
// update applovin.sdk.verbose_logging meta data value.
|
||||
verboseLoggingMetaData.LastAttribute.Value = enabled.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove applovin.sdk.verbose_logging meta data.
|
||||
verboseLoggingMetaData.Remove();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
// add applovin.sdk.verbose_logging meta data if it does not exist.
|
||||
var metaData = CreateMetaDataElement(KeyMetaDataAppLovinVerboseLoggingOn, enabled.ToString());
|
||||
elementApplication.Add(metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddGoogleApplicationIdIfNeeded(XElement elementApplication, IEnumerable<XElement> metaDataElements)
|
||||
{
|
||||
if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
|
||||
|
||||
var googleApplicationIdMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleApplicationId);
|
||||
var appId = AppLovinSettings.Instance.AdMobAndroidAppId;
|
||||
// Log error if the App ID is not set.
|
||||
if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
|
||||
{
|
||||
MaxSdkLogger.UserError("Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the Google App ID meta data already exists. Update if it already exists.
|
||||
if (googleApplicationIdMetaData != null)
|
||||
{
|
||||
googleApplicationIdMetaData.SetAttributeValue(AndroidNamespace + "value", appId);
|
||||
}
|
||||
// Meta data doesn't exist, add it.
|
||||
else
|
||||
{
|
||||
elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleApplicationId, appId));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddGoogleOptimizationFlagsIfNeeded(XElement elementApplication, IEnumerable<XElement> metaDataElements)
|
||||
{
|
||||
if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
|
||||
|
||||
var googleOptimizeInitializationMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleOptimizeInitialization);
|
||||
// If meta data doesn't exist, add it
|
||||
if (googleOptimizeInitializationMetaData == null)
|
||||
{
|
||||
elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleOptimizeInitialization, true));
|
||||
}
|
||||
|
||||
var googleOptimizeAdLoadingMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleOptimizeAdLoading);
|
||||
// If meta data doesn't exist, add it
|
||||
if (googleOptimizeAdLoadingMetaData == null)
|
||||
{
|
||||
elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleOptimizeAdLoading, true));
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisableAutoInitIfNeeded(XElement elementApplication, IEnumerable<XElement> metaDataElements)
|
||||
{
|
||||
if (AppLovinPackageManager.IsAdapterInstalled("MobileFuse"))
|
||||
{
|
||||
var mobileFuseMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataMobileFuseAutoInit);
|
||||
// If MobileFuse meta data doesn't exist, add it
|
||||
if (mobileFuseMetaData == null)
|
||||
{
|
||||
elementApplication.Add(CreateMetaDataElement(KeyMetaDataMobileFuseAutoInit, true));
|
||||
}
|
||||
}
|
||||
|
||||
if (AppLovinPackageManager.IsAdapterInstalled("MyTarget"))
|
||||
{
|
||||
var myTargetMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataMyTargetAutoInit);
|
||||
// If MyTarget meta data doesn't exist, add it
|
||||
if (myTargetMetaData == null)
|
||||
{
|
||||
elementApplication.Add(CreateMetaDataElement(KeyMetaDataMyTargetAutoInit, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveSdkKeyIfNeeded(IEnumerable<XElement> metaDataElements)
|
||||
{
|
||||
var sdkKeyMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataAppLovinSdkKey);
|
||||
if (sdkKeyMetaData == null) return;
|
||||
|
||||
sdkKeyMetaData.Remove();
|
||||
}
|
||||
|
||||
private static void UpdateGradleVersionsIfNeeded(string gradleWrapperPropertiesPath, string rootGradleBuildFilePath)
|
||||
{
|
||||
var customGradleVersionUrl = AppLovinSettings.Instance.CustomGradleVersionUrl;
|
||||
var customGradleToolsVersion = AppLovinSettings.Instance.CustomGradleToolsVersion;
|
||||
|
||||
if (MaxSdkUtils.IsValidString(customGradleVersionUrl))
|
||||
{
|
||||
var newDistributionUrl = string.Format("distributionUrl={0}", customGradleVersionUrl);
|
||||
if (ReplaceStringInFile(gradleWrapperPropertiesPath, TokenDistributionUrl, newDistributionUrl))
|
||||
{
|
||||
MaxSdkLogger.D("Distribution url set to " + newDistributionUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxSdkLogger.E("Failed to set distribution URL");
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxSdkUtils.IsValidString(customGradleToolsVersion))
|
||||
{
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
// 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+");
|
||||
return;
|
||||
}
|
||||
|
||||
var newGradleVersionLibraryLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("id 'com.android.library' version '{0}' apply false", customGradleToolsVersion));
|
||||
if (ReplaceStringInFile(rootGradleBuildFilePath, TokenGradleVersionLibrary, newGradleVersionLibraryLine))
|
||||
{
|
||||
MaxSdkLogger.D("Gradle library version set to " + newGradleVersionLibraryLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxSdkLogger.E("Failed to set gradle library version");
|
||||
}
|
||||
|
||||
var newGradleVersionLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("id 'com.android.application' version '{0}' apply false", customGradleToolsVersion));
|
||||
#else
|
||||
var newGradleVersionLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("classpath 'com.android.tools.build:gradle:{0}'", customGradleToolsVersion));
|
||||
#endif
|
||||
if (ReplaceStringInFile(rootGradleBuildFilePath, TokenGradleVersion, newGradleVersionLine))
|
||||
{
|
||||
MaxSdkLogger.D("Gradle version set to " + newGradleVersionLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxSdkLogger.E("Failed to set gradle plugin version");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddSdkSettings(string path)
|
||||
{
|
||||
var appLovinSdkSettings = new Dictionary<string, object>();
|
||||
var rawResourceDirectory = Path.Combine(path, "src/main/res/raw");
|
||||
|
||||
// Add the SDK key to the SDK settings.
|
||||
appLovinSdkSettings[KeySdkKey] = AppLovinSettings.Instance.SdkKey;
|
||||
appLovinSdkSettings[KeyRenderOutsideSafeArea] = PlayerSettings.Android.renderOutsideSafeArea;
|
||||
|
||||
// Add the Terms and Privacy Policy flow settings if needed.
|
||||
EnableConsentFlowIfNeeded(rawResourceDirectory, appLovinSdkSettings);
|
||||
|
||||
WriteAppLovinSettings(rawResourceDirectory, appLovinSdkSettings);
|
||||
}
|
||||
|
||||
private static void EnableConsentFlowIfNeeded(string rawResourceDirectory, Dictionary<string, object> applovinSdkSettings)
|
||||
{
|
||||
// Check if consent flow is enabled. No need to create the applovin_consent_flow_settings.json if consent flow is disabled.
|
||||
var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
|
||||
if (!consentFlowEnabled)
|
||||
{
|
||||
RemoveAppLovinSettingsRawResourceFileIfNeeded(rawResourceDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
|
||||
if (string.IsNullOrEmpty(privacyPolicyUrl))
|
||||
{
|
||||
AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL in the AppLovin Integration Manager.");
|
||||
|
||||
// No need to update the applovin_consent_flow_settings.json here. Default consent flow state will be determined on the SDK side.
|
||||
return;
|
||||
}
|
||||
|
||||
var consentFlowSettings = new Dictionary<string, object>();
|
||||
consentFlowSettings[KeyConsentFlowEnabled] = consentFlowEnabled;
|
||||
consentFlowSettings[KeyConsentFlowPrivacyPolicy] = privacyPolicyUrl;
|
||||
|
||||
var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
|
||||
if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
|
||||
{
|
||||
consentFlowSettings[KeyConsentFlowTermsOfService] = termsOfServiceUrl;
|
||||
}
|
||||
|
||||
consentFlowSettings[KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR] = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
|
||||
|
||||
var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
|
||||
if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
|
||||
{
|
||||
consentFlowSettings[KeyConsentFlowDebugUserGeography] = "gdpr";
|
||||
}
|
||||
|
||||
applovinSdkSettings[KeyConsentFlowSettings] = consentFlowSettings;
|
||||
}
|
||||
|
||||
private static void WriteAppLovinSettingsRawResourceFile(string applovinSdkSettingsJson, string rawResourceDirectory)
|
||||
{
|
||||
if (!Directory.Exists(rawResourceDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(rawResourceDirectory);
|
||||
}
|
||||
|
||||
var consentFlowSettingsFilePath = Path.Combine(rawResourceDirectory, AppLovinSettingsFileName);
|
||||
try
|
||||
{
|
||||
File.WriteAllText(consentFlowSettingsFilePath, applovinSdkSettingsJson + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("applovin_settings.json file write failed due to: " + exception.Message);
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the applovin_settings json file from the build if it exists.
|
||||
/// </summary>
|
||||
/// <param name="rawResourceDirectory">The raw resource directory that holds the json file</param>
|
||||
private static void RemoveAppLovinSettingsRawResourceFileIfNeeded(string rawResourceDirectory)
|
||||
{
|
||||
var consentFlowSettingsFilePath = Path.Combine(rawResourceDirectory, AppLovinSettingsFileName);
|
||||
if (!File.Exists(consentFlowSettingsFilePath)) return;
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(consentFlowSettingsFilePath);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Deleting applovin_settings.json failed due to: " + exception.Message);
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteAppLovinSettings(string rawResourceDirectory, Dictionary<string, object> applovinSdkSettings)
|
||||
{
|
||||
var applovinSdkSettingsJson = Json.Serialize(applovinSdkSettings);
|
||||
WriteAppLovinSettingsRawResourceFile(applovinSdkSettingsJson, rawResourceDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns a <c>meta-data</c> element with the given name and value.
|
||||
/// </summary>
|
||||
private static XElement CreateMetaDataElement(string name, object value)
|
||||
{
|
||||
var metaData = new XElement("meta-data");
|
||||
metaData.Add(new XAttribute(AndroidNamespace + "name", name));
|
||||
metaData.Add(new XAttribute(AndroidNamespace + "value", value));
|
||||
|
||||
return metaData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks through all the given meta-data elements to check if the required one exists. Returns <c>null</c> if it doesn't exist.
|
||||
/// </summary>
|
||||
private static XElement GetMetaDataElement(IEnumerable<XElement> metaDataElements, string metaDataName)
|
||||
{
|
||||
foreach (var metaDataElement in metaDataElements)
|
||||
{
|
||||
var attributes = metaDataElement.Attributes();
|
||||
if (attributes.Any(attribute => attribute.Name.Namespace.Equals(AndroidNamespace)
|
||||
&& attribute.Name.LocalName.Equals("name")
|
||||
&& attribute.Value.Equals(metaDataName)))
|
||||
{
|
||||
return metaDataElement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first line that contains regexToMatch and replaces the whole line with replacement
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the file you want to replace a line in</param>
|
||||
/// <param name="regexToMatch">Regex to search for in the line you want to replace</param>
|
||||
/// <param name="replacement">String that you want as the new line</param>
|
||||
/// <returns>Returns whether the string was successfully replaced or not</returns>
|
||||
private static bool ReplaceStringInFile(string path, Regex regexToMatch, string replacement)
|
||||
{
|
||||
if (!File.Exists(path)) return false;
|
||||
|
||||
var lines = File.ReadAllLines(path);
|
||||
for (var i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (regexToMatch.IsMatch(lines[i]))
|
||||
{
|
||||
lines[i] = replacement;
|
||||
File.WriteAllLines(path, lines);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c01ea79d0cb2a43c093e2fd07201df9e
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// AppLovinBuildPostProcessor.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 8/29/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
using System.IO;
|
||||
using UnityEditor.Android;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Quality Service plugin to the Gradle project once the project has been exported. See <see cref="AppLovinProcessGradleBuildFile"/> for more details.
|
||||
/// </summary>
|
||||
public class AppLovinPostProcessGradleProject : AppLovinProcessGradleBuildFile, IPostGenerateGradleAndroidProject
|
||||
{
|
||||
public void OnPostGenerateGradleAndroidProject(string path)
|
||||
{
|
||||
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 shouldAddQualityServiceToDependencies = ShouldAddQualityServiceToDependencies(rootGradleBuildFilePath);
|
||||
|
||||
var failedToAddPlugin = false;
|
||||
if (shouldAddQualityServiceToDependencies)
|
||||
{
|
||||
// Add the Quality Service Plugin to the dependencies block in the root build.gradle file
|
||||
var buildScriptChangesAdded = AddQualityServiceBuildScriptLines(rootGradleBuildFilePath);
|
||||
failedToAddPlugin = !buildScriptChangesAdded;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
failedToAddPlugin = !(qualityServiceAdded && appLovinRepositoryAdded);
|
||||
}
|
||||
|
||||
if (failedToAddPlugin)
|
||||
{
|
||||
MaxSdkLogger.UserWarning("Failed to add AppLovin Quality Service plugin to the gradle project.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
MaxSdkLogger.UserWarning("Couldn't find build.gradle file. Failed to add AppLovin Quality Service plugin to the gradle project.");
|
||||
return;
|
||||
}
|
||||
|
||||
AddAppLovinQualityServicePlugin(applicationGradleBuildFilePath);
|
||||
}
|
||||
|
||||
public int callbackOrder
|
||||
{
|
||||
get { return CallbackOrder; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f75e54e2eb78f427ca8643c97684387b
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,721 @@
|
||||
//
|
||||
// MaxIntegrationManager.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 8/29/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_IOS || UNITY_IPHONE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.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;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
[Serializable]
|
||||
public class SkAdNetworkData
|
||||
{
|
||||
[SerializeField] public string[] SkAdNetworkIds;
|
||||
}
|
||||
|
||||
public class AppLovinPostProcessiOS
|
||||
{
|
||||
private const string OutputFileName = "AppLovinQualityServiceSetup.rb";
|
||||
|
||||
#if !UNITY_2019_3_OR_NEWER
|
||||
private const string 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;
|
||||
|
||||
private const string TargetUnityIphonePodfileLine = "target 'Unity-iPhone' do";
|
||||
private const string UseFrameworksPodfileLine = "use_frameworks!";
|
||||
private const string UseFrameworksDynamicPodfileLine = "use_frameworks! :linkage => :dynamic";
|
||||
private const string UseFrameworksStaticPodfileLine = "use_frameworks! :linkage => :static";
|
||||
|
||||
private const string ResourcesDirectoryName = "Resources";
|
||||
private const string AppLovinMaxResourcesDirectoryName = "AppLovinMAXResources";
|
||||
private const string AppLovinAdvertisingAttributionEndpoint = "https://postbacks-app.com";
|
||||
|
||||
private const string AppLovinSettingsPlistFileName = "AppLovin-Settings.plist";
|
||||
|
||||
private const string KeySdkKey = "SdkKey";
|
||||
|
||||
private const string AppLovinVerboseLoggingOnKey = "AppLovinVerboseLoggingOn";
|
||||
|
||||
private const string KeyConsentFlowInfo = "ConsentFlowInfo";
|
||||
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";
|
||||
|
||||
private static readonly Regex PodfilePodLineRegex = new Regex("pod \'([^\']*)\'");
|
||||
|
||||
/// <summary>
|
||||
/// Adds AppLovin Quality Service to the iOS project once the project has been exported.
|
||||
///
|
||||
/// 1. Downloads the Quality Service ruby script.
|
||||
/// 2. Runs the script using Ruby which integrates AppLovin Quality Service to the project.
|
||||
/// </summary>
|
||||
[PostProcessBuild(AppLovinPreProcess.CallbackOrder)] // We want to run Quality Service script last.
|
||||
public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
|
||||
{
|
||||
if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
|
||||
|
||||
var sdkKey = AppLovinSettings.Instance.SdkKey;
|
||||
if (string.IsNullOrEmpty(sdkKey))
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
|
||||
return;
|
||||
}
|
||||
|
||||
var outputFilePath = Path.Combine(buildPath, OutputFileName);
|
||||
|
||||
// Check if Quality Service is already installed.
|
||||
if (File.Exists(outputFilePath) && Directory.Exists(Path.Combine(buildPath, "AppLovinQualityService")))
|
||||
{
|
||||
// TODO: Check if there is a way to validate if the SDK key matches the script. Else the pub can't use append when/if they change the SDK Key.
|
||||
return;
|
||||
}
|
||||
|
||||
var webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
DownloadHandler = new DownloadHandlerFile(outputFilePath),
|
||||
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
|
||||
EndPoint = "https://api2.safedk.com/v1/build/ios_setup2",
|
||||
RequestType = WebRequestType.Post,
|
||||
};
|
||||
|
||||
webRequestConfig.Headers.Add("Content-Type", "application/json");
|
||||
|
||||
var maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
AppLovinEditorCoroutine.StartCoroutine(maxWebRequest.Send(webResponse =>
|
||||
{
|
||||
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)]
|
||||
public static void MaxPostProcessPbxProject(BuildTarget buildTarget, string buildPath)
|
||||
{
|
||||
var projectPath = PBXProject.GetPBXProjectPath(buildPath);
|
||||
var project = new PBXProject();
|
||||
project.ReadFromFile(projectPath);
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
var unityMainTargetGuid = project.GetUnityMainTargetGuid();
|
||||
var unityFrameworkTargetGuid = project.GetUnityFrameworkTargetGuid();
|
||||
#else
|
||||
var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
|
||||
var unityFrameworkTargetGuid = project.TargetGuidByName(UnityMainTargetName);
|
||||
#endif
|
||||
EmbedDynamicLibrariesIfNeeded(buildPath, project, unityMainTargetGuid);
|
||||
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, "en", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, "es", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, "fr", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, "ja", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, "ko", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, "zh-Hans", buildPath, project, unityMainTargetGuid);
|
||||
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, "zh-Hant", buildPath, project, unityMainTargetGuid);
|
||||
|
||||
AddSwiftSupport(buildPath, project, unityFrameworkTargetGuid, unityMainTargetGuid);
|
||||
AddYandexSettingsIfNeeded(project, unityMainTargetGuid);
|
||||
|
||||
project.WriteToFile(projectPath);
|
||||
}
|
||||
|
||||
private static void EmbedDynamicLibrariesIfNeeded(string buildPath, PBXProject project, string targetGuid)
|
||||
{
|
||||
// Check that the Pods directory exists (it might not if a publisher is building with Generate Podfile setting disabled in EDM).
|
||||
var podsDirectory = Path.Combine(buildPath, "Pods");
|
||||
if (!Directory.Exists(podsDirectory) || !ShouldEmbedDynamicLibraries(buildPath)) return;
|
||||
|
||||
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>
|
||||
/// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// | embed | use_frameworks! (:linkage => :dynamic) | use_frameworks! :linkage => :static | `use_frameworks!` line not present |
|
||||
/// |---------------------------|------------------------------------------|---------------------------------------|--------------------------------------|
|
||||
/// | Unity-iPhone present | Do not embed dynamic libraries | Embed dynamic libraries | Do not embed dynamic libraries |
|
||||
/// | Unity-iPhone not present | Embed dynamic libraries | Embed dynamic libraries | Embed dynamic libraries |
|
||||
/// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// </summary>
|
||||
/// <param name="buildPath">An iOS build path</param>
|
||||
/// <returns>Whether or not the dynamic libraries should be embedded.</returns>
|
||||
private static bool ShouldEmbedDynamicLibraries(string buildPath)
|
||||
{
|
||||
var podfilePath = Path.Combine(buildPath, "Podfile");
|
||||
if (!File.Exists(podfilePath)) return false;
|
||||
|
||||
// If the Podfile doesn't have a `Unity-iPhone` target, we should embed the dynamic libraries.
|
||||
var lines = File.ReadAllLines(podfilePath);
|
||||
var containsUnityIphoneTarget = lines.Any(line => line.Contains(TargetUnityIphonePodfileLine));
|
||||
if (!containsUnityIphoneTarget) return true;
|
||||
|
||||
// If the Podfile does not have a `use_frameworks! :linkage => static` line, we should not embed the dynamic libraries.
|
||||
var useFrameworksStaticLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksStaticPodfileLine));
|
||||
if (useFrameworksStaticLineIndex == -1) return false;
|
||||
|
||||
// If more than one of the `use_frameworks!` lines are present, CocoaPods will use the last one.
|
||||
var useFrameworksLineIndex = Array.FindIndex(lines, line => line.Trim() == UseFrameworksPodfileLine); // Check for exact line to avoid matching `use_frameworks! :linkage => static/dynamic`
|
||||
var useFrameworksDynamicLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksDynamicPodfileLine));
|
||||
|
||||
// Check if `use_frameworks! :linkage => :static` is the last line of the three. If it is, we should embed the dynamic libraries.
|
||||
return useFrameworksLineIndex < useFrameworksStaticLineIndex && useFrameworksDynamicLineIndex < useFrameworksStaticLineIndex;
|
||||
}
|
||||
|
||||
private static List<string> GetDynamicLibraryPathsToEmbed(string podsDirectory, string buildPath)
|
||||
{
|
||||
var podfilePath = Path.Combine(buildPath, "Podfile");
|
||||
var dynamicLibraryFrameworksToEmbed = GetDynamicLibraryFrameworksToEmbed(podfilePath);
|
||||
|
||||
return GetDynamicLibraryPathsInProjectToEmbed(podsDirectory, dynamicLibraryFrameworksToEmbed);
|
||||
}
|
||||
|
||||
private static List<string> GetDynamicLibraryFrameworksToEmbed(string podfilePath)
|
||||
{
|
||||
var dynamicLibrariesToEmbed = GetDynamicLibrariesToEmbed();
|
||||
|
||||
var podsInUnityIphoneTarget = GetPodNamesInUnityIphoneTarget(podfilePath);
|
||||
var dynamicLibrariesToIgnore = dynamicLibrariesToEmbed.Where(dynamicLibraryToEmbed => podsInUnityIphoneTarget.Contains(dynamicLibraryToEmbed.PodName)).ToList();
|
||||
|
||||
// Determine frameworks to embed based on the dynamic libraries to embed and ignore
|
||||
var dynamicLibraryFrameworksToIgnore = dynamicLibrariesToIgnore.SelectMany(library => library.FrameworkNames).Distinct().ToList();
|
||||
return dynamicLibrariesToEmbed.SelectMany(library => library.FrameworkNames).Except(dynamicLibraryFrameworksToIgnore).Distinct().ToList();
|
||||
}
|
||||
|
||||
private static List<DynamicLibraryToEmbed> GetDynamicLibrariesToEmbed()
|
||||
{
|
||||
var pluginData = AppLovinIntegrationManager.LoadPluginDataSync();
|
||||
if (pluginData == null)
|
||||
{
|
||||
MaxSdkLogger.E("Failed to load plugin data. Dynamic libraries will not be embedded.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the dynamic libraries to embed for each network
|
||||
var librariesToAdd = pluginData.MediatedNetworks
|
||||
.Where(network => network.DynamicLibrariesToEmbed != null)
|
||||
.SelectMany(network => network.DynamicLibrariesToEmbed
|
||||
.Where(libraryToEmbed => IsRequiredNetworkVersionInstalled(libraryToEmbed, network)))
|
||||
.ToList();
|
||||
|
||||
// Get the dynamic libraries to embed for AppLovin MAX
|
||||
if (pluginData.AppLovinMax.DynamicLibrariesToEmbed != null)
|
||||
{
|
||||
librariesToAdd.AddRange(pluginData.AppLovinMax.DynamicLibrariesToEmbed);
|
||||
}
|
||||
|
||||
// Get the dynamic libraries to embed for third parties
|
||||
if (pluginData.ThirdPartyDynamicLibrariesToEmbed != null)
|
||||
{
|
||||
// TODO: Add version check for third party dynamic libraries.
|
||||
librariesToAdd.AddRange(pluginData.ThirdPartyDynamicLibrariesToEmbed);
|
||||
}
|
||||
|
||||
return librariesToAdd;
|
||||
}
|
||||
|
||||
private static List<string> GetPodNamesInUnityIphoneTarget(string podfilePath)
|
||||
{
|
||||
var lines = File.ReadAllLines(podfilePath);
|
||||
var podNamesInUnityIphone = new List<string>();
|
||||
|
||||
var insideUnityIphoneTarget = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
// Loop until we find the `target 'Unity-iPhone'` line
|
||||
if (insideUnityIphoneTarget)
|
||||
{
|
||||
if (line.Trim() == "end") break;
|
||||
|
||||
if (PodfilePodLineRegex.IsMatch(line))
|
||||
{
|
||||
var podName = PodfilePodLineRegex.Match(line).Groups[1].Value;
|
||||
podNamesInUnityIphone.Add(podName);
|
||||
}
|
||||
}
|
||||
else if (line.Contains(TargetUnityIphonePodfileLine))
|
||||
{
|
||||
insideUnityIphoneTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
return podNamesInUnityIphone;
|
||||
}
|
||||
|
||||
private static bool IsRequiredNetworkVersionInstalled(DynamicLibraryToEmbed libraryToEmbed, Network network)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private static List<string> GetDynamicLibraryPathsInProjectToEmbed(string podsDirectory, List<string> dynamicLibrariesToEmbed)
|
||||
{
|
||||
var dynamicLibraryPathsPresentInProject = new List<string>();
|
||||
foreach (var dynamicLibraryToSearch in dynamicLibrariesToEmbed)
|
||||
{
|
||||
// both .framework and .xcframework are directories, not files
|
||||
var directories = Directory.GetDirectories(podsDirectory, dynamicLibraryToSearch, SearchOption.AllDirectories);
|
||||
if (directories.Length <= 0) continue;
|
||||
|
||||
var dynamicLibraryAbsolutePath = directories[0];
|
||||
var relativePath = GetDynamicLibraryRelativePath(dynamicLibraryAbsolutePath);
|
||||
dynamicLibraryPathsPresentInProject.Add(relativePath);
|
||||
}
|
||||
|
||||
return dynamicLibraryPathsPresentInProject;
|
||||
}
|
||||
|
||||
private static string GetDynamicLibraryRelativePath(string dynamicLibraryAbsolutePath)
|
||||
{
|
||||
var index = dynamicLibraryAbsolutePath.LastIndexOf("Pods", StringComparison.Ordinal);
|
||||
return dynamicLibraryAbsolutePath.Substring(index);
|
||||
}
|
||||
|
||||
private static void LocalizeUserTrackingDescriptionIfNeeded(string localizedUserTrackingDescription, string localeCode, string buildPath, PBXProject project, string targetGuid)
|
||||
{
|
||||
var resourcesDirectoryPath = Path.Combine(buildPath, AppLovinMaxResourcesDirectoryName);
|
||||
var localeSpecificDirectoryName = localeCode + ".lproj";
|
||||
var localeSpecificDirectoryPath = Path.Combine(resourcesDirectoryPath, localeSpecificDirectoryName);
|
||||
var infoPlistStringsFilePath = Path.Combine(localeSpecificDirectoryPath, "InfoPlist.strings");
|
||||
|
||||
// Check if localization has been disabled between builds, and remove them as needed.
|
||||
if (ShouldRemoveLocalization(localizedUserTrackingDescription))
|
||||
{
|
||||
if (!File.Exists(infoPlistStringsFilePath)) return;
|
||||
|
||||
File.Delete(infoPlistStringsFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log an error if we detect a localization file for this language in the `Resources` directory
|
||||
var legacyResourcedDirectoryPath = Path.Combine(buildPath, ResourcesDirectoryName);
|
||||
var localeSpecificLegacyDirectoryPath = Path.Combine(legacyResourcedDirectoryPath, localeSpecificDirectoryName);
|
||||
if (Directory.Exists(localeSpecificLegacyDirectoryPath))
|
||||
{
|
||||
MaxSdkLogger.UserError("Detected existing localization resource for \"" + localeCode + "\" locale. Skipping localization for User Tracking Usage Description. Please disable localization in AppLovin Integration manager and add the localizations to your existing resource.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create intermediate directories as needed.
|
||||
if (!Directory.Exists(resourcesDirectoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(resourcesDirectoryPath);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(localeSpecificDirectoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(localeSpecificDirectoryPath);
|
||||
}
|
||||
|
||||
var localizedDescriptionLine = "\"NSUserTrackingUsageDescription\" = \"" + localizedUserTrackingDescription + "\";\n";
|
||||
// File already exists, update it in case the value changed between builds.
|
||||
if (File.Exists(infoPlistStringsFilePath))
|
||||
{
|
||||
var output = new List<string>();
|
||||
var lines = File.ReadAllLines(infoPlistStringsFilePath);
|
||||
var keyUpdated = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Contains("NSUserTrackingUsageDescription"))
|
||||
{
|
||||
output.Add(localizedDescriptionLine);
|
||||
keyUpdated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keyUpdated)
|
||||
{
|
||||
output.Add(localizedDescriptionLine);
|
||||
}
|
||||
|
||||
File.WriteAllText(infoPlistStringsFilePath, string.Join("\n", output.ToArray()) + "\n");
|
||||
}
|
||||
// File doesn't exist, create one.
|
||||
else
|
||||
{
|
||||
File.WriteAllText(infoPlistStringsFilePath, "/* Localized versions of Info.plist keys - Generated by AL MAX plugin */\n" + localizedDescriptionLine);
|
||||
}
|
||||
|
||||
var localeSpecificDirectoryRelativePath = Path.Combine(AppLovinMaxResourcesDirectoryName, localeSpecificDirectoryName);
|
||||
var guid = project.AddFolderReference(localeSpecificDirectoryRelativePath, localeSpecificDirectoryRelativePath);
|
||||
project.AddFileToBuild(targetGuid, guid);
|
||||
}
|
||||
|
||||
private static bool ShouldRemoveLocalization(string localizedUserTrackingDescription)
|
||||
{
|
||||
if (string.IsNullOrEmpty(localizedUserTrackingDescription)) return true;
|
||||
|
||||
var internalSettings = AppLovinInternalSettings.Instance;
|
||||
return !internalSettings.ConsentFlowEnabled || !internalSettings.UserTrackingUsageLocalizationEnabled;
|
||||
}
|
||||
|
||||
private static void AddSwiftSupport(string buildPath, PBXProject project, string unityFrameworkTargetGuid, string unityMainTargetGuid)
|
||||
{
|
||||
var swiftFileRelativePath = "Classes/MAXSwiftSupport.swift";
|
||||
var swiftFilePath = Path.Combine(buildPath, swiftFileRelativePath);
|
||||
|
||||
// Add Swift file
|
||||
CreateSwiftFile(swiftFilePath);
|
||||
var swiftFileGuid = project.AddFile(swiftFileRelativePath, swiftFileRelativePath);
|
||||
project.AddFileToBuild(unityFrameworkTargetGuid, swiftFileGuid);
|
||||
|
||||
// Add Swift version property if needed
|
||||
var swiftVersion = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "SWIFT_VERSION");
|
||||
if (string.IsNullOrEmpty(swiftVersion))
|
||||
{
|
||||
project.SetBuildProperty(unityFrameworkTargetGuid, "SWIFT_VERSION", "5.0");
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (File.Exists(swiftFilePath)) return;
|
||||
|
||||
// Create a file to write to.
|
||||
using (var writer = File.CreateText(swiftFilePath))
|
||||
{
|
||||
writer.WriteLine("//\n// MAXSwiftSupport.swift\n//");
|
||||
writer.WriteLine("\nimport Foundation\n");
|
||||
writer.WriteLine("// This file ensures the project includes Swift support.");
|
||||
writer.WriteLine("// It is automatically generated by the MAX Unity Plugin.");
|
||||
writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[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);
|
||||
|
||||
RemoveAttributionReportEndpointIfNeeded(plist);
|
||||
|
||||
EnableVerboseLoggingIfNeeded(plist);
|
||||
AddGoogleApplicationIdIfNeeded(plist);
|
||||
|
||||
AddSdkSettings(plist, path);
|
||||
AddSkAdNetworksInfoIfNeeded(plist);
|
||||
RemoveSdkKeyIfNeeded(plist);
|
||||
|
||||
plist.WriteToFile(plistPath);
|
||||
}
|
||||
|
||||
private static void RemoveAttributionReportEndpointIfNeeded(PlistDocument plist)
|
||||
{
|
||||
PlistElement attributionReportEndPoint;
|
||||
plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!EditorPrefs.HasKey(MaxSdkLogger.KeyVerboseLoggingEnabled)) return;
|
||||
|
||||
var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled);
|
||||
if (enabled)
|
||||
{
|
||||
plist.root.SetBoolean(AppLovinVerboseLoggingOnKey, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
plist.root.values.Remove(AppLovinVerboseLoggingOnKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddGoogleApplicationIdIfNeeded(PlistDocument plist)
|
||||
{
|
||||
if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
|
||||
|
||||
const string googleApplicationIdentifier = "GADApplicationIdentifier";
|
||||
var appId = AppLovinSettings.Instance.AdMobIosAppId;
|
||||
// Log error if the App ID is not set.
|
||||
if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
|
||||
{
|
||||
MaxSdkLogger.UserError("[AppLovin MAX] Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
|
||||
return;
|
||||
}
|
||||
|
||||
plist.root.SetString(googleApplicationIdentifier, appId);
|
||||
}
|
||||
|
||||
private static void AddYandexSettingsIfNeeded(PBXProject project, string unityMainTargetGuid)
|
||||
{
|
||||
if (!AppLovinPackageManager.IsAdapterInstalled("Yandex")) return;
|
||||
|
||||
if (MaxSdkUtils.CompareVersions(PlayerSettings.iOS.targetOSVersionString, "12.0") == MaxSdkUtils.VersionComparisonResult.Lesser)
|
||||
{
|
||||
MaxSdkLogger.UserWarning("Your iOS target version is under the minimum required version by Yandex. Please update it to 12.0 or newer in your ProjectSettings and rebuild your project.");
|
||||
return;
|
||||
}
|
||||
|
||||
project.SetBuildProperty(unityMainTargetGuid, "GENERATE_INFOPLIST_FILE", "NO");
|
||||
}
|
||||
|
||||
private static void AddSdkSettings(PlistDocument infoPlist, string buildPath)
|
||||
{
|
||||
var sdkSettingsPlistPath = Path.Combine(buildPath, AppLovinSettingsPlistFileName);
|
||||
var sdkSettingsPlist = new PlistDocument();
|
||||
if (File.Exists(sdkSettingsPlistPath))
|
||||
{
|
||||
sdkSettingsPlist.ReadFromFile(sdkSettingsPlistPath);
|
||||
}
|
||||
|
||||
// Add the SDK key to the SDK settings plist.
|
||||
sdkSettingsPlist.root.SetString(KeySdkKey, AppLovinSettings.Instance.SdkKey);
|
||||
|
||||
// Add consent flow settings if needed.
|
||||
EnableConsentFlowIfNeeded(sdkSettingsPlist, infoPlist);
|
||||
|
||||
sdkSettingsPlist.WriteToFile(sdkSettingsPlistPath);
|
||||
|
||||
var projectPath = PBXProject.GetPBXProjectPath(buildPath);
|
||||
var project = new PBXProject();
|
||||
project.ReadFromFile(projectPath);
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
var unityMainTargetGuid = project.GetUnityMainTargetGuid();
|
||||
#else
|
||||
var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
|
||||
#endif
|
||||
|
||||
var guid = project.AddFile(AppLovinSettingsPlistFileName, AppLovinSettingsPlistFileName);
|
||||
project.AddFileToBuild(unityMainTargetGuid, guid);
|
||||
project.WriteToFile(projectPath);
|
||||
}
|
||||
|
||||
private static void EnableConsentFlowIfNeeded(PlistDocument applovinSettingsPlist, PlistDocument infoPlist)
|
||||
{
|
||||
var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
|
||||
if (!consentFlowEnabled) return;
|
||||
|
||||
var userTrackingUsageDescription = AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn;
|
||||
var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
|
||||
if (string.IsNullOrEmpty(userTrackingUsageDescription) || string.IsNullOrEmpty(privacyPolicyUrl))
|
||||
{
|
||||
AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL and the `User Tracking Usage Description` in the AppLovin Integration Manager. \n\n" +
|
||||
"Both values must be included to enable the SDK's consent flow.");
|
||||
|
||||
// No need to update the info.plist here. Default consent flow state will be determined on the SDK side.
|
||||
return;
|
||||
}
|
||||
|
||||
var consentFlowInfoRoot = applovinSettingsPlist.root.CreateDict(KeyConsentFlowInfo);
|
||||
consentFlowInfoRoot.SetBoolean(KeyConsentFlowEnabled, consentFlowEnabled);
|
||||
consentFlowInfoRoot.SetString(KeyConsentFlowPrivacyPolicy, privacyPolicyUrl);
|
||||
|
||||
var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
|
||||
if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
|
||||
{
|
||||
consentFlowInfoRoot.SetString(KeyConsentFlowTermsOfService, termsOfServiceUrl);
|
||||
}
|
||||
|
||||
var shouldShowTermsAndPrivacyPolicyAlertInGdpr = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
|
||||
consentFlowInfoRoot.SetBoolean(KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR, shouldShowTermsAndPrivacyPolicyAlertInGdpr);
|
||||
|
||||
var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
|
||||
if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
|
||||
{
|
||||
consentFlowInfoRoot.SetString(KeyConsentFlowDebugUserGeography, "gdpr");
|
||||
}
|
||||
|
||||
infoPlist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
|
||||
}
|
||||
|
||||
private static void AddSkAdNetworksInfoIfNeeded(PlistDocument plist)
|
||||
{
|
||||
var skAdNetworkData = GetSkAdNetworkData();
|
||||
var skAdNetworkIds = skAdNetworkData.SkAdNetworkIds;
|
||||
// Check if we have a valid list of SKAdNetworkIds that need to be added.
|
||||
if (skAdNetworkIds == null || skAdNetworkIds.Length < 1) return;
|
||||
|
||||
//
|
||||
// Add the SKAdNetworkItems to the plist. It should look like following:
|
||||
//
|
||||
// <key>SKAdNetworkItems</key>
|
||||
// <array>
|
||||
// <dict>
|
||||
// <key>SKAdNetworkIdentifier</key>
|
||||
// <string>ABC123XYZ.skadnetwork</string>
|
||||
// </dict>
|
||||
// <dict>
|
||||
// <key>SKAdNetworkIdentifier</key>
|
||||
// <string>123QWE456.skadnetwork</string>
|
||||
// </dict>
|
||||
// <dict>
|
||||
// <key>SKAdNetworkIdentifier</key>
|
||||
// <string>987XYZ123.skadnetwork</string>
|
||||
// </dict>
|
||||
// </array>
|
||||
//
|
||||
PlistElement skAdNetworkItems;
|
||||
plist.root.values.TryGetValue("SKAdNetworkItems", out skAdNetworkItems);
|
||||
var existingSkAdNetworkIds = new HashSet<string>();
|
||||
// Check if SKAdNetworkItems array is already in the Plist document and collect all the IDs that are already present.
|
||||
if (skAdNetworkItems != null && skAdNetworkItems.GetType() == typeof(PlistElementArray))
|
||||
{
|
||||
var plistElementDictionaries = skAdNetworkItems.AsArray().values.Where(plistElement => plistElement.GetType() == typeof(PlistElementDict));
|
||||
foreach (var plistElement in plistElementDictionaries)
|
||||
{
|
||||
PlistElement existingId;
|
||||
plistElement.AsDict().values.TryGetValue("SKAdNetworkIdentifier", out existingId);
|
||||
if (existingId == null || existingId.GetType() != typeof(PlistElementString) || string.IsNullOrEmpty(existingId.AsString())) continue;
|
||||
|
||||
existingSkAdNetworkIds.Add(existingId.AsString());
|
||||
}
|
||||
}
|
||||
// Else, create an array of SKAdNetworkItems into which we will add our IDs.
|
||||
else
|
||||
{
|
||||
skAdNetworkItems = plist.root.CreateArray("SKAdNetworkItems");
|
||||
}
|
||||
|
||||
foreach (var skAdNetworkId in skAdNetworkIds)
|
||||
{
|
||||
// Skip adding IDs that are already in the array.
|
||||
if (existingSkAdNetworkIds.Contains(skAdNetworkId)) continue;
|
||||
|
||||
var skAdNetworkItemDict = skAdNetworkItems.AsArray().AddDict();
|
||||
skAdNetworkItemDict.SetString("SKAdNetworkIdentifier", skAdNetworkId);
|
||||
}
|
||||
}
|
||||
|
||||
private static SkAdNetworkData GetSkAdNetworkData()
|
||||
{
|
||||
// Get the list of installed ad networks to be passed up
|
||||
var installedNetworks = AppLovinPackageManager.GetInstalledMediationNetworks();
|
||||
var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
|
||||
var adNetworks = string.Join(",", installedNetworks.ToArray());
|
||||
if (MaxSdkUtils.IsValidString(adNetworks))
|
||||
{
|
||||
uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
|
||||
}
|
||||
|
||||
var webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
EndPoint = uriBuilder.ToString()
|
||||
};
|
||||
|
||||
var maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
var webResponse = maxWebRequest.SendSync();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveSdkKeyIfNeeded(PlistDocument plist)
|
||||
{
|
||||
if (!plist.root.values.ContainsKey(KeyAppLovinSdkKeyToRemove)) return;
|
||||
|
||||
plist.root.values.Remove(KeyAppLovinSdkKeyToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5d209f90444f4a90830b48b5f3f3ff4
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// AppLovinPreProcess.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Jonathan Liu on 10/19/2023.
|
||||
// Copyright © 2023 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
Indent = true,
|
||||
IndentChars = " ",
|
||||
NewLineChars = "\n",
|
||||
NewLineHandling = NewLineHandling.Replace
|
||||
};
|
||||
|
||||
protected static string AppLovinDependenciesFilePath
|
||||
{
|
||||
get { return AppLovinIntegrationManager.IsPluginInPackageManager ? Path.Combine("Assets", AppLovinDependenciesFileExportPath) : MaxSdkUtils.GetAssetPathForExportPath(AppLovinDependenciesFileExportPath); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AppLovin Dependencies.xml file. If `createIfNotExists` is true, a new file will be created if one does not exist.
|
||||
/// </summary>
|
||||
/// <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
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return XDocument.Load(path);
|
||||
}
|
||||
|
||||
if (createIfNotExists)
|
||||
{
|
||||
return new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
|
||||
new XElement(ElementNameDependencies));
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.E("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 dependency from an xml file.
|
||||
/// </summary>
|
||||
/// <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
|
||||
{
|
||||
// Ensure directory exists before saving the file
|
||||
var directory = Path.GetDirectoryName(path);
|
||||
if (MaxSdkUtils.IsValidString(directory))
|
||||
{
|
||||
// Does nothing if directory already exists
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
using (var xmlWriter = XmlWriter.Create(path, DependenciesFileXmlWriterSettings))
|
||||
{
|
||||
doc.Save(xmlWriter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.E("Unable to save Dependencies file due to exception: " + exception.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e6254f24e89548b3a7644fa7bf25f9d
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// AppLovinBuildPreProcessor.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 8/27/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
using System.Xml.Linq;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the AppLovin Quality Service plugin to the gradle template file. See <see cref="AppLovinProcessGradleBuildFile"/> for more details.
|
||||
/// </summary>
|
||||
public class AppLovinPreProcessAndroid : AppLovinProcessGradleBuildFile, IPreprocessBuildWithReport
|
||||
{
|
||||
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 = "2.1.0";
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
PreprocessAppLovinQualityServicePlugin();
|
||||
AddGoogleCmpDependencyIfNeeded();
|
||||
}
|
||||
|
||||
private static void PreprocessAppLovinQualityServicePlugin()
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
|
||||
{
|
||||
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
|
||||
{
|
||||
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 CallbackOrder; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ccaf444d0d4f4cadb5debe7c41b20c4
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// AppLovinBuildPreProcessiOS.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Jonathan Liu on 10/17/2023.
|
||||
// Copyright © 2023 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_IOS
|
||||
|
||||
using System.Xml.Linq;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
public class AppLovinPreProcessiOS : AppLovinPreProcess, IPreprocessBuildWithReport
|
||||
{
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
AddGoogleCmpDependencyIfNeeded();
|
||||
}
|
||||
|
||||
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 = "~> 2.1";
|
||||
|
||||
private static void AddGoogleCmpDependencyIfNeeded()
|
||||
{
|
||||
if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
|
||||
{
|
||||
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
|
||||
{
|
||||
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 CallbackOrder; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee45537a5833240d7afcfac4a38df1b9
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,625 @@
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 9/3/19.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AppLovinMax.Internal;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
[Serializable]
|
||||
public class AppLovinQualityServiceData
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming - Need to keep name for response data
|
||||
public string api_key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates the AppLovin Quality Service plugin to the provided build.gradle file.
|
||||
/// If the gradle file already has the plugin, the API key is updated.
|
||||
/// </summary>
|
||||
public abstract class AppLovinProcessGradleBuildFile : AppLovinPreProcess
|
||||
{
|
||||
private static readonly Regex TokenBuildScriptRepositories = new Regex(".*repositories.*");
|
||||
private static readonly Regex TokenBuildScriptDependencies = new Regex(".*classpath \'com.android.tools.build:gradle.*");
|
||||
private static readonly Regex TokenApplicationPlugin = new Regex(".*apply plugin: \'com.android.application\'.*");
|
||||
private static readonly Regex TokenApiKey = new Regex(".*apiKey.*");
|
||||
private static readonly Regex TokenAppLovinPlugin = new Regex(".*apply plugin:.+?(?=applovin-quality-service).*");
|
||||
|
||||
private const string PluginsMatcher = "plugins";
|
||||
private const string PluginManagementMatcher = "pluginManagement";
|
||||
private const string QualityServicePluginRoot = " id 'com.applovin.quality' version '+' apply false // NOTE: Requires version 4.8.3+ for Gradle version 7.2+";
|
||||
|
||||
private const string BuildScriptMatcher = "buildscript";
|
||||
private const string QualityServiceMavenRepo = "maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }";
|
||||
private const string QualityServiceDependencyClassPath = "classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'";
|
||||
private const string QualityServiceApplyPlugin = "apply plugin: 'applovin-quality-service'";
|
||||
private const string QualityServicePlugin = "applovin {";
|
||||
private const string QualityServiceApiKey = " apiKey '{0}'";
|
||||
private const string QualityServiceBintrayMavenRepo = "https://applovin.bintray.com/Quality-Service";
|
||||
private const string QualityServiceNoRegexMavenRepo = "maven { url 'https://artifacts.applovin.com/android' }";
|
||||
|
||||
// Legacy plugin detection variables
|
||||
private const string QualityServiceDependencyClassPathV3 = "classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:3.+'";
|
||||
private static readonly Regex TokenSafeDkLegacyApplyPlugin = new Regex(".*apply plugin:.+?(?=safedk).*");
|
||||
private const string SafeDkLegacyPlugin = "safedk {";
|
||||
private const string SafeDkLegacyMavenRepo = "http://download.safedk.com";
|
||||
private const string SafeDkLegacyDependencyClassPath = "com.safedk:SafeDKGradlePlugin:";
|
||||
|
||||
/// <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>
|
||||
protected 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>
|
||||
/// <param name="applicationGradleBuildFilePath">The gradle file to update.</param>
|
||||
protected static void AddAppLovinQualityServicePlugin(string applicationGradleBuildFilePath)
|
||||
{
|
||||
if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
|
||||
|
||||
var sdkKey = AppLovinSettings.Instance.SdkKey;
|
||||
if (string.IsNullOrEmpty(sdkKey))
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the API Key using the SDK Key.
|
||||
var qualityServiceData = RetrieveQualityServiceData(sdkKey);
|
||||
var apiKey = qualityServiceData.api_key;
|
||||
if (string.IsNullOrEmpty(apiKey))
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. API Key is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the updated Gradle file that needs to be written.
|
||||
var lines = File.ReadAllLines(applicationGradleBuildFilePath).ToList();
|
||||
var sanitizedLines = RemoveLegacySafeDkPlugin(lines);
|
||||
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
|
||||
);
|
||||
// outputLines can be null if we couldn't add the plugin.
|
||||
if (outputLines == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(applicationGradleBuildFilePath, string.Join("\n", outputLines.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Gradle file write failed.");
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds AppLovin Quality Service plugin DSL element to the project's root build.gradle file.
|
||||
/// Sample build.gradle file after adding quality service:
|
||||
/// 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
|
||||
/// }
|
||||
/// tasks.register('clean', Delete) {
|
||||
/// delete rootProject.layout.buildDirectory
|
||||
/// }
|
||||
///
|
||||
/// </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)
|
||||
{
|
||||
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;
|
||||
|
||||
var outputLines = new List<string>();
|
||||
var insidePluginsClosure = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Contains(PluginsMatcher))
|
||||
{
|
||||
insidePluginsClosure = true;
|
||||
}
|
||||
|
||||
if (!pluginAdded && insidePluginsClosure && line.Contains("}"))
|
||||
{
|
||||
outputLines.Add(QualityServicePluginRoot);
|
||||
pluginAdded = true;
|
||||
insidePluginsClosure = false;
|
||||
}
|
||||
|
||||
outputLines.Add(line);
|
||||
}
|
||||
|
||||
if (!pluginAdded) return false;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(rootGradleBuildFile, string.Join("\n", outputLines.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Root Gradle file write failed.");
|
||||
Console.WriteLine(exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the AppLovin maven repository to the project's settings.gradle file.
|
||||
/// Sample settings.gradle file after adding AppLovin Repository:
|
||||
/// pluginManagement {
|
||||
/// repositories {
|
||||
/// maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }
|
||||
///
|
||||
/// gradlePluginPortal()
|
||||
/// google()
|
||||
/// mavenCentral()
|
||||
/// }
|
||||
/// }
|
||||
/// ...
|
||||
///
|
||||
/// </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)
|
||||
{
|
||||
var lines = File.ReadLines(settingsGradleFile).ToList();
|
||||
var outputLines = new List<string>();
|
||||
var mavenRepoAdded = false;
|
||||
var pluginManagementClosureDepth = 0;
|
||||
var insidePluginManagementClosure = false;
|
||||
var pluginManagementMatched = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
outputLines.Add(line);
|
||||
|
||||
if (!pluginManagementMatched && line.Contains(PluginManagementMatcher))
|
||||
{
|
||||
pluginManagementMatched = true;
|
||||
insidePluginManagementClosure = true;
|
||||
}
|
||||
|
||||
if (insidePluginManagementClosure)
|
||||
{
|
||||
if (line.Contains("{"))
|
||||
{
|
||||
pluginManagementClosureDepth++;
|
||||
}
|
||||
|
||||
if (line.Contains("}"))
|
||||
{
|
||||
pluginManagementClosureDepth--;
|
||||
}
|
||||
|
||||
if (pluginManagementClosureDepth == 0)
|
||||
{
|
||||
insidePluginManagementClosure = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (insidePluginManagementClosure)
|
||||
{
|
||||
if (!mavenRepoAdded && TokenBuildScriptRepositories.IsMatch(line))
|
||||
{
|
||||
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
|
||||
mavenRepoAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mavenRepoAdded) return false;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(settingsGradleFile, string.Join("\n", outputLines.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Setting Gradle file write failed.");
|
||||
Console.WriteLine(exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
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:
|
||||
/// allprojects {
|
||||
/// buildscript {
|
||||
/// repositories {
|
||||
/// maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }
|
||||
/// google()
|
||||
/// jcenter()
|
||||
/// }
|
||||
///
|
||||
/// dependencies {
|
||||
/// classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
/// classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'
|
||||
/// }
|
||||
/// ...
|
||||
///
|
||||
/// </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)
|
||||
{
|
||||
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
|
||||
var outputLines = GenerateUpdatedBuildFileLines(lines, null, true);
|
||||
|
||||
// outputLines will be null if we couldn't add the build script lines.
|
||||
if (outputLines == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(rootGradleBuildFile, string.Join("\n", outputLines.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Root Gradle file write failed.");
|
||||
Console.WriteLine(exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the AppLovin Quality Service Plugin or Legacy SafeDK plugin from the given gradle template file if either of them are present.
|
||||
/// </summary>
|
||||
/// <param name="gradleTemplateFile">The gradle template file from which to remove the plugin from</param>
|
||||
protected static void RemoveAppLovinQualityServiceOrSafeDkPlugin(string gradleTemplateFile)
|
||||
{
|
||||
var lines = File.ReadAllLines(gradleTemplateFile).ToList();
|
||||
lines = RemoveLegacySafeDkPlugin(lines);
|
||||
lines = RemoveAppLovinQualityServicePlugin(lines);
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(gradleTemplateFile, string.Join("\n", lines.ToArray()) + "\n");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to remove AppLovin Quality Service Plugin from mainTemplate.gradle. Please remove the Quality Service plugin from the mainTemplate.gradle manually.");
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static AppLovinQualityServiceData RetrieveQualityServiceData(string sdkKey)
|
||||
{
|
||||
var webRequestConfig = new WebRequestConfig()
|
||||
{
|
||||
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
|
||||
EndPoint = "https://api2.safedk.com/v1/build/cred",
|
||||
RequestType = WebRequestType.Post,
|
||||
};
|
||||
|
||||
webRequestConfig.Headers.Add("Content-Type", "application/json");
|
||||
|
||||
var maxWebRequest = new MaxWebRequest(webRequestConfig);
|
||||
var webResponse = maxWebRequest.SendSync();
|
||||
|
||||
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>(webResponse.ResponseMessage);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MaxSdkLogger.UserError("Failed to parse API Key." + exception);
|
||||
return new AppLovinQualityServiceData();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> RemoveLegacySafeDkPlugin(List<string> lines)
|
||||
{
|
||||
return RemovePlugin(lines, SafeDkLegacyPlugin, SafeDkLegacyMavenRepo, SafeDkLegacyDependencyClassPath, TokenSafeDkLegacyApplyPlugin);
|
||||
}
|
||||
|
||||
private static List<string> RemoveAppLovinQualityServicePlugin(List<string> lines)
|
||||
{
|
||||
return RemovePlugin(lines, QualityServicePlugin, QualityServiceMavenRepo, QualityServiceDependencyClassPath, TokenAppLovinPlugin);
|
||||
}
|
||||
|
||||
private static List<string> RemovePlugin(List<string> lines, string pluginLine, string mavenRepo, string dependencyClassPath, Regex applyPluginToken)
|
||||
{
|
||||
var sanitizedLines = new List<string>();
|
||||
var legacyRepoRemoved = false;
|
||||
var legacyDependencyClassPathRemoved = false;
|
||||
var legacyPluginRemoved = false;
|
||||
var legacyPluginMatched = false;
|
||||
var insideLegacySafeDkClosure = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (!legacyPluginMatched && line.Contains(pluginLine))
|
||||
{
|
||||
legacyPluginMatched = true;
|
||||
insideLegacySafeDkClosure = true;
|
||||
}
|
||||
|
||||
if (insideLegacySafeDkClosure && line.Contains("}"))
|
||||
{
|
||||
insideLegacySafeDkClosure = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideLegacySafeDkClosure)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!legacyRepoRemoved && line.Contains(mavenRepo))
|
||||
{
|
||||
legacyRepoRemoved = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!legacyDependencyClassPathRemoved && line.Contains(dependencyClassPath))
|
||||
{
|
||||
legacyDependencyClassPathRemoved = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!legacyPluginRemoved && applyPluginToken.IsMatch(line))
|
||||
{
|
||||
legacyPluginRemoved = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
sanitizedLines.Add(line);
|
||||
}
|
||||
|
||||
return sanitizedLines;
|
||||
}
|
||||
|
||||
private static List<string> GenerateUpdatedBuildFileLines(List<string> lines, string apiKey, bool addBuildScriptLines)
|
||||
{
|
||||
var addPlugin = MaxSdkUtils.IsValidString(apiKey);
|
||||
// A sample of the template file.
|
||||
// ...
|
||||
// allprojects {
|
||||
// repositories {**ARTIFACTORYREPOSITORY**
|
||||
// google()
|
||||
// jcenter()
|
||||
// flatDir {
|
||||
// dirs 'libs'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// apply plugin: 'com.android.application'
|
||||
// **APPLY_PLUGINS**
|
||||
//
|
||||
// dependencies {
|
||||
// implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
// **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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
public static string GetFormattedBuildScriptLine(string buildScriptLine)
|
||||
{
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
return " "
|
||||
#elif UNITY_2019_3_OR_NEWER
|
||||
return " "
|
||||
#else
|
||||
return " "
|
||||
#endif
|
||||
+ buildScriptLine;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GenerateAppLovinPluginClosure(string apiKey)
|
||||
{
|
||||
// applovin {
|
||||
// // NOTE: DO NOT CHANGE - this is NOT your AppLovin MAX SDK key - this is a derived key.
|
||||
// apiKey "456...a1b"
|
||||
// }
|
||||
var linesToInject = new List<string>(5);
|
||||
linesToInject.Add("");
|
||||
linesToInject.Add("applovin {");
|
||||
linesToInject.Add(" // NOTE: DO NOT CHANGE - this is NOT your AppLovin MAX SDK key - this is a derived key.");
|
||||
linesToInject.Add(string.Format(QualityServiceApiKey, apiKey));
|
||||
linesToInject.Add("}");
|
||||
|
||||
return linesToInject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 732b7510bc9c94aafb3fd3b8c0dc5d2d
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// AppLovinSettings.cs
|
||||
// AppLovin MAX Unity Plugin
|
||||
//
|
||||
// Created by Santosh Bagadi on 1/27/20.
|
||||
// Copyright © 2019 AppLovin. All rights reserved.
|
||||
//
|
||||
|
||||
using AppLovinMax.Scripts.IntegrationManager.Editor;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ScriptableObject"/> representing the AppLovin Settings that can be set in the Integration Manager Window.
|
||||
///
|
||||
/// The scriptable object asset is created with the name <c>AppLovinSettings.asset</c> and is placed under the directory <c>Assets/MaxSdk/Resources</c>.
|
||||
///
|
||||
/// NOTE: Not name spacing this class since it is reflected upon by the Google adapter and will break compatibility.
|
||||
/// </summary>
|
||||
public class AppLovinSettings : ScriptableObject
|
||||
{
|
||||
private const string SettingsExportPath = "MaxSdk/Resources/AppLovinSettings.asset";
|
||||
|
||||
private static AppLovinSettings instance;
|
||||
|
||||
[SerializeField] private bool qualityServiceEnabled = true;
|
||||
[SerializeField] private string sdkKey;
|
||||
|
||||
[SerializeField] private string customGradleVersionUrl;
|
||||
[SerializeField] private string customGradleToolsVersion;
|
||||
|
||||
[SerializeField] private string adMobAndroidAppId = string.Empty;
|
||||
[SerializeField] private string adMobIosAppId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// An instance of AppLovin Setting.
|
||||
/// </summary>
|
||||
public static AppLovinSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
// Check for an existing AppLovinSettings somewhere in the project
|
||||
var guids = AssetDatabase.FindAssets("AppLovinSettings t:ScriptableObject");
|
||||
if (guids.Length > 1)
|
||||
{
|
||||
MaxSdkLogger.UserWarning("Multiple AppLovinSettings found. This may cause unexpected results.");
|
||||
}
|
||||
|
||||
if (guids.Length != 0)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
instance = AssetDatabase.LoadAssetAtPath<AppLovinSettings>(path);
|
||||
return instance;
|
||||
}
|
||||
|
||||
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 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 path relative to Assets/ directory.
|
||||
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine("Assets", SettingsExportPath));
|
||||
|
||||
var maxSdkDir = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(Application.dataPath, "MaxSdk"));
|
||||
if (!Directory.Exists(maxSdkDir))
|
||||
{
|
||||
Directory.CreateDirectory(maxSdkDir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath));
|
||||
}
|
||||
|
||||
var settingsDir = Path.GetDirectoryName(settingsFilePath);
|
||||
if (!Directory.Exists(settingsDir))
|
||||
{
|
||||
Directory.CreateDirectory(settingsDir);
|
||||
}
|
||||
|
||||
// On script reload AssetDatabase.FindAssets() can fail and will overwrite AppLovinSettings without this check
|
||||
if (!File.Exists(settingsFilePath))
|
||||
{
|
||||
instance = CreateInstance<AppLovinSettings>();
|
||||
AssetDatabase.CreateAsset(instance, settingsFilePath);
|
||||
MaxSdkLogger.D("Creating new AppLovinSettings asset at path: " + settingsFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to install Quality Service plugin.
|
||||
/// </summary>
|
||||
public bool QualityServiceEnabled
|
||||
{
|
||||
get { return Instance.qualityServiceEnabled; }
|
||||
set { Instance.qualityServiceEnabled = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AppLovin SDK Key.
|
||||
/// </summary>
|
||||
public string SdkKey
|
||||
{
|
||||
get { return Instance.sdkKey; }
|
||||
set { Instance.sdkKey = 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>
|
||||
public string CustomGradleVersionUrl
|
||||
{
|
||||
get { return Instance.customGradleVersionUrl; }
|
||||
set { Instance.customGradleVersionUrl = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A string to set the custom gradle tools version (ex: com.android.tools.build:gradle:4.2.0)
|
||||
/// </summary>
|
||||
public string CustomGradleToolsVersion
|
||||
{
|
||||
get { return Instance.customGradleToolsVersion; }
|
||||
set { Instance.customGradleToolsVersion = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AdMob Android App ID.
|
||||
/// </summary>
|
||||
public string AdMobAndroidAppId
|
||||
{
|
||||
get { return Instance.adMobAndroidAppId; }
|
||||
set { Instance.adMobAndroidAppId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AdMob iOS App ID.
|
||||
/// </summary>
|
||||
public string AdMobIosAppId
|
||||
{
|
||||
get { return Instance.adMobIosAppId; }
|
||||
set { Instance.adMobIosAppId = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the instance of the settings.
|
||||
/// </summary>
|
||||
public void SaveAsync()
|
||||
{
|
||||
EditorUtility.SetDirty(instance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebc0ba1b5ef6b4a6b9dd53d7eadfea16
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AppLovinMax.ThirdParty.MiniJson;
|
||||
|
||||
namespace AppLovinMax.Scripts.IntegrationManager.Editor
|
||||
{
|
||||
public class AppLovinUpmManifest
|
||||
{
|
||||
private const string KeyUrl = "url";
|
||||
private const string KeyName = "name";
|
||||
private const string KeyScopes = "scopes";
|
||||
private const string KeyScopedRegistry = "scopedRegistries";
|
||||
|
||||
private Dictionary<string, object> manifest;
|
||||
|
||||
private static string ManifestPath
|
||||
{
|
||||
get { return Path.Combine(Directory.GetCurrentDirectory(), "Packages/manifest.json"); }
|
||||
}
|
||||
|
||||
// Private constructor to enforce the use of the Load() method
|
||||
private AppLovinUpmManifest() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of AppLovinUpmManifest and loads the manifest.json file.
|
||||
/// </summary>
|
||||
/// <returns>An instance of AppLovinUpmManifest</returns>
|
||||
public static AppLovinUpmManifest Load()
|
||||
{
|
||||
return new AppLovinUpmManifest { manifest = GetManifest() };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a scoped registry in the manifest.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the registry</param>
|
||||
/// <param name="url">The url of the registry</param>
|
||||
/// <param name="scopes">The scopes of the registry</param>
|
||||
public void AddOrUpdateRegistry(string name, string url, List<string> scopes)
|
||||
{
|
||||
var registry = GetRegistry(name);
|
||||
if (registry == null)
|
||||
{
|
||||
var registries = GetRegistries();
|
||||
if (registries == null) return;
|
||||
|
||||
registries.Add(new Dictionary<string, object>
|
||||
{
|
||||
{KeyName, name},
|
||||
{KeyUrl, url},
|
||||
{KeyScopes, scopes}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateRegistry(registry, scopes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the manifest by serializing it back to JSON and writing to file.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
var content = Json.Serialize(manifest, true);
|
||||
File.WriteAllText(ManifestPath, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a dependency to the manifest.
|
||||
/// </summary>
|
||||
/// <param name="packageName">The name of the package to add</param>
|
||||
/// <param name="version">The version of the package to add</param>
|
||||
public void AddPackageDependency(string packageName, string version)
|
||||
{
|
||||
var manifestDependencies = GetDependencies();
|
||||
manifestDependencies[packageName] = version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a dependency from the manifest.
|
||||
/// </summary>
|
||||
/// <param name="packageName">The name of the package to remove</param>
|
||||
public void RemovePackageDependency(string packageName)
|
||||
{
|
||||
var manifestDependencies = GetDependencies();
|
||||
manifestDependencies.Remove(packageName);
|
||||
}
|
||||
|
||||
#region Utility
|
||||
|
||||
/// <summary>
|
||||
/// Returns the manifest.json file as a dictionary.
|
||||
/// </summary>
|
||||
private static Dictionary<string, object> GetManifest()
|
||||
{
|
||||
if (!File.Exists(ManifestPath))
|
||||
{
|
||||
throw new Exception("Manifest not Found!");
|
||||
}
|
||||
|
||||
var manifestJson = File.ReadAllText(ManifestPath);
|
||||
if (string.IsNullOrEmpty(manifestJson))
|
||||
{
|
||||
throw new Exception("Manifest is empty!");
|
||||
}
|
||||
|
||||
var deserializedManifest = Json.Deserialize(manifestJson) as Dictionary<string, object>;
|
||||
if (deserializedManifest == null)
|
||||
{
|
||||
throw new Exception("Failed to deserialize manifest");
|
||||
}
|
||||
|
||||
return deserializedManifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manifest's dependencies section.
|
||||
/// </summary>
|
||||
/// <returns>The dependencies section of the manifest.</returns>
|
||||
private Dictionary<string, object> GetDependencies()
|
||||
{
|
||||
var dependencies = manifest["dependencies"] as Dictionary<string, object>;
|
||||
if (dependencies == null)
|
||||
{
|
||||
throw new Exception("No dependencies found in manifest.");
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manifest's registries section. Creates a new registries section if one does not exist.
|
||||
/// </summary>
|
||||
/// <returns>The registries section of the manifest.</returns>
|
||||
private List<object> GetRegistries()
|
||||
{
|
||||
EnsureScopedRegistryExists();
|
||||
return manifest[KeyScopedRegistry] as List<object>;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a scoped registry with the given name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the registry</param>
|
||||
/// <returns>Returns the registry, or null if it can't be found</returns>
|
||||
private Dictionary<string, object> GetRegistry(string name)
|
||||
{
|
||||
var registries = GetRegistries();
|
||||
if (registries == null) return null;
|
||||
|
||||
return registries
|
||||
.OfType<Dictionary<string, object>>()
|
||||
.FirstOrDefault(registry => MaxSdkUtils.GetStringFromDictionary(registry, KeyName).Equals(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the section for scoped registries in the manifest.json file if it doesn't exist.
|
||||
/// </summary>
|
||||
private void EnsureScopedRegistryExists()
|
||||
{
|
||||
if (manifest.ContainsKey(KeyScopedRegistry)) return;
|
||||
|
||||
manifest.Add(KeyScopedRegistry, new List<object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a registry to make sure it contains the new scopes.
|
||||
/// </summary>
|
||||
/// <param name="registry">The registry to update</param>
|
||||
/// <param name="newScopes">The scopes we want added to the registry</param>
|
||||
private static void UpdateRegistry(Dictionary<string, object> registry, List<string> newScopes)
|
||||
{
|
||||
var scopes = MaxSdkUtils.GetListFromDictionary(registry, KeyScopes);
|
||||
if (scopes == null)
|
||||
{
|
||||
registry[KeyScopes] = new List<string>(newScopes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add scopes that are not already in the list
|
||||
var uniqueNewScopes = newScopes.Where(scope => !scopes.Contains(scope)).ToList();
|
||||
scopes.AddRange(uniqueNewScopes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40e2ad4a252104688b8627f82e806b2e
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/AppLovinUpmManifest.cs
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinUpmManifest.cs
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "MaxSdk.Scripts.IntegrationManager.Editor",
|
||||
"references": [
|
||||
"MaxSdk.Scripts"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a10a05a8449c42519fd80f2b8b580de3
|
||||
labels:
|
||||
- al_max
|
||||
- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/MaxSdk.IntegrationManager.Editor.asmdef
|
||||
timeCreated: 1591749873
|
||||
Reference in New Issue
Block a user