635 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			635 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | //  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 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 postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey); | |||
|  |             var bodyRaw = Encoding.UTF8.GetBytes(postJson); | |||
|  |             // Upload handler is automatically disposed when UnityWebRequest is disposed | |||
|  |             var uploadHandler = new UploadHandlerRaw(bodyRaw); | |||
|  |             uploadHandler.contentType = "application/json"; | |||
|  | 
 | |||
|  |             using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/cred")) | |||
|  |             { | |||
|  |                 unityWebRequest.method = UnityWebRequest.kHttpVerbPOST; | |||
|  |                 unityWebRequest.uploadHandler = uploadHandler; | |||
|  |                 unityWebRequest.downloadHandler = new DownloadHandlerBuffer(); | |||
|  | 
 | |||
|  |                 var operation = unityWebRequest.SendWebRequest(); | |||
|  | 
 | |||
|  |                 // Wait for the download to complete or the request to timeout. | |||
|  |                 while (!operation.isDone) { } | |||
|  | 
 | |||
|  | #if UNITY_2020_1_OR_NEWER | |||
|  |                 if (unityWebRequest.result != UnityWebRequest.Result.Success) | |||
|  | #else | |||
|  |                 if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError) | |||
|  | #endif | |||
|  |                 { | |||
|  |                     MaxSdkLogger.UserError("Failed to retrieve API Key for SDK Key: " + sdkKey + "with error: " + unityWebRequest.error); | |||
|  |                     return new AppLovinQualityServiceData(); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 try | |||
|  |                 { | |||
|  |                     return JsonUtility.FromJson<AppLovinQualityServiceData>(unityWebRequest.downloadHandler.text); | |||
|  |                 } | |||
|  |                 catch (Exception exception) | |||
|  |                 { | |||
|  |                     MaxSdkLogger.UserError("Failed to parse API Key." + exception); | |||
|  |                     return new AppLovinQualityServiceData(); | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         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 |