feat(channel): 支持渠道 Manifest 增量合并
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEngine;
|
||||
@@ -16,6 +17,10 @@ public class ChannelBuildTool
|
||||
private const string AndroidAddressablesContentState = "Assets/AddressableAssetsData/Android/addressables_content_state.bin";
|
||||
private const string DefaultProductName = "Fishing Travel";
|
||||
private const string EnjoyPayProductName = "Fishing Travel: Hook & Explore";
|
||||
private const string ChannelManifestLib = "channel_manifest_lib";
|
||||
private const string EmptyManifest = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />\n";
|
||||
private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
|
||||
private static readonly XNamespace ToolsNamespace = "http://schemas.android.com/tools";
|
||||
|
||||
// ==================== Channel Switching ====================
|
||||
|
||||
@@ -29,9 +34,10 @@ public class ChannelBuildTool
|
||||
}
|
||||
|
||||
Debug.Log($"[ChannelSwitch] Switching to {channel}...");
|
||||
CleanChannelManifestLibBuildArtifacts();
|
||||
|
||||
// 1. Sync root Android config files
|
||||
SyncRootAndroidConfigFiles(cfgDir);
|
||||
SyncRootAndroidConfigFiles(channel, cfgDir);
|
||||
|
||||
// 2. Sync channel SDK adapter Java files to tysdk package
|
||||
SyncChannelAdapterFiles(cfgDir);
|
||||
@@ -110,6 +116,7 @@ public class ChannelBuildTool
|
||||
{
|
||||
try
|
||||
{
|
||||
CleanChannelManifestLibBuildArtifacts();
|
||||
SwitchChannel(channel);
|
||||
|
||||
var fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
|
||||
@@ -190,11 +197,12 @@ public class ChannelBuildTool
|
||||
}
|
||||
}
|
||||
|
||||
private static void SyncRootAndroidConfigFiles(string cfgDir)
|
||||
private static void SyncRootAndroidConfigFiles(string channel, string cfgDir)
|
||||
{
|
||||
CopyRootConfigFiles(cfgDir, "*.gradle");
|
||||
CopyRootConfigFiles(cfgDir, "*.properties");
|
||||
CopyRootConfigFile(cfgDir, "AndroidManifest.xml");
|
||||
SetMainManifestChannel(channel);
|
||||
SyncChannelManifestLib(channel, cfgDir);
|
||||
}
|
||||
|
||||
private static void SyncChannelAdapterFiles(string cfgDir)
|
||||
@@ -215,18 +223,134 @@ public class ChannelBuildTool
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyRootConfigFiles(string cfgDir, string searchPattern)
|
||||
private static void SyncChannelManifestLib(string channel, string cfgDir)
|
||||
{
|
||||
foreach (var src in Directory.GetFiles(cfgDir, searchPattern, SearchOption.TopDirectoryOnly))
|
||||
var dest = Path.Combine(AssetsPlugins, ChannelManifestLib, "src");
|
||||
if (channel == "GooglePlay")
|
||||
{
|
||||
CopyRootConfigFile(src);
|
||||
WriteEmptyChannelManifestLib(dest);
|
||||
Debug.Log($"[ChannelSwitch] Manifest lib reset to empty default. Channel={channel}");
|
||||
return;
|
||||
}
|
||||
|
||||
var srcManifest = Path.Combine(cfgDir, "AndroidManifest.xml");
|
||||
if (!File.Exists(srcManifest))
|
||||
{
|
||||
throw new FileNotFoundException($"[ChannelSwitch] Channel manifest not found: {srcManifest}");
|
||||
}
|
||||
|
||||
WriteChannelManifestLib(srcManifest, Path.Combine(cfgDir, "res"), dest);
|
||||
Debug.Log($"[ChannelSwitch] Manifest: {srcManifest} -> {Path.Combine(dest, "main", "AndroidManifest.xml")}");
|
||||
}
|
||||
|
||||
private static void SetMainManifestChannel(string channel)
|
||||
{
|
||||
var manifestPath = Path.Combine(AssetsPlugins, "AndroidManifest.xml");
|
||||
if (!File.Exists(manifestPath))
|
||||
{
|
||||
throw new FileNotFoundException($"[ChannelSwitch] Main AndroidManifest not found: {manifestPath}");
|
||||
}
|
||||
|
||||
var doc = XDocument.Load(manifestPath);
|
||||
var root = doc.Root;
|
||||
var application = root?.Elements().FirstOrDefault(x => x.Name.LocalName == "application");
|
||||
if (application == null)
|
||||
{
|
||||
throw new InvalidDataException($"[ChannelSwitch] Main AndroidManifest application node not found: {manifestPath}");
|
||||
}
|
||||
|
||||
var channelMeta = application.Elements()
|
||||
.FirstOrDefault(x => x.Name.LocalName == "meta-data" &&
|
||||
(string)x.Attribute(AndroidNamespace + "name") == "com.arkgame.ft.channel");
|
||||
if (channelMeta == null)
|
||||
{
|
||||
channelMeta = new XElement("meta-data",
|
||||
new XAttribute(AndroidNamespace + "name", "com.arkgame.ft.channel"));
|
||||
application.Add(channelMeta);
|
||||
}
|
||||
|
||||
channelMeta.SetAttributeValue(AndroidNamespace + "value", channel);
|
||||
SetMainManifestBackupRules(application, channel);
|
||||
doc.Save(manifestPath);
|
||||
AssetDatabase.ImportAsset(manifestPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
|
||||
Debug.Log($"[ChannelSwitch] Main manifest channel -> {channel}");
|
||||
}
|
||||
|
||||
private static void SetMainManifestBackupRules(XElement application, string channel)
|
||||
{
|
||||
const string fullBackupContent = "android:fullBackupContent";
|
||||
var replaceAttr = application.Attribute(ToolsNamespace + "replace");
|
||||
var replaceItems = replaceAttr?.Value
|
||||
.Split(',')
|
||||
.Select(x => x.Trim())
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.ToList() ?? new List<string>();
|
||||
|
||||
if (channel == "Flexion")
|
||||
{
|
||||
application.SetAttributeValue(AndroidNamespace + "fullBackupContent", "@xml/flexion_backup_rules");
|
||||
if (!replaceItems.Contains(fullBackupContent))
|
||||
{
|
||||
replaceItems.Add(fullBackupContent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
application.SetAttributeValue(AndroidNamespace + "fullBackupContent", null);
|
||||
replaceItems.RemoveAll(x => x == fullBackupContent);
|
||||
}
|
||||
|
||||
application.SetAttributeValue(ToolsNamespace + "replace",
|
||||
replaceItems.Count > 0 ? string.Join(",", replaceItems) : null);
|
||||
}
|
||||
|
||||
private static void WriteEmptyChannelManifestLib(string dest)
|
||||
{
|
||||
if (Directory.Exists(dest))
|
||||
{
|
||||
Directory.Delete(dest, true);
|
||||
}
|
||||
|
||||
var manifestDir = Path.Combine(dest, "main");
|
||||
Directory.CreateDirectory(manifestDir);
|
||||
File.WriteAllText(Path.Combine(manifestDir, "AndroidManifest.xml"), EmptyManifest);
|
||||
}
|
||||
|
||||
private static void CleanChannelManifestLibBuildArtifacts()
|
||||
{
|
||||
var buildDir = Path.Combine(AssetsPlugins, ChannelManifestLib, "build");
|
||||
if (Directory.Exists(buildDir))
|
||||
{
|
||||
Directory.Delete(buildDir, true);
|
||||
}
|
||||
|
||||
var buildMeta = buildDir + ".meta";
|
||||
if (File.Exists(buildMeta))
|
||||
{
|
||||
File.Delete(buildMeta);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyRootConfigFile(string cfgDir, string fileName)
|
||||
private static void WriteChannelManifestLib(string srcManifest, string srcResDir, string dest)
|
||||
{
|
||||
var src = Path.Combine(cfgDir, fileName);
|
||||
if (File.Exists(src))
|
||||
if (Directory.Exists(dest))
|
||||
{
|
||||
Directory.Delete(dest, true);
|
||||
}
|
||||
|
||||
var manifestDir = Path.Combine(dest, "main");
|
||||
Directory.CreateDirectory(manifestDir);
|
||||
File.Copy(srcManifest, Path.Combine(manifestDir, "AndroidManifest.xml"), true);
|
||||
|
||||
if (Directory.Exists(srcResDir))
|
||||
{
|
||||
CopyDirectoryClean(srcResDir, Path.Combine(manifestDir, "res"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyRootConfigFiles(string cfgDir, string searchPattern)
|
||||
{
|
||||
foreach (var src in Directory.GetFiles(cfgDir, searchPattern, SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
CopyRootConfigFile(src);
|
||||
}
|
||||
@@ -240,6 +364,27 @@ public class ChannelBuildTool
|
||||
Debug.Log($"[ChannelSwitch] Config: {src} -> {dest}");
|
||||
}
|
||||
|
||||
private static void CopyDirectoryClean(string src, string dest)
|
||||
{
|
||||
if (Directory.Exists(dest))
|
||||
{
|
||||
Directory.Delete(dest, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(dest);
|
||||
foreach (var srcFile in Directory.GetFiles(src, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (srcFile.EndsWith(".meta")) continue;
|
||||
|
||||
var relativePath = srcFile.Substring(src.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
var destFile = Path.Combine(dest, relativePath);
|
||||
var destDir = Path.GetDirectoryName(destFile);
|
||||
if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);
|
||||
|
||||
File.Copy(srcFile, destFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAddressablesProfile(string channel)
|
||||
{
|
||||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
|
||||
Reference in New Issue
Block a user