You've already forked Commercialization.tapadn
286 lines
12 KiB
C#
286 lines
12 KiB
C#
#if UNITY_ANDROID
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.Android;
|
|
using UnityEngine;
|
|
|
|
namespace Tapadn_Adapter.Editor
|
|
{
|
|
public sealed class TapadnBuildAndroidProcess : IPostGenerateGradleAndroidProject
|
|
{
|
|
private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
|
|
private static readonly XNamespace ToolsNamespace = "http://schemas.android.com/tools";
|
|
|
|
public int callbackOrder => 180;
|
|
|
|
public void OnPostGenerateGradleAndroidProject(string path)
|
|
{
|
|
ProcessGradleProperties(path);
|
|
ProcessAndroidManifest(path);
|
|
}
|
|
|
|
public static void ProcessAndroidManifest(string path)
|
|
{
|
|
var unityManifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");
|
|
var launcherManifestPath = Path.Combine(path, "../launcher/src/main/AndroidManifest.xml");
|
|
var providerTargetManifestPath = File.Exists(launcherManifestPath) ? launcherManifestPath : unityManifestPath;
|
|
|
|
if (!File.Exists(unityManifestPath))
|
|
{
|
|
Debug.LogWarning($"[TapADN] AndroidManifest.xml is missing: {unityManifestPath}");
|
|
return;
|
|
}
|
|
|
|
var unityManifest = XDocument.Load(unityManifestPath);
|
|
EnsureManifestPatch(unityManifest, true, false);
|
|
unityManifest.Save(unityManifestPath);
|
|
|
|
if (File.Exists(providerTargetManifestPath))
|
|
{
|
|
var providerManifest = XDocument.Load(providerTargetManifestPath);
|
|
EnsureManifestPatch(providerManifest, false, true);
|
|
providerManifest.Save(providerTargetManifestPath);
|
|
}
|
|
|
|
var resXmlPath = Path.Combine(path, "src/main/res/xml");
|
|
Directory.CreateDirectory(resXmlPath);
|
|
var sourceXmlPath = GetEditorAssetPath("tapad_ad_file_path.xml");
|
|
if (!string.IsNullOrEmpty(sourceXmlPath) && File.Exists(sourceXmlPath))
|
|
{
|
|
File.Copy(sourceXmlPath, Path.Combine(resXmlPath, "tapad_ad_file_path.xml"), true);
|
|
}
|
|
}
|
|
|
|
private static void EnsureManifestPatch(XDocument manifest, bool includeWechatActivity, bool includeTapadProvider)
|
|
{
|
|
var manifestElement = manifest.Element("manifest");
|
|
if (manifestElement == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureNamespace(manifestElement, "tools", ToolsNamespace.NamespaceName);
|
|
|
|
EnsurePermission(manifestElement, "android.permission.INTERNET");
|
|
EnsurePermission(manifestElement, "android.permission.ACCESS_NETWORK_STATE");
|
|
EnsurePermission(manifestElement, "android.permission.READ_PHONE_STATE");
|
|
EnsurePermission(manifestElement, "android.permission.QUERY_ALL_PACKAGES");
|
|
EnsurePermission(manifestElement, "android.permission.REQUEST_INSTALL_PACKAGES");
|
|
EnsurePermission(manifestElement, "android.permission.BLUETOOTH");
|
|
EnsurePermission(manifestElement, "android.permission.BLUETOOTH_CONNECT");
|
|
EnsurePermission(manifestElement, "android.permission.ACCESS_FINE_LOCATION");
|
|
EnsurePermission(manifestElement, "android.permission.ACCESS_COARSE_LOCATION");
|
|
EnsurePermission(manifestElement, "android.permission.POST_NOTIFICATIONS");
|
|
EnsureWechatQueries(manifestElement);
|
|
|
|
var applicationElement = manifestElement.Element("application");
|
|
if (applicationElement == null)
|
|
{
|
|
applicationElement = new XElement("application");
|
|
manifestElement.Add(applicationElement);
|
|
}
|
|
|
|
SetUnityActivitySingleTop(applicationElement);
|
|
|
|
if (includeWechatActivity)
|
|
{
|
|
EnsureWechatEntryActivity(applicationElement);
|
|
}
|
|
|
|
if (includeTapadProvider)
|
|
{
|
|
EnsureTapadFileProvider(applicationElement);
|
|
SetOrAddAttribute(applicationElement, AndroidNamespace + "allowBackup", "false");
|
|
MergeToolsReplaceAttribute(applicationElement, "android:allowBackup");
|
|
}
|
|
}
|
|
|
|
private static void EnsurePermission(XElement manifestElement, string permissionName)
|
|
{
|
|
var exists = manifestElement.Elements("uses-permission")
|
|
.Any(element => element.Attribute(AndroidNamespace + "name")?.Value == permissionName);
|
|
if (exists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var permission = new XElement("uses-permission");
|
|
permission.Add(new XAttribute(AndroidNamespace + "name", permissionName));
|
|
if (permissionName == "android.permission.BLUETOOTH_CONNECT" ||
|
|
permissionName == "android.permission.POST_NOTIFICATIONS")
|
|
{
|
|
permission.Add(new XAttribute(ToolsNamespace + "targetApi", permissionName.EndsWith("CONNECT") ? "s" : "33"));
|
|
}
|
|
|
|
manifestElement.Add(permission);
|
|
}
|
|
|
|
private static void EnsureWechatQueries(XElement manifestElement)
|
|
{
|
|
var queries = manifestElement.Elements("queries").FirstOrDefault();
|
|
if (queries == null)
|
|
{
|
|
queries = new XElement("queries");
|
|
manifestElement.Add(queries);
|
|
}
|
|
|
|
var exists = queries.Elements("package")
|
|
.Any(element => element.Attribute(AndroidNamespace + "name")?.Value == "com.tencent.mm");
|
|
if (!exists)
|
|
{
|
|
var packageElement = new XElement("package");
|
|
packageElement.Add(new XAttribute(AndroidNamespace + "name", "com.tencent.mm"));
|
|
queries.Add(packageElement);
|
|
}
|
|
}
|
|
|
|
private static void EnsureWechatEntryActivity(XElement applicationElement)
|
|
{
|
|
RemoveElementsByAndroidName(applicationElement, "activity", ".wxapi.WXEntryActivity");
|
|
|
|
var activity = new XElement("activity");
|
|
activity.Add(new XAttribute(AndroidNamespace + "name", ".wxapi.WXEntryActivity"));
|
|
activity.Add(new XAttribute(AndroidNamespace + "label", "@string/app_name"));
|
|
activity.Add(new XAttribute(AndroidNamespace + "theme", "@android:style/Theme.Translucent.NoTitleBar"));
|
|
activity.Add(new XAttribute(AndroidNamespace + "exported", "true"));
|
|
activity.Add(new XAttribute(AndroidNamespace + "taskAffinity", Application.identifier));
|
|
activity.Add(new XAttribute(AndroidNamespace + "launchMode", "singleTop"));
|
|
applicationElement.Add(activity);
|
|
}
|
|
|
|
private static void EnsureTapadFileProvider(XElement applicationElement)
|
|
{
|
|
RemoveElementsByAndroidName(applicationElement, "provider", "com.tapsdk.tapad.internal.TapADFileProvider");
|
|
|
|
var provider = new XElement("provider");
|
|
provider.Add(new XAttribute(AndroidNamespace + "name", "com.tapsdk.tapad.internal.TapADFileProvider"));
|
|
provider.Add(new XAttribute(AndroidNamespace + "authorities", "${applicationId}.com.tds.ad.fileprovider"));
|
|
provider.Add(new XAttribute(AndroidNamespace + "exported", "false"));
|
|
provider.Add(new XAttribute(AndroidNamespace + "grantUriPermissions", "true"));
|
|
provider.Add(new XAttribute(ToolsNamespace + "replace", "android:authorities"));
|
|
|
|
var metadata = new XElement("meta-data");
|
|
metadata.Add(new XAttribute(AndroidNamespace + "name", "android.support.FILE_PROVIDER_PATHS"));
|
|
metadata.Add(new XAttribute(AndroidNamespace + "resource", "@xml/tapad_ad_file_path"));
|
|
provider.Add(metadata);
|
|
|
|
applicationElement.Add(provider);
|
|
}
|
|
|
|
private static void SetUnityActivitySingleTop(XElement applicationElement)
|
|
{
|
|
foreach (var activity in applicationElement.Elements("activity"))
|
|
{
|
|
var name = activity.Attribute(AndroidNamespace + "name")?.Value;
|
|
if (name == "com.unity3d.player.UnityPlayerActivity")
|
|
{
|
|
SetOrAddAttribute(activity, AndroidNamespace + "launchMode", "singleTop");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void RemoveElementsByAndroidName(XElement parent, string localName, string androidName)
|
|
{
|
|
parent.Elements(localName)
|
|
.Where(element => element.Attribute(AndroidNamespace + "name")?.Value == androidName)
|
|
.ToList()
|
|
.ForEach(element => element.Remove());
|
|
}
|
|
|
|
private static void ProcessGradleProperties(string path)
|
|
{
|
|
var root = Directory.GetParent(path)?.FullName;
|
|
if (string.IsNullOrEmpty(root))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var gradlePropertiesPath = Path.Combine(root, "gradle.properties");
|
|
var lines = File.Exists(gradlePropertiesPath)
|
|
? File.ReadAllLines(gradlePropertiesPath).ToList()
|
|
: new List<string>();
|
|
|
|
UpsertProperty(lines, "android.useAndroidX", "true");
|
|
UpsertProperty(lines, "android.enableJetifier", "true");
|
|
|
|
File.WriteAllText(gradlePropertiesPath, string.Join("\n", lines) + "\n");
|
|
}
|
|
|
|
private static void UpsertProperty(List<string> lines, string key, string value)
|
|
{
|
|
var prefix = key + "=";
|
|
var index = lines.FindIndex(line => line.TrimStart().StartsWith(prefix));
|
|
if (index >= 0)
|
|
{
|
|
lines[index] = prefix + value;
|
|
return;
|
|
}
|
|
|
|
lines.Add(prefix + value);
|
|
}
|
|
|
|
private static void SetOrAddAttribute(XElement element, XName attributeName, string value)
|
|
{
|
|
var attribute = element.Attribute(attributeName);
|
|
if (attribute == null)
|
|
{
|
|
element.Add(new XAttribute(attributeName, value));
|
|
return;
|
|
}
|
|
|
|
attribute.SetValue(value);
|
|
}
|
|
|
|
private static void MergeToolsReplaceAttribute(XElement element, params string[] replaceValues)
|
|
{
|
|
var replaceAttributeName = ToolsNamespace + "replace";
|
|
var mergedValues = new List<string>();
|
|
var currentValue = element.Attribute(replaceAttributeName)?.Value;
|
|
if (!string.IsNullOrWhiteSpace(currentValue))
|
|
{
|
|
mergedValues.AddRange(currentValue.Split(',')
|
|
.Select(value => value.Trim())
|
|
.Where(value => !string.IsNullOrWhiteSpace(value)));
|
|
}
|
|
|
|
foreach (var replaceValue in replaceValues)
|
|
{
|
|
if (!mergedValues.Contains(replaceValue))
|
|
{
|
|
mergedValues.Add(replaceValue);
|
|
}
|
|
}
|
|
|
|
element.SetAttributeValue(replaceAttributeName, string.Join(",", mergedValues));
|
|
}
|
|
|
|
private static void EnsureNamespace(XElement element, string prefix, string namespaceName)
|
|
{
|
|
var namespaceAttributeName = XNamespace.Xmlns + prefix;
|
|
if (element.Attribute(namespaceAttributeName) == null)
|
|
{
|
|
element.Add(new XAttribute(namespaceAttributeName, namespaceName));
|
|
}
|
|
}
|
|
|
|
private static string GetEditorAssetPath(string fileName)
|
|
{
|
|
var guids = AssetDatabase.FindAssets(Path.GetFileNameWithoutExtension(fileName));
|
|
foreach (var guid in guids)
|
|
{
|
|
var path = AssetDatabase.GUIDToAssetPath(guid);
|
|
if (Path.GetFileName(path) == fileName)
|
|
{
|
|
return path;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
#endif
|