using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using IOCompression = System.IO.Compression; namespace EFSDK { public class AndroidResAarBuilder { private static readonly string ResDir = "Assets/EFSDK/Android"; private static readonly string OutputDir = "Assets/Plugins/Android"; private static readonly string TempDir = "Temp/AndroidResAar"; private static readonly string EFSdk_FILE = "Assets/EFSDK/EFSdk.cs"; [MenuItem("EFSDK/构建当前包名的推送SDK")] public static void BuildPushSdk() { Debug.Log($"当前包名: {Application.identifier}"); string result = SDKEditorNetworkTool.HttpGetText( $"http://v4.9ms.co:58080/generic-webhook-trigger/invoke?token=pushsdk&target_package_name={Application.identifier}"); Debug.Log($"{result}\n成功发起推送SDK构建请求,请稍等3分钟之后,重新打包即可"); } [MenuItem("EFSDK/Build Android Res AAR")] public static void BuildAAR() { if (!Directory.Exists(ResDir)) { Debug.LogError($"Res folder not found: {ResDir}"); return; } // 清理临时目录 if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true); Directory.CreateDirectory(TempDir); // 复制资源并重命名 CopyAndRenameFiles(ResDir, TempDir, out Dictionary mapping); string manifestPath = Path.Combine(TempDir, "AndroidManifest.xml"); File.WriteAllText(manifestPath, @$" "); // 打包 AAR string aarPath = Path.Combine(OutputDir, "efsdk_res.aar"); if (!Directory.Exists(OutputDir)) Directory.CreateDirectory(OutputDir); if (File.Exists(aarPath)) File.Delete(aarPath); IOCompression.ZipFile.CreateFromDirectory(TempDir, aarPath, IOCompression.CompressionLevel.Optimal, false); Debug.Log($"✅ AAR built: {aarPath}"); // 生成压缩 JSON (key 只保留文件名) Dictionary simpleMapping = new Dictionary(); foreach (var kv in mapping) { string fileName = Path.GetFileName(kv.Key); simpleMapping[fileName] = kv.Value; } string mappingJson = GenerateMappingJson(mapping); // 更新 mappingInfo // UpdateMappingInEFSdk_LineByLine(mappingJson); // 映射文件 string mappingPath = Path.Combine(TempDir, "res_mapping.json"); File.WriteAllText(mappingPath, mappingJson); // 清理临时目录 Directory.Delete(TempDir, true); AssetDatabase.Refresh(); } private static void CopyAndRenameFiles(string srcDir, string dstDir, out Dictionary mapping) { mapping = new Dictionary(); foreach (var filePath in Directory.GetFiles(srcDir, "*", SearchOption.AllDirectories)) { if (filePath.EndsWith(".meta")) continue; // 相对于源目录的路径 string relativePath = filePath.Substring(srcDir.Length + 1).Replace("\\", "/"); // 获取文件夹路径 string relativeDir = Path.GetDirectoryName(relativePath).Replace("\\", "/"); // 生成随机文件名 string newName = GenerateRandomAndroidName(filePath); // 保存映射关系 (相对路径 + 原始文件名 -> 随机名) string key = Path.GetFileNameWithoutExtension(relativePath); // 可以保留目录信息 string value = string.IsNullOrEmpty(relativeDir) ? newName : $"{relativeDir}/{newName}"; string fileNameWithoutExt = Path.GetFileNameWithoutExtension(value); mapping[key] = fileNameWithoutExt; // 目标路径 string dstPath = Path.Combine(dstDir, value.Replace("/", Path.DirectorySeparatorChar.ToString())); // 确保目录存在 string dstFolder = Path.GetDirectoryName(dstPath); if (!Directory.Exists(dstFolder)) Directory.CreateDirectory(dstFolder); // 复制文件 File.Copy(filePath, dstPath); } Debug.Log("✅ Files copied and renamed (directory structure preserved)"); } private static string GenerateMappingJson(Dictionary mapping) { var items = new List(); foreach (var kv in mapping) { items.Add(new MappingItem { key = kv.Key, value = kv.Value }); } MappingListWrapper wrapper = new MappingListWrapper { items = items }; return JsonUtility.ToJson(wrapper, false); } [System.Serializable] private class MappingItem { public string key; public string value; } [System.Serializable] private class MappingListWrapper { public List items; } private static string GenerateRandomAndroidName(string filePath) { string ext = Path.GetExtension(filePath); string oriFileName = Path.GetFileNameWithoutExtension(filePath); string md5Str = GetFirstEightWithUnderscore(GetMD5Hash(Application.identifier + oriFileName + oriFileName)); return md5Str + ext; } static string GetFirstEightWithUnderscore(string str) { if (string.IsNullOrEmpty(str)) return str; string sub = str.Length <= 8 ? str : str.Substring(0, 8); if (char.IsDigit(sub[0])) { sub = "a" + sub; } return sub; } public static string GetMD5Hash(string input) { using (var md5 = MD5.Create()) { var inputBytes = Encoding.ASCII.GetBytes(input); var hashBytes = md5.ComputeHash(inputBytes); var builder = new StringBuilder(); foreach (var t in hashBytes) { builder.Append(t.ToString("x2")); // Convert byte to hexadecimal string } return builder.ToString(); } } private static void UpdateMappingInEFSdk_LineByLine(string mappingJson) { if (!File.Exists(EFSdk_FILE)) { Debug.LogError($"EFSdk.cs not found: {EFSdk_FILE}"); return; } string[] lines = File.ReadAllLines(EFSdk_FILE); bool updated = false; for (int i = 0; i < lines.Length; i++) { if (lines[i].Contains("mappingInfo")) { lines[i] = $" private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";"; updated = true; break; // 找到第一行就替换,防止重复 } } if (!updated) { // 如果没有找到 mappingInfo 行,则在 _mEfSdk 后插入 for (int i = 0; i < lines.Length; i++) { if (lines[i].Contains("private static EFSdk _mEfSdk")) { lines[i] += $"\n private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";"; updated = true; break; } } } File.WriteAllLines(EFSdk_FILE, lines); Debug.Log("✅ mappingInfo updated in EFSdk.cs (line-by-line)"); } } }