namespace Guru.Editor { using System.Xml; using System.IO; using UnityEngine; using System.Collections.Generic; /// /// Android 配置修改器 /// public class AndroidManifestDoc { private const string TargetPath = "Plugins/Android/AndroidManifest.xml"; private const string XmlnsAndroid = "xmlns:android"; private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android"; private const string XmlnsTools= "xmlns:tools"; private const string NamespaceTools = "http://schemas.android.com/tools"; private const string UserPermission = "uses-permission"; private const string MetaData = "meta-data"; private const string KName = "name"; private XmlDocument _doc; public XmlDocument Doc => _doc; private string _docPath; private bool _isReady = false; private XmlElement _manifestNode; private XmlElement _applicationNode; private XmlElement _queriesNode; #region Initiallize /// /// 加载文件 /// /// /// public static AndroidManifestDoc Load(string docPath = "") { if (string.IsNullOrEmpty(docPath)) { docPath = Path.GetFullPath(Path.Combine(Application.dataPath, TargetPath)); } if (!File.Exists(docPath)) { Debug.LogError($"--- File not found: {docPath}"); return null; } var mod = new AndroidManifestDoc(); mod.ReadFromPath(docPath); return mod; } public static AndroidManifestDoc Read(string xmlStr, string docPath = "") { var mod = new AndroidManifestDoc(); mod.ReadFromXml(xmlStr, docPath); return mod; } /// /// 从文件路径读取 /// /// public void ReadFromPath(string docPath) { _isReady = false; if (File.Exists(docPath)) { var xmlStr = File.ReadAllText(docPath); ReadFromXml(xmlStr, docPath); } else { Debug.LogError($"--- File not found: {docPath}"); } } public void ReadFromXml(string xmlStr, string docPath = "") { _doc = new XmlDocument(); _doc.LoadXml(xmlStr); if(!string.IsNullOrEmpty(docPath)) _docPath = docPath; Init(); } /// /// Initializes the Doc /// private void Init() { // --- Root Nodes --- _manifestNode = _doc.SelectSingleNode("manifest") as XmlElement; _applicationNode = _doc.SelectSingleNode("manifest/application") as XmlElement; _queriesNode = _doc.SelectSingleNode("manifest/queries") as XmlElement; AddXmlnsAndroid(); AddXmlnsTools(); _isReady = true; } /// /// Save Doc /// public void Save(string docPath = "") { if (_isReady) { if (!string.IsNullOrEmpty(docPath)) _docPath = docPath; if (!string.IsNullOrEmpty(_docPath)) { var dir = Directory.GetParent(_docPath); if(!dir.Exists) dir.Create(); _doc.Save(_docPath); } } } #endregion #region Node Opreation public static bool AddAttribute(XmlElement node, string key, string value) { if (node != null) { if (node.HasAttribute(key)) { node.Attributes[key].Value = value; } else { node.SetAttribute(key, value); } return true; } return false; } #endregion #region API public bool AddXmlnsAndroid() { return AddAttribute(_manifestNode, XmlnsAndroid, NamespaceAndroid); } public bool AddXmlnsTools() { return AddAttribute(_manifestNode, XmlnsTools, NamespaceTools); } /// /// Add Replace Item /// /// public void AddApplicationReplaceItem(string item) { if (_applicationNode != null) { List items = new List(5); if (_applicationNode.HasAttribute("replace", NamespaceTools)) { var arr = _applicationNode.GetAttribute("replace",NamespaceTools).Split(','); if(arr != null && arr.Length > 0) { items.AddRange(arr); } } if (!items.Contains(item)) items.Add(item); _applicationNode.SetAttribute("replace", NamespaceTools, string.Join(",", items)); } } public void SetApplicationAttribute(string key, string value) { if (_applicationNode != null) { _applicationNode.SetAttribute(key, NamespaceAndroid, value); } } /// /// Set metadata /// /// /// /// /// public void SetMetadata(string key, string value, string valueName = "value", string keyName = KName) { if (_doc == null || !_isReady) return; XmlElement node = null; if (!TryGetMetadata(key, out node, keyName)) { node = _doc.CreateElement(MetaData); _applicationNode?.AppendChild(node); } node.SetAttribute(keyName, NamespaceAndroid, key); node.SetAttribute(valueName, NamespaceAndroid, value); } /// /// 添加权限 /// /// /// public void AddPermission(string key, string keyName = KName) { if (_doc == null || !_isReady) return; XmlElement node = null; if(!TryGetPermission(key, out node, keyName)) { node = _doc.CreateElement(UserPermission); _manifestNode?.AppendChild(node); } node.SetAttribute(keyName, NamespaceAndroid, key); } /// /// 删除 Permission /// /// /// /// /// public void RemovePermission(string key, string value = "remove", string keyName = "name", string valueName = "node") { if (_doc == null || !_isReady) return; XmlElement node = null; if(!TryGetPermission(key, out node, keyName)) { node = _doc.CreateElement(UserPermission); _manifestNode?.AppendChild(node); } node.SetAttribute(keyName, NamespaceAndroid, key); node.SetAttribute(valueName, NamespaceTools, value); } public bool SetPackageName(string packageName) { if (_manifestNode != null) { _manifestNode.Attributes["package"].Value = packageName; return true; } return false; } /// /// 添加 Queries Intent /// /// /// /// public void AddQueriesIntent(string value, string keyName = "name") { if (_queriesNode == null) { _queriesNode = _doc.CreateElement("queries"); _manifestNode?.AppendChild(_queriesNode); } var intentList = _queriesNode.SelectNodes("intent"); if (intentList != null) { foreach (XmlElement intent in intentList) { var action = intent?.SelectSingleNode("action") as XmlElement; if (action != null && action.GetAttribute(keyName, NamespaceAndroid) == value) { return; // Has injected,skip ... } } } // Inject new intent node XmlElement intentNode = _doc.CreateElement("intent"); _queriesNode.AppendChild(intentNode); XmlElement actionNode = _doc.CreateElement("action"); intentNode.AppendChild(actionNode); actionNode.SetAttribute(keyName, NamespaceAndroid, value); } #endregion #region Data Opration public bool TryGetMetadata(string name, out XmlElement node, string keyName = KName) { node = null; if(_applicationNode != null) { var list = _applicationNode.SelectNodes(MetaData); if (list != null) { foreach (XmlElement e in list) { if (e.GetAttribute(keyName, NamespaceAndroid) == name) { node = e; return true; } } } } return false; } public bool TryGetPermission(string name, out XmlElement node, string keyName = KName) { node = null; if(_manifestNode != null) { var list = _manifestNode.SelectNodes(UserPermission); if (list != null) { foreach (XmlElement e in list) { if (e.GetAttribute(keyName, NamespaceAndroid) == name) { node = e; return true; } } } } return false; } public bool TryRootNode(string nodeName, out XmlElement node) { node = null; if (_doc != null) { node = _doc.SelectSingleNode(nodeName) as XmlElement; return node != null; } return false; } #endregion #region Output public override string ToString() { if (_doc != null) return _doc.InnerXml; return this.ToString(); } #endregion } }