533 lines
22 KiB
C#
533 lines
22 KiB
C#
using System.Text.RegularExpressions;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.IO;
|
|
using System;
|
|
using System.Xml;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Debug = System.Diagnostics.Debug;
|
|
using AppodealAds.Unity.Editor.Utils;
|
|
|
|
|
|
namespace AppodealAds.Unity.Editor.Checkers
|
|
{
|
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
public class MultidexActivator : CheckingStep
|
|
{
|
|
#region Constants
|
|
|
|
//Templates in Unity Editor Data folder
|
|
private const string gradleDefaultTemplatePath = "PlaybackEngines/AndroidPlayer/Tools/GradleTemplates";
|
|
public const string manifestDefaultTemplatePath = "PlaybackEngines/AndroidPlayer/Apk/AndroidManifest.xml";
|
|
|
|
//Paths without leading Assets folder
|
|
public const string androidPluginsPath = "Plugins/Android";
|
|
public const string gradleTemplateName = "mainTemplate.gradle";
|
|
public const string manifestTemplateName = "AndroidManifest.xml";
|
|
public const string appodealTemplatesPath = "Appodeal/InternalResources";
|
|
private const string appodealDexesPath = "Assets/Plugins/Android/appodeal/assets/dex";
|
|
|
|
//Gradle search lines
|
|
public const string GRADLE_GOOGLE_REPOSITORY = "google()";
|
|
public const string GRADLE_GOOGLE_REPOSITORY_COMPAT = "maven { url \"https://maven.google.com\" }";
|
|
public const string GRADLE_DEPENDENCIES = "**DEPS**";
|
|
public const string GRADLE_APP_ID = "**APPLICATIONID**";
|
|
public const string GRADLE_USE_PROGUARD = "useProguard";
|
|
public const string GRADLE_MULTIDEX_DEPENDENCY_WO_VERSION = "androidx.multidex:multidex:";
|
|
public const string GRAFLE_DEFAULT_CONFIG = "defaultConfig";
|
|
public const string COMPILE_OPTIONS = "compileOptions {";
|
|
public const string GRADLE_JAVA_VERSION_1_8 = "JavaVersion.VERSION_1_8";
|
|
public const string GRADLE_SOURCE_CAPABILITY = "sourceCompatibility ";
|
|
public const string GRADLE_TARGET_CAPATILITY = "targetCompatibility ";
|
|
|
|
//Gradle add lines
|
|
public const string GRADLE_COMPILE = "compile ";
|
|
public const string GRADLE_IMPLEMENTATION = "implementation ";
|
|
public const string GRADLE_MULTIDEX_DEPENDENCY = "'androidx.multidex:multidex:2.0.1'";
|
|
public const string GRADLE_MULTIDEX_ENABLE = "multiDexEnabled true";
|
|
|
|
//Manifest add lines
|
|
public const string manifestMutlidexApp = "androidx.multidex.MultiDexApplication";
|
|
|
|
#endregion
|
|
|
|
public override string getName()
|
|
{
|
|
return "Android Multidex Settings";
|
|
}
|
|
|
|
public override bool isRequiredForPlatform(BuildTarget target)
|
|
{
|
|
return target == BuildTarget.Android;
|
|
}
|
|
|
|
public override List<FixProblemInstruction> check()
|
|
{
|
|
var instructions = new List<FixProblemInstruction>();
|
|
if (isNodexBuild() && EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
|
|
{
|
|
if (!AppodealUnityUtils.isGradleEnabled())
|
|
{
|
|
if (!AppodealUnityUtils.isGradleAvailable())
|
|
{
|
|
var fix = new FixProblemInstruction(
|
|
"Gradle should be enabled if you use nodex version of Appodeal Plugin. " +
|
|
"But your version of Unity doesn't support gradle build system.Please update your Unity or use export to Eclipse or Android Studio.",
|
|
false);
|
|
instructions.Add(fix);
|
|
return instructions;
|
|
}
|
|
|
|
instructions.Add(new EnableGradle());
|
|
}
|
|
|
|
var customGradeScript = getCustomGradleScriptPath();
|
|
if (string.IsNullOrEmpty(customGradeScript) || !File.Exists(customGradeScript))
|
|
{
|
|
if (File.Exists(getDefaultGradleTemplate()))
|
|
{
|
|
instructions.Add(new CopyGradleScriptAndEnableMultidex());
|
|
}
|
|
else
|
|
{
|
|
var instr = new FixProblemInstruction(
|
|
"There is no build.gradle template in your Unity. " +
|
|
"Please ensure that your copy of Unity isn't crashed and contact Appodeal Support team.",
|
|
false);
|
|
instructions.Add(instr);
|
|
return instructions;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var settings = new ImportantGradleSettings(customGradeScript);
|
|
if (!settings.isMultiDexAddedCompletely())
|
|
{
|
|
instructions.Add(new EnableMultidexInGradle(customGradeScript));
|
|
}
|
|
|
|
if (!settings.isJavaVersionIncluded())
|
|
{
|
|
instructions.Add(new EnableJavaVersion(customGradeScript));
|
|
}
|
|
}
|
|
|
|
var customManifestPath = getCustomManifestPath();
|
|
if (string.IsNullOrEmpty(customManifestPath) || !File.Exists(customManifestPath))
|
|
{
|
|
instructions.Add(new CopyManifestTemplateAndAddNameAttribute());
|
|
}
|
|
else
|
|
{
|
|
var appNode = getApplicationNode(customManifestPath);
|
|
var ns = appNode.GetNamespaceOfPrefix("android");
|
|
if (!appNode.HasAttribute("name", ns))
|
|
{
|
|
instructions.Add(new AddNameAttrubute(appNode, ns));
|
|
}
|
|
else
|
|
{
|
|
if (appNode.GetAttribute("name", ns) == manifestMutlidexApp) return instructions;
|
|
var fix = new FixProblemInstruction(
|
|
"We found that you use custom Application class in this project. " +
|
|
"Please ensure that your application class meets the multidex requirements (see the official android multidex documentation).",
|
|
false);
|
|
instructions.Add(fix);
|
|
}
|
|
}
|
|
}
|
|
else if (!isNodexBuild() && isMultidexEnabledSimpleCheck() &&
|
|
EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
|
|
{
|
|
var fix = new FixProblemInstruction(
|
|
"We found that multidex is enabled in your project. To avoid possible problems please " +
|
|
"replace current version of Appodeal plugin with nodex version.", false);
|
|
instructions.Add(fix);
|
|
}
|
|
|
|
return instructions;
|
|
}
|
|
|
|
public static XmlElement getApplicationNode(string manifestPath)
|
|
{
|
|
var doc = new XmlDocument();
|
|
doc.Load(manifestPath);
|
|
var manNode = AppodealUnityUtils.XmlFindChildNode(doc, "manifest");
|
|
var appNode = (XmlElement) AppodealUnityUtils.XmlFindChildNode(manNode, "application");
|
|
return appNode;
|
|
}
|
|
|
|
private static string getCustomGradleScriptPath()
|
|
{
|
|
var androidDirectory = new DirectoryInfo(Path.Combine("Assets", androidPluginsPath));
|
|
var filePaths = androidDirectory.GetFiles("*.gradle");
|
|
return filePaths.Length > 0
|
|
? Path.Combine(Path.Combine(Application.dataPath, androidPluginsPath), filePaths[0].Name)
|
|
: null;
|
|
}
|
|
|
|
public static string getCustomManifestPath()
|
|
{
|
|
return AppodealUnityUtils.combinePaths(Application.dataPath, androidPluginsPath, manifestTemplateName);
|
|
}
|
|
|
|
private static bool isNodexBuild()
|
|
{
|
|
var dexesDirectory = new DirectoryInfo(appodealDexesPath);
|
|
if (!dexesDirectory.Exists)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
var dexes = dexesDirectory.GetFiles("*.dex");
|
|
return dexes.Length == 0;
|
|
}
|
|
|
|
private static bool isMultidexEnabledSimpleCheck()
|
|
{
|
|
var path = getCustomGradleScriptPath();
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var settings = new ImportantGradleSettings(path);
|
|
return settings.isMultidexEnabled() && AppodealUnityUtils.isGradleEnabled();
|
|
}
|
|
|
|
public static string getDefaultGradleTemplate()
|
|
{
|
|
var defaultGradleTemplateFullName = AppodealUnityUtils.combinePaths(
|
|
EditorApplication.applicationContentsPath,
|
|
gradleDefaultTemplatePath,
|
|
gradleTemplateName);
|
|
if (File.Exists(defaultGradleTemplateFullName)) return defaultGradleTemplateFullName;
|
|
var unixAppContentsPath =
|
|
Path.GetDirectoryName(Path.GetDirectoryName(EditorApplication.applicationContentsPath));
|
|
defaultGradleTemplateFullName = AppodealUnityUtils.combinePaths(unixAppContentsPath,
|
|
gradleDefaultTemplatePath,
|
|
gradleTemplateName);
|
|
|
|
return defaultGradleTemplateFullName;
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
|
|
public class ImportantGradleSettings
|
|
{
|
|
public readonly bool googleRepositoryPresented;
|
|
public readonly bool multidexDependencyPresented;
|
|
public readonly bool multidexEnabled;
|
|
public readonly bool deprecatedProguardPresented;
|
|
public readonly bool compileOptions;
|
|
public readonly bool sourceCapability;
|
|
public readonly bool targetCapability;
|
|
public bool defaultConfig;
|
|
|
|
public ImportantGradleSettings(string gradlePath)
|
|
{
|
|
var gradleScriptFullText = File.ReadAllText(gradlePath);
|
|
multidexDependencyPresented =
|
|
gradleScriptFullText.Contains(MultidexActivator.GRADLE_MULTIDEX_DEPENDENCY_WO_VERSION);
|
|
multidexEnabled = gradleScriptFullText.Contains(MultidexActivator.GRADLE_MULTIDEX_ENABLE);
|
|
deprecatedProguardPresented = gradleScriptFullText.Contains(MultidexActivator.GRADLE_USE_PROGUARD);
|
|
compileOptions = gradleScriptFullText.Contains(MultidexActivator.COMPILE_OPTIONS);
|
|
sourceCapability =
|
|
gradleScriptFullText.Contains(MultidexActivator.GRADLE_SOURCE_CAPABILITY +
|
|
MultidexActivator.GRADLE_JAVA_VERSION_1_8);
|
|
targetCapability =
|
|
gradleScriptFullText.Contains(MultidexActivator.GRADLE_TARGET_CAPATILITY +
|
|
MultidexActivator.GRADLE_JAVA_VERSION_1_8);
|
|
defaultConfig = gradleScriptFullText.Contains(MultidexActivator.GRAFLE_DEFAULT_CONFIG);
|
|
|
|
var allprojects = getModule("allprojects", gradleScriptFullText);
|
|
googleRepositoryPresented = allprojects.Contains(MultidexActivator.GRADLE_GOOGLE_REPOSITORY) ||
|
|
gradleScriptFullText.Contains(MultidexActivator
|
|
.GRADLE_GOOGLE_REPOSITORY_COMPAT);
|
|
}
|
|
|
|
public bool isJavaVersionIncluded()
|
|
{
|
|
return sourceCapability && targetCapability;
|
|
}
|
|
|
|
public bool isMultidexEnabled()
|
|
{
|
|
return multidexDependencyPresented || multidexEnabled;
|
|
}
|
|
|
|
public bool isMultiDexAddedCompletely()
|
|
{
|
|
return multidexDependencyPresented && multidexEnabled && !deprecatedProguardPresented &&
|
|
googleRepositoryPresented;
|
|
}
|
|
|
|
private static string getModule(string moduleName, string fulltext)
|
|
{
|
|
var startIndex = fulltext.IndexOf(moduleName, StringComparison.Ordinal);
|
|
startIndex = fulltext.IndexOf('{', startIndex) + 1;
|
|
var currentIndex = startIndex;
|
|
var braces = 1;
|
|
while (braces != 0)
|
|
{
|
|
var c = fulltext[currentIndex];
|
|
switch (c)
|
|
{
|
|
case '{':
|
|
braces++;
|
|
break;
|
|
case '}':
|
|
braces--;
|
|
break;
|
|
}
|
|
|
|
currentIndex++;
|
|
}
|
|
|
|
return fulltext.Substring(startIndex, currentIndex - startIndex);
|
|
}
|
|
}
|
|
|
|
internal class EnableGradle : FixProblemInstruction
|
|
{
|
|
public EnableGradle() : base(
|
|
"Gradle build system isn't enabled.\n(required for nodex version of Appodeal Plugin " +
|
|
"and if you aren't going to export your project to Android Studio or Eclipse).", true)
|
|
{
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
AppodealUnityUtils.enableGradleBuildSystem();
|
|
}
|
|
}
|
|
|
|
internal class CopyGradleScriptAndEnableMultidex : FixProblemInstruction
|
|
{
|
|
public CopyGradleScriptAndEnableMultidex() : base("Assets/Plugins/Android/mainTemplate.gradle not found.\n" +
|
|
"(required if you aren't going to export your project to Android Studio or Eclipse)",
|
|
true)
|
|
{
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
//EditorApplication.applicationContentsPath is different for macos and win. need to fix to reach manifest and gradle templates
|
|
var defaultGradleTemplateFullName = MultidexActivator.getDefaultGradleTemplate();
|
|
|
|
var destGradleScriptFullName = AppodealUnityUtils.combinePaths(Application.dataPath,
|
|
MultidexActivator.androidPluginsPath,
|
|
MultidexActivator.gradleTemplateName);
|
|
//Prefer to use build.gradle from the box. Just in case.
|
|
if (File.Exists(defaultGradleTemplateFullName))
|
|
{
|
|
File.Copy(defaultGradleTemplateFullName, destGradleScriptFullName);
|
|
}
|
|
|
|
AssetDatabase.ImportAsset(AppodealUnityUtils.absolute2Relative(destGradleScriptFullName),
|
|
ImportAssetOptions.ForceUpdate);
|
|
|
|
//There are no multidex settings in default build.gradle but they can add that stuff.
|
|
var settings = new ImportantGradleSettings(destGradleScriptFullName);
|
|
|
|
if (!settings.isMultiDexAddedCompletely())
|
|
new EnableMultidexInGradle(destGradleScriptFullName).fixProblem();
|
|
}
|
|
}
|
|
|
|
internal class EnableMultidexInGradle : FixProblemInstruction
|
|
{
|
|
private readonly string path;
|
|
|
|
public EnableMultidexInGradle(string gradleScriptPath) : base(
|
|
"Multidex isn't enabled. mainTemplate.gradle should be edited " +
|
|
"according to the official documentation:\nhttps://developer.android.com/studio/build/multidex", true)
|
|
{
|
|
path = gradleScriptPath;
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
var settings = new ImportantGradleSettings(path);
|
|
var leadingWhitespaces = "";
|
|
string line;
|
|
var prevLine = "";
|
|
var modifiedGradle = "";
|
|
var gradleScript = new StreamReader(path);
|
|
string multidexDependency;
|
|
var comparsionUnityVersionWith20182 =
|
|
AppodealUnityUtils.compareVersions(Application.unityVersion, "2018.2");
|
|
if (comparsionUnityVersionWith20182 < 0)
|
|
{
|
|
multidexDependency = MultidexActivator.GRADLE_COMPILE + MultidexActivator.GRADLE_MULTIDEX_DEPENDENCY;
|
|
}
|
|
else
|
|
{
|
|
multidexDependency = MultidexActivator.GRADLE_IMPLEMENTATION +
|
|
MultidexActivator.GRADLE_MULTIDEX_DEPENDENCY;
|
|
}
|
|
|
|
while ((line = gradleScript.ReadLine()) != null)
|
|
{
|
|
if (!settings.multidexDependencyPresented && line.Contains(MultidexActivator.GRADLE_DEPENDENCIES))
|
|
{
|
|
modifiedGradle += leadingWhitespaces + multidexDependency + Environment.NewLine;
|
|
}
|
|
|
|
if (!settings.multidexEnabled && line.Contains(MultidexActivator.GRADLE_APP_ID))
|
|
{
|
|
modifiedGradle += leadingWhitespaces + MultidexActivator.GRADLE_MULTIDEX_ENABLE +
|
|
Environment.NewLine;
|
|
}
|
|
|
|
if (settings.deprecatedProguardPresented && line.Contains(MultidexActivator.GRADLE_USE_PROGUARD))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
modifiedGradle += line + Environment.NewLine;
|
|
leadingWhitespaces = Regex.Match(line, "^\\s*").Value;
|
|
if (line.Contains("repositories") && prevLine.Contains("allprojects") &&
|
|
!settings.googleRepositoryPresented)
|
|
{
|
|
leadingWhitespaces += leadingWhitespaces;
|
|
modifiedGradle += leadingWhitespaces + MultidexActivator.GRADLE_GOOGLE_REPOSITORY_COMPAT +
|
|
Environment.NewLine;
|
|
}
|
|
|
|
prevLine = line;
|
|
}
|
|
|
|
gradleScript.Close();
|
|
File.WriteAllText(path, modifiedGradle);
|
|
AssetDatabase.ImportAsset(AppodealUnityUtils.absolute2Relative(path), ImportAssetOptions.ForceUpdate);
|
|
}
|
|
}
|
|
|
|
internal class EnableJavaVersion : FixProblemInstruction
|
|
{
|
|
private readonly string path;
|
|
|
|
public EnableJavaVersion(string gradleScriptPath) : base("Java version isn't included to mainTemplate.gradle",
|
|
true)
|
|
{
|
|
path = gradleScriptPath;
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
var settings = new ImportantGradleSettings(path);
|
|
var leadingWhitespaces = " ";
|
|
const string additionalWhiteSpaces = "";
|
|
string line;
|
|
var modifiedGradle = "";
|
|
|
|
var gradleScript = new StreamReader(path);
|
|
|
|
while ((line = gradleScript.ReadLine()) != null)
|
|
{
|
|
if (line.Contains(MultidexActivator.GRAFLE_DEFAULT_CONFIG))
|
|
{
|
|
if (!settings.compileOptions)
|
|
{
|
|
modifiedGradle += additionalWhiteSpaces + leadingWhitespaces +
|
|
MultidexActivator.COMPILE_OPTIONS + Environment.NewLine;
|
|
}
|
|
|
|
if (!settings.sourceCapability)
|
|
{
|
|
modifiedGradle += leadingWhitespaces + leadingWhitespaces +
|
|
MultidexActivator.GRADLE_SOURCE_CAPABILITY
|
|
+ MultidexActivator.GRADLE_JAVA_VERSION_1_8 + Environment.NewLine;
|
|
}
|
|
|
|
if (!settings.targetCapability)
|
|
{
|
|
modifiedGradle += leadingWhitespaces + leadingWhitespaces +
|
|
MultidexActivator.GRADLE_TARGET_CAPATILITY
|
|
+ MultidexActivator.GRADLE_JAVA_VERSION_1_8 + Environment.NewLine;
|
|
}
|
|
|
|
if (!settings.targetCapability)
|
|
{
|
|
modifiedGradle += leadingWhitespaces + "}" + Environment.NewLine;
|
|
}
|
|
|
|
if (!settings.targetCapability)
|
|
{
|
|
modifiedGradle += leadingWhitespaces + Environment.NewLine;
|
|
}
|
|
}
|
|
|
|
modifiedGradle += line + Environment.NewLine;
|
|
leadingWhitespaces = Regex.Match(line, "^\\s*").Value;
|
|
}
|
|
|
|
gradleScript.Close();
|
|
File.WriteAllText(path, modifiedGradle);
|
|
AssetDatabase.ImportAsset(AppodealUnityUtils.absolute2Relative(path), ImportAssetOptions.ForceUpdate);
|
|
}
|
|
}
|
|
|
|
internal class CopyManifestTemplateAndAddNameAttribute : FixProblemInstruction
|
|
{
|
|
public CopyManifestTemplateAndAddNameAttribute() : base(
|
|
"Assets/Plugins/Android/AndroidManifest.xml not found.\n", true)
|
|
{
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
var fullManifestName = MultidexActivator.getCustomManifestPath();
|
|
var defaultTemplate = Path.Combine(EditorApplication.applicationContentsPath,
|
|
MultidexActivator.manifestDefaultTemplatePath);
|
|
if (!File.Exists(defaultTemplate))
|
|
{
|
|
var unixAppContentsPath =
|
|
Path.GetDirectoryName(Path.GetDirectoryName(EditorApplication.applicationContentsPath));
|
|
Debug.Assert(unixAppContentsPath != null, nameof(unixAppContentsPath) + " != null");
|
|
defaultTemplate = Path.Combine(unixAppContentsPath, MultidexActivator.manifestDefaultTemplatePath);
|
|
}
|
|
|
|
var appodealTemplate = AppodealUnityUtils.combinePaths(Application.dataPath,
|
|
MultidexActivator.appodealTemplatesPath,
|
|
MultidexActivator.manifestTemplateName);
|
|
File.Copy(File.Exists(defaultTemplate) ? defaultTemplate : appodealTemplate, fullManifestName);
|
|
AssetDatabase.ImportAsset(AppodealUnityUtils.absolute2Relative(fullManifestName),
|
|
ImportAssetOptions.ForceUpdate);
|
|
|
|
var appNode = MultidexActivator.getApplicationNode(fullManifestName);
|
|
var ns = appNode.GetNamespaceOfPrefix("android");
|
|
if (!appNode.HasAttribute("name", ns))
|
|
{
|
|
new AddNameAttrubute(appNode, ns).fixProblem();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class AddNameAttrubute : FixProblemInstruction
|
|
{
|
|
private readonly XmlElement appNode;
|
|
private readonly string ns;
|
|
|
|
public AddNameAttrubute(XmlElement appNode, string ns) : base(
|
|
"Problem in AndroidManifest.xml found.\nApplication tag should contain android:" +
|
|
"name attribute with value: " + MultidexActivator.manifestMutlidexApp, true)
|
|
{
|
|
this.appNode = appNode;
|
|
this.ns = ns;
|
|
}
|
|
|
|
public override void fixProblem()
|
|
{
|
|
var fullManifestName = MultidexActivator.getCustomManifestPath();
|
|
appNode.SetAttribute("name", ns, MultidexActivator.manifestMutlidexApp);
|
|
Debug.Assert(appNode.OwnerDocument != null, "appNode.OwnerDocument != null");
|
|
appNode.OwnerDocument.Save(fullManifestName);
|
|
AssetDatabase.ImportAsset(AppodealUnityUtils.absolute2Relative(fullManifestName),
|
|
ImportAssetOptions.ForceUpdate);
|
|
}
|
|
}
|
|
} |