Files

286 lines
12 KiB
C#
Raw Permalink Normal View History

#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