using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Azerion.BlueStack.Editor.Android; using Azerion.BlueStack.Editor.iOS; using System.Linq; using System.Xml; namespace Azerion.BlueStack.Editor { [CustomEditor(typeof(BlueStackSettings))] public class BlueStackSettingsEditor : UnityEditor.Editor { private const string BlueStackSettingsResDir = "Assets/Resources"; private const string BlueStackSettingsFile = "BlueStackSettings"; private const string BlueStackSettingsFileExtension = ".asset"; SerializedProperty iOSMediationNetworkListProperty; SerializedProperty androidMediationNetworkListProperty; SerializedProperty iOSAdmobAppIdProperty; SerializedProperty androidAdmobAppIdProperty; public static event EventHandler> OnIOSDependenciesUpdateEvent; public static event EventHandler> OnAndroidDependenciesUpdateEvent; [MenuItem("Azerion/BlueStack/Settings")] public static void OpenInspector() { if (DoesBlueStackSettingsExist()) { Selection.activeObject = BlueStackSettings.Instance; EditorApplication.ExecuteMenuItem("Window/General/Inspector"); } } private static bool DoesBlueStackSettingsExist() { if (BlueStackSettings.Instance == null) { Debug.LogWarning("BlueStackSettings Instance is not available!"); try { Directory.CreateDirectory(BlueStackSettingsResDir); BlueStackSettings.Instance = ScriptableObject.CreateInstance(); string assetPath = Path.Combine(BlueStackSettingsResDir, BlueStackSettingsFile); string assetPathWithExtension = Path.ChangeExtension( assetPath, BlueStackSettingsFileExtension ); AssetDatabase.CreateAsset(BlueStackSettings.Instance, assetPathWithExtension); AssetDatabase.SaveAssets(); return true; } catch (Exception e) { // handle errors Debug.LogWarning("Failed to create a BlueStackSettings scriptable object!"); Debug.LogError(e.Message); return false; } } else { return true; } } // This is Required when updating the SDK. // Initialize and Sync dependencies when project loaded in Unity Editor. // This logic is now moved to OnPostprocessAllAssets of BlueStackAssetPostProcessor // [InitializeOnLoadMethod] // static void OnProjectLoadedInEditor() // { // Initialize(); // } void OnEnable() { Initialize(); } public static void Initialize() { if (!DoesBlueStackSettingsExist()) return; BlueStackSettings.Instance.IOSDependencies.Clear(); BlueStackSettings.Instance.AndroidDependencies.Clear(); DependencyProvider.SyncDependenciesXMLs(); var androidDependencies = DependencyProvider.GetBlueStackDependencies(new AndroidDependencyParser()); var iOSDependencies = DependencyProvider.GetBlueStackDependencies(new IOSDependencyParser()); BlueStackSettings.Instance.AndroidDependencies = androidDependencies; BlueStackSettings.Instance.IOSDependencies = iOSDependencies; } public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); serializedObject.Update(); androidAdmobAppIdProperty = serializedObject.FindProperty("admobAppIdAndroid"); iOSAdmobAppIdProperty = serializedObject.FindProperty("admobAppIdIOS"); EditorGUILayout.PropertyField( androidAdmobAppIdProperty, new GUIContent("Admob AppId Android: ") ); EditorGUILayout.PropertyField( iOSAdmobAppIdProperty, new GUIContent("Admob AppId IOS: ") ); // Android Dependencies EditorGUILayout.Separator(); EditorGUILayout.LabelField("Android Dependencies:", EditorStyles.boldLabel); var androidToggles = new List<(BlueStackDependency dep, bool newActive)>(); foreach (var dep in BlueStackSettings.Instance.AndroidDependencies) { if (dep.Alias != "BlueStack") { bool newActive = GUILayout.Toggle(dep.Active, dep.Alias ?? dep.Name); if (newActive != dep.Active) { androidToggles.Add((dep, newActive)); } } } foreach (var (dep, newActive) in androidToggles) { UpdateDependencyActiveState(dep, newActive, isIOS: false); } // iOS Dependencies EditorGUILayout.Separator(); EditorGUILayout.LabelField("iOS Dependencies:", EditorStyles.boldLabel); var iosToggles = new List<(BlueStackDependency dep, bool newActive)>(); foreach (var dep in BlueStackSettings.Instance.IOSDependencies) { if (dep.Alias != "BlueStack") { bool newActive = GUILayout.Toggle(dep.Active, dep.Alias ?? dep.Name); if (newActive != dep.Active) { iosToggles.Add((dep, newActive)); } } } foreach (var (dep, newActive) in iosToggles) { UpdateDependencyActiveState(dep, newActive, isIOS: true); } bool anyAndroidChanged = androidToggles.Count > 0; bool anyIOSChanged = iosToggles.Count > 0; if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); if (anyAndroidChanged && OnAndroidDependenciesUpdateEvent != null) { OnAndroidDependenciesUpdateEvent.Invoke(this,BlueStackSettings.Instance.AndroidDependencies); } if (anyIOSChanged && OnIOSDependenciesUpdateEvent != null) { OnIOSDependenciesUpdateEvent.Invoke(this,BlueStackSettings.Instance.IOSDependencies); } } } private void UpdateDependencyActiveState(BlueStackDependency dep, bool isActive, bool isIOS) { // Load the asset XML var xmlDoc = new XmlDocument(); xmlDoc.Load("Assets/Editor/BlueStackDependencies.xml"); string nodePath = isIOS ? "//dependencies/iosPods" : "//dependencies/androidPackages"; var parentNode = xmlDoc.SelectSingleNode(nodePath); if (parentNode == null) return; // Find the node by name XmlNode found = null; foreach (XmlNode node in parentNode.ChildNodes) { var nameAttr = isIOS ? node.Attributes?["name"] : node.Attributes?["spec"]; if (nameAttr != null && nameAttr.Value == dep.Name) { found = node; break; } } if (isActive && found == null) { // Add node var imported = xmlDoc.ImportNode(dep.PackageNode, true); parentNode.AppendChild(imported); } else if (!isActive && found != null) { // Remove node parentNode.RemoveChild(found); } // Save changes DependencyProvider.SaveDependenciesXMLFile(xmlDoc.OuterXml); // Update the in-memory state dep.Active = isActive; } } }