using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Ubisoft.Hotel.Localisation; namespace Ubisoft.Hotel.Package.Editor { public class PlatformBuildProperty_Android : PackageProperty { public static PlatformBuildProperty_Android CreateInstanceWithParams(string name) { PlatformBuildProperty_Android returnValue = CreateInstance(); returnValue.name = name; return returnValue; } private static readonly string PACKAGE_NAME = "com.ubisoft.hotel.core"; private static readonly string GRADLE_PROPERTIES_FILE_NAME = "gradleTemplate.properties"; private static readonly string GRADLE_BASE_FILE_NAME = "baseProjectTemplate.gradle"; private static readonly string PLUGINS_PACKAGEMANAGER_ANDROIDLIB_NAME = "com.ubisoft.hotel.packagemanager.androidlib"; private static readonly string PLUGINS_PACKAGE_MANAGER_ANDROIDLIB_PACKAGE_SPACE_PATH = $"Package/Editor/Assets/{PLUGINS_PACKAGEMANAGER_ANDROIDLIB_NAME}"; private static readonly string PLUGINS_PACKAGEMANAGER_ANDROIDLIB_APP_SPACE_PATH = $"{PackageManagerHelper.ASSETS_HOTEL_PLUGINS_PATH}/Android/{PLUGINS_PACKAGEMANAGER_ANDROIDLIB_NAME}"; private static readonly string PLUGINS_GRADLE_PROPERTIES_PATH = $"Assets/Plugins/Android/{GRADLE_PROPERTIES_FILE_NAME}"; private static readonly string PLUGINS_GRADLE_BASE_PATH = $"Assets/Plugins/Android/{GRADLE_BASE_FILE_NAME}"; private const string GRADLE_PROPERTY_USE_ANDROID_X = "android.useAndroidX"; private static readonly List GRADLE_PROPERTIES = new List() { GRADLE_PROPERTY_USE_ANDROID_X }; private const string EOL = "\n"; private const string TAB = "\t"; private const string TAB_3 = TAB + TAB + TAB; private const string REPOSITORIES_PLACEHOLDER = "**ARTIFACTORYREPOSITORY**"; private const string TOKEN_HOTEL_BEGIN = "** HOTEL BEGIN **"; private const string TOKEN_HOTEL_END = "** HOTEL END **"; private const string GRADLE_PROPERTIES_HOTEL_BEGIN = TOKEN_HOTEL_BEGIN; private const string GRADLE_PROPERTIES_HOTEL_END = TOKEN_HOTEL_END; private const string GRADLE_BASE_HOTEL_BEGIN = "//" + TOKEN_HOTEL_BEGIN; private const string GRADLE_BASE_HOTEL_END = "//" + TOKEN_HOTEL_END; [SerializeField] private List m_localisedAppNames; [SerializeField] private EBoolean m_useAndroidX; [SerializeField] private List m_mavenRepositories; protected List LocalisedAppNames { get { return m_localisedAppNames; } set { m_localisedAppNames = value; } } public EBoolean UseAndroidX { get { return m_useAndroidX; } set { m_useAndroidX = value; } } public List MavenRepositories { get { return m_mavenRepositories; } private set { m_mavenRepositories = value; } } public void Clear() { if (LocalisedAppNames != null) { LocalisedAppNames.Clear(); } UseAndroidX = EBoolean.AsUnitySettings; if (MavenRepositories != null) { MavenRepositories.Clear(); } } public override void Apply(BuildSuite buildSuite) { if (buildSuite != null) { buildSuite.OrderProperty(this); } } public void Join(PlatformBuildProperty_Android property) { if (property != null) { if (property.LocalisedAppNames != null) { foreach (AppName appName in property.LocalisedAppNames) { AddLocalisedAppName(appName.LanguageId, appName.Name, appName.ShortName); } } if (property.UseAndroidX != EBoolean.AsUnitySettings) { if (UseAndroidX == EBoolean.AsUnitySettings) { UseAndroidX = property.UseAndroidX; } else if (UseAndroidX != property.UseAndroidX) { Debug.EditorLogError("Conflict: UseAndroidX needs to be enabled or disabled for all properties: " + Debug.FormatTextInUserContext(property.name)); } } if (property.MavenRepositories != null && property.MavenRepositories.Count > 0) { if (MavenRepositories == null) { MavenRepositories = new List(); } foreach (string repository in property.MavenRepositories) { if (!MavenRepositories.Contains(repository)) { MavenRepositories.Add(repository); } } } } } public void PreBuild() { // Clear Hotel PackageManager android lib folder if (HtAssetDatabase.ExistsFolder(PLUGINS_PACKAGEMANAGER_ANDROIDLIB_APP_SPACE_PATH)) { HtAssetDatabase.DeleteFolder(PLUGINS_PACKAGEMANAGER_ANDROIDLIB_APP_SPACE_PATH); } ProcessLocalisedAppNames(); bool newProperties = GradleProperties_Process(); bool newBase = GradleBase_Process(); if (newProperties || newBase) { AssetDatabase.Refresh(); } } #pragma warning disable IDE0060 // Remove unused parameter public void PostBuild(string pathToBuiltProject) #pragma warning restore IDE0060 // Remove unused parameter { } public void AddAppNameLocalisations(List appNameLocalisations) { if (appNameLocalisations != null) { foreach (AppName appName in appNameLocalisations) { AddLocalisedAppName(appName.LanguageId, appName.Name, appName.ShortName); } } } public void AddLocalisedAppName(ELanguageId languageId, string appName, string shortAppName = null) { if (LocalisedAppNames == null) { LocalisedAppNames = new List(); } AppName thisAppName = GetAppNameByLanguageId(languageId); if (thisAppName == null) { if (LocalisedAppNames == null) { LocalisedAppNames = new List(); } if (shortAppName == null) { shortAppName = appName; } thisAppName = new AppName(languageId, appName, shortAppName); LocalisedAppNames.Add(thisAppName); } else { Debug.EditorLogError($"Duplicate app name for language {languageId}."); } } private AppName GetAppNameByLanguageId(ELanguageId languageId) { AppName returnValue = null; if (LocalisedAppNames != null) { int count = LocalisedAppNames.Count; for (int i = 0; i < count && returnValue == null; ++i) { if (LocalisedAppNames[i].LanguageId == languageId) { returnValue = LocalisedAppNames[i]; } } } return returnValue; } private void ProcessLocalisedAppNames() { if (LocalisedAppNames != null) { string path; AppName appName; int count = LocalisedAppNames.Count; if (count > 0) { // Copy Hotel PackageManager androidlib to app space string srcUnityPath = HtAssetUtility.GetAssetInPackagePath(PACKAGE_NAME, PLUGINS_PACKAGE_MANAGER_ANDROIDLIB_PACKAGE_SPACE_PATH); string dstUnityPath = PLUGINS_PACKAGEMANAGER_ANDROIDLIB_APP_SPACE_PATH; if (HtAssetDatabase.ExistsFolder(srcUnityPath)) { if (!HtAssetDatabase.ExistsFolder(dstUnityPath)) { HtAssetDatabase.CreateFolder(dstUnityPath); } HtAssetDatabase.CopyFolder(srcUnityPath, dstUnityPath); } // Generate resources that contain the localised app name inside Hotel PackageManager androidlib for (int i = 0; i < count; ++i) { appName = LocalisedAppNames[i]; if (appName.LanguageId == LocalisationUtils.DEFAULT_LANGUAGE_ID) { // Default language sets PlayerSettings.productName so it's used for all languages that don't have an own app name PlayerSettings.productName = appName.Name; } else { string languageIsoCode = LocalisationUtils.LanguageIdToIsoCode(appName.LanguageId); // According to Android naming conventions 'r' needs to be between region and language languageIsoCode = languageIsoCode.Replace("_", "-r"); path = $"{dstUnityPath}/res/values-{languageIsoCode}"; HtAssetDatabase.CreateFolder(path); string valuesLanguageFile = HtAssetDatabase.UnityPathCombine(path, "app_name_strings.xml"); string fileContent = $"{EOL}"; fileContent += $"{EOL}"; fileContent = fileContent + $"{TAB}" + appName.Name + $"{EOL}"; fileContent += $"{EOL}"; //Debug.EditorLog("Writing " + valuesLanguageFile + " content: " + fileContent); File.WriteAllText(valuesLanguageFile, fileContent); } } } } } public void AddMavenRepository(string value) { if (MavenRepositories == null) { MavenRepositories = new List(); } if (!MavenRepositories.Contains(value)) { MavenRepositories.Add(value); } } private static string GetPackageUnityPath() { string assetsDirectoryUnityPath = "Package/Editor/Assets"; #if HT_PACKAGE_DEV return $"Assets/Hotel/Core/{assetsDirectoryUnityPath}"; #else return HtAssetUtility.GetAssetInPackagePath(PACKAGE_NAME, assetsDirectoryUnityPath); #endif } #region gradle_properties private bool GradleProperties_Process() { bool returnValue = false; bool needsToUseGradleProperties = UseAndroidX != EBoolean.AsUnitySettings; if (needsToUseGradleProperties) { if (!GradleProperties_ExistsFile()) { GradleProperties_CreateFile(); returnValue = true; } GradleProperties_PatchFile(); } return returnValue; } private bool GradleProperties_ExistsFile() { return HtAssetDatabase.ExistsFile(PLUGINS_GRADLE_PROPERTIES_PATH); } private void GradleProperties_CreateFile() { string srcUnityPath = HtAssetDatabase.UnityPathCombine(GetPackageUnityPath(), GRADLE_PROPERTIES_FILE_NAME); HtAssetDatabase.CopyFile(srcUnityPath, PLUGINS_GRADLE_PROPERTIES_PATH, false); } private void GradleProperties_PatchFile() { string output = ""; Dictionary handledProperties = new Dictionary(); string[] tokens; string[] lines = File.ReadAllLines(PLUGINS_GRADLE_PROPERTIES_PATH); bool writeIsEnabled = true; int count = lines.Length; for (int i = 0; i < count; ++i) { if (lines[i].Contains(GRADLE_PROPERTIES_HOTEL_BEGIN)) { writeIsEnabled = false; } else if (writeIsEnabled) { tokens = lines[i].Split('='); if (tokens.Length != 2) { output = GradleProperties_AddLine(output, lines[i]); } else if (GradleProperties_IsHandled(tokens[0])) { handledProperties.Add(tokens[0], tokens[1]); } else { output = GradleProperties_AddLine(output, lines[i]); } } else if (lines[i].Contains(GRADLE_PROPERTIES_HOTEL_END)) { writeIsEnabled = true; } } output = GradleProperties_AddLine(output, GRADLE_PROPERTIES_HOTEL_BEGIN); if (UseAndroidX != EBoolean.AsUnitySettings) { if (handledProperties.ContainsKey(GRADLE_PROPERTY_USE_ANDROID_X)) { _ = handledProperties.Remove(GRADLE_PROPERTY_USE_ANDROID_X); } handledProperties.Add(GRADLE_PROPERTY_USE_ANDROID_X, UseAndroidX == EBoolean.True ? "true" : "false"); } foreach (KeyValuePair pair in handledProperties) { output = GradleProperties_AddLine(output, $"{pair.Key}={pair.Value}"); } output = GradleProperties_AddLine(output, GRADLE_PROPERTIES_HOTEL_END); File.WriteAllText(PLUGINS_GRADLE_PROPERTIES_PATH, output); } private string GradleProperties_AddLine(string output, string line) { if (output.Length > 0) { output += EOL; } output += line; return output; } private bool GradleProperties_IsHandled(string value) { return !string.IsNullOrEmpty(value) && GRADLE_PROPERTIES.Contains(value); } #endregion #region gradle_base /* public static void Test() { if (!GradleBase_ExistsFile()) { GradleBase_CreateFile(); } List testRepositories = new List() { "myTest1", "myTest2" }; GradleBase_PatchFile(testRepositories); AssetDatabase.Refresh(); } */ private static bool GradleBase_ExistsFile() { return HtAssetDatabase.ExistsFile(PLUGINS_GRADLE_BASE_PATH); } private static void GradleBase_CreateFile() { string srcUnityPath = HtAssetDatabase.UnityPathCombine(GetPackageUnityPath(), GRADLE_BASE_FILE_NAME); HtAssetDatabase.CopyFile(srcUnityPath, PLUGINS_GRADLE_BASE_PATH, false); } private static void GradleBase_PatchFile(List repositories) { string output = ""; bool originalPlaceHolder = false; string[] lines = File.ReadAllLines(PLUGINS_GRADLE_BASE_PATH); bool writeIsEnabled = true; int count = lines.Length; for (int i = 0; i < count; ++i) { if (lines[i].Contains(GRADLE_BASE_HOTEL_BEGIN)) { writeIsEnabled = false; } else if (writeIsEnabled) { if (lines[i].Contains(REPOSITORIES_PLACEHOLDER)) { originalPlaceHolder = lines[i].Trim().Length > REPOSITORIES_PLACEHOLDER.Length; } output = GradleBase_AddLine(output, lines[i]); } else if (lines[i].Contains(GRADLE_BASE_HOTEL_END)) { writeIsEnabled = true; } } if (repositories != null && repositories.Count > 0) { string mavenRepositories = originalPlaceHolder ? $"{EOL}{TAB_3}" : ""; mavenRepositories += $"{REPOSITORIES_PLACEHOLDER}{EOL}{TAB_3}{GRADLE_BASE_HOTEL_BEGIN}"; for (int i = 0; i < repositories.Count; ++i) { mavenRepositories += EOL + TAB_3 + "maven {url \"" + repositories[i] + "\" }"; } mavenRepositories += $"{EOL}{TAB_3}{GRADLE_BASE_HOTEL_END}"; if (originalPlaceHolder) { mavenRepositories += EOL; } output = output.Replace(REPOSITORIES_PLACEHOLDER, mavenRepositories); } File.WriteAllText(PLUGINS_GRADLE_BASE_PATH, output); } private static string GradleBase_AddLine(string output, string line) { if (output.Length > 0) { output += EOL; } output += line; return output; } private bool GradleBase_Process() { bool returnValue = false; bool needsToUseGradleBase = GradleBase_ExistsFile() || (MavenRepositories != null && MavenRepositories.Count > 0); if (needsToUseGradleBase) { if (!GradleBase_ExistsFile()) { GradleBase_CreateFile(); returnValue = true; } GradleBase_PatchFile(MavenRepositories); } return returnValue; } #endregion } }