using System; using System.Linq; using System.Collections.Generic; using System.IO; using System.Reflection; using UnityEditor; using UnityEngine; namespace AppCenterEditor { public class AppCenterEditorSDKTools : Editor { public enum SDKState { SDKNotInstalled, SDKNotInstalledAndInstalling, SDKNotFull, SDKNotFullAndInstalling, SDKIsFull } public static bool IsInstalled { get { return AreSomePackagesInstalled(); } } public static bool IsFullSDK { get { return CheckIfAllPackagesInstalled(); } } public static bool IsInstalling { get; set; } public static bool IsUpgrading { get; set; } public static string LatestSdkVersion { get; private set; } public static UnityEngine.Object SdkFolder { get; private set; } public static string InstalledSdkVersion { get; private set; } public static GUIStyle TitleStyle { get { return new GUIStyle(AppCenterEditorHelper.uiStyle.GetStyle("titleLabel")); } } private static Type appCenterSettingsType = null; private static bool isInitialized; // used to check once, gets reset after each compile private static UnityEngine.Object _previousSdkFolderPath; private static bool sdkFolderNotFound; private static int angle = 0; public static SDKState GetSDKState() { if (!IsInstalled) { if (IsInstalling) { return SDKState.SDKNotInstalledAndInstalling; } else { return SDKState.SDKNotInstalled; } } //SDK installed. if (IsFullSDK) { return SDKState.SDKIsFull; } //SDK is not full. if (IsInstalling) { return SDKState.SDKNotFullAndInstalling; } else { return SDKState.SDKNotFull; } } public static void DrawSdkPanel() { if (!isInitialized) { //SDK is installed. CheckSdkVersion(); isInitialized = true; GetLatestSdkVersion(); SdkFolder = FindSdkAsset(); if (SdkFolder != null) { AppCenterEditorPrefsSO.Instance.SdkPath = AssetDatabase.GetAssetPath(SdkFolder); // AppCenterEditorDataService.SaveEnvDetails(); } } ShowSdkInstallationPanel(); } public static void DisplayPackagePanel(AppCenterSDKPackage sdkPackage) { using (new AppCenterGuiFieldHelper.UnityVertical(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleGray1"))) { using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); if (sdkPackage.IsInstalled) { sdkPackage.ShowPackageInstalledMenu(); } else { sdkPackage.ShowPackageNotInstalledMenu(); } GUILayout.FlexibleSpace(); } } } private static void ShowSdkInstallationPanel() { sdkFolderNotFound = SdkFolder == null; if (_previousSdkFolderPath != SdkFolder) { // something changed, better save the result. _previousSdkFolderPath = SdkFolder; AppCenterEditorPrefsSO.Instance.SdkPath = (AssetDatabase.GetAssetPath(SdkFolder)); //TODO: check if we need this? // AppCenterEditorDataService.SaveEnvDetails(); sdkFolderNotFound = false; } SDKState SDKstate = GetSDKState(); using (new AppCenterGuiFieldHelper.UnityVertical(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleGray1"))) { switch (SDKstate) { case SDKState.SDKNotInstalled: ShowNOSDKLabel(); ShowInstallButton(); break; case SDKState.SDKNotInstalledAndInstalling: ShowNOSDKLabel(); ShowInstallingButton(); break; case SDKState.SDKNotFull: ShowSdkInstalledLabel(); ShowFolderObject(); ShowInstallButton(); ShowRemoveButton(); break; case SDKState.SDKNotFullAndInstalling: ShowSdkInstalledLabel(); ShowFolderObject(); ShowInstallingButton(); ShowRemoveButton(); break; case SDKState.SDKIsFull: ShowSdkInstalledLabel(); ShowFolderObject(); ShowRemoveButton(); break; } } } public static void ShowUpgradePanel() { if (!sdkFolderNotFound) { using (new AppCenterGuiFieldHelper.UnityVertical(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleGray1"))) { string[] versionNumber = !string.IsNullOrEmpty(InstalledSdkVersion) ? InstalledSdkVersion.Split('.') : new string[0]; var numerical = 0; bool isEmptyVersion = string.IsNullOrEmpty(InstalledSdkVersion) || versionNumber == null || versionNumber.Length == 0; if (isEmptyVersion || (versionNumber.Length > 0 && int.TryParse(versionNumber[0], out numerical) && numerical < 0)) { //older version of the SDK using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); EditorGUILayout.LabelField("SDK is outdated. Consider upgrading to the get most features.", AppCenterEditorHelper.uiStyle.GetStyle("orTxt")); GUILayout.FlexibleSpace(); } } var buttonWidth = 200; GUILayout.Space(5); if (ShowSDKUpgrade()) { if (IsUpgrading) { using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); var image = DrawUtils.RotateImage(AssetDatabase.LoadAssetAtPath("Assets/AppCenterEditorExtensions/Editor/UI/Images/wheel.png", typeof(Texture2D)) as Texture2D, angle++); GUILayout.Button(new GUIContent(" Upgrading to " + LatestSdkVersion, image), AppCenterEditorHelper.uiStyle.GetStyle("Button"), GUILayout.MaxWidth(buttonWidth), GUILayout.MinHeight(32)); GUILayout.FlexibleSpace(); } } else { using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Upgrade to " + LatestSdkVersion, AppCenterEditorHelper.uiStyle.GetStyle("Button"), GUILayout.MinHeight(32))) { IsUpgrading = true; UpgradeSdk(); } GUILayout.FlexibleSpace(); } } } else { using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); EditorGUILayout.LabelField("You have the latest SDK!", TitleStyle, GUILayout.MinHeight(32)); GUILayout.FlexibleSpace(); } } GUILayout.Space(5); using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); if (GUILayout.Button("VIEW RELEASE NOTES", AppCenterEditorHelper.uiStyle.GetStyle("textButton"), GUILayout.MinHeight(32), GUILayout.MinWidth(200))) { Application.OpenURL("https://github.com/Microsoft/AppCenter-SDK-Unity/releases"); } GUILayout.FlexibleSpace(); } } } } private static void ShowRemoveButton() { if (!sdkFolderNotFound) { using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClear"))) { GUILayout.FlexibleSpace(); if (GUILayout.Button("REMOVE SDK", AppCenterEditorHelper.uiStyle.GetStyle("textButton"), GUILayout.MinHeight(32), GUILayout.MinWidth(200))) { RemoveSdk(); } GUILayout.FlexibleSpace(); } } } private static void ShowFolderObject() { if (sdkFolderNotFound) { EditorGUILayout.LabelField("An SDK was detected, but we were unable to find the directory. Drag-and-drop the top-level App Center SDK folder below.", AppCenterEditorHelper.uiStyle.GetStyle("orTxt")); } else { // This hack is needed to disable folder object and remove the blue border around it. // Other UI is getting enabled later in the method. GUI.enabled = false; } GUILayout.Space(5); using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleClearWithleftPad"))) { GUILayout.FlexibleSpace(); SdkFolder = EditorGUILayout.ObjectField(SdkFolder, typeof(UnityEngine.Object), false, GUILayout.MaxWidth(200)); GUILayout.FlexibleSpace(); } GUILayout.Space(5); GUI.enabled = AppCenterEditor.IsGUIEnabled(); } private static void ShowSdkInstalledLabel() { GUILayout.Space(5); EditorGUILayout.LabelField(string.Format("SDK {0} is installed", string.IsNullOrEmpty(InstalledSdkVersion) ? Constants.UnknownVersion : InstalledSdkVersion), TitleStyle, GUILayout.ExpandWidth(true)); GUILayout.Space(5); } private static void ShowInstallingButton() { var buttonWidth = 250; GUILayout.Space(5); using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleEmpty"))) { GUILayout.FlexibleSpace(); var image = DrawUtils.RotateImage(AssetDatabase.LoadAssetAtPath("Assets/AppCenterEditorExtensions/Editor/UI/Images/wheel.png", typeof(Texture2D)) as Texture2D, angle++); GUILayout.Button(new GUIContent(" SDK is installing", image), AppCenterEditorHelper.uiStyle.GetStyle("Button"), GUILayout.MaxWidth(buttonWidth), GUILayout.MinHeight(32)); GUILayout.FlexibleSpace(); } GUILayout.Space(5); } private static void ShowInstallButton() { var buttonWidth = 250; GUILayout.Space(5); using (new AppCenterGuiFieldHelper.UnityHorizontal(AppCenterEditorHelper.uiStyle.GetStyle("gpStyleEmpty"))) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Install all App Center SDK packages", AppCenterEditorHelper.uiStyle.GetStyle("Button"), GUILayout.MaxWidth(buttonWidth), GUILayout.MinHeight(32))) { IsInstalling = true; try { PackagesInstaller.ImportLatestSDK(GetNotInstalledPackages(), LatestSdkVersion); } catch (Exception exception) { EdExLogger.LoggerInstance.LogError("Failed to install SDK packages: " + exception); IsInstalling = false; } } GUILayout.FlexibleSpace(); } GUILayout.Space(5); } private static void ShowNOSDKLabel() { EditorGUILayout.LabelField("No SDK is installed.", TitleStyle, GUILayout.ExpandWidth(true)); GUILayout.Space(10); } public static bool AreSomePackagesInstalled() { return GetAppCenterSettings() != null; } public static List GetNotInstalledPackages() { List notInstalledPackages = new List(); if (!IsInstalled) { notInstalledPackages.AddRange(AppCenterSDKPackage.SupportedPackages); return notInstalledPackages; } foreach (var package in AppCenterSDKPackage.SupportedPackages) { if (!package.IsInstalled) { notInstalledPackages.Add(package); } } return notInstalledPackages; } public static bool CheckIfAllPackagesInstalled() { foreach (var package in AppCenterSDKPackage.SupportedPackages) { if (!package.IsInstalled) { return false; } } return GetAppCenterSettings() != null; } public static Type GetAppCenterSettings() { if (appCenterSettingsType == typeof(object)) return null; // Sentinel value to indicate that AppCenterSettings doesn't exist if (appCenterSettingsType != null) return appCenterSettingsType; appCenterSettingsType = typeof(object); // Sentinel value to indicate that AppCenterSettings doesn't exist var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in allAssemblies) foreach (var eachType in assembly.GetTypes()) if (eachType.Name == AppCenterEditorHelper.APPCENTER_SETTINGS_TYPENAME) appCenterSettingsType = eachType; //if (appCenterSettingsType == typeof(object)) // Debug.LogWarning("Should not have gotten here: " + allAssemblies.Length); //else // Debug.Log("Found Settings: " + allAssemblies.Length + ", " + appCenterSettingsType.Assembly.FullName); return appCenterSettingsType == typeof(object) ? null : appCenterSettingsType; } private static bool ShowSDKUpgrade() { if (string.IsNullOrEmpty(LatestSdkVersion) || LatestSdkVersion == Constants.UnknownVersion) { return false; } if (string.IsNullOrEmpty(InstalledSdkVersion) || InstalledSdkVersion == Constants.UnknownVersion) { return true; } bool isOutdated = false; foreach (var package in AppCenterSDKPackage.SupportedPackages) { if (package.IsInstalled) { string packageVersion = package.InstalledVersion; bool isPackageOutdated = false; if (string.IsNullOrEmpty(packageVersion) || packageVersion == Constants.UnknownVersion) { isPackageOutdated = true; } else { string[] current = packageVersion.Split('.'); string[] latest = LatestSdkVersion.Split('.'); isPackageOutdated = int.Parse(latest[0]) > int.Parse(current[0]) || int.Parse(latest[1]) > int.Parse(current[1]) || int.Parse(latest[2]) > int.Parse(current[2]); } if (isPackageOutdated) { isOutdated = true; } } } return isOutdated; } private static void UpgradeSdk() { if (EditorUtility.DisplayDialog("Confirm SDK Upgrade", "This action will remove the current App Center SDK and install the lastet version.", "Confirm", "Cancel")) { try { var installedPackages = AppCenterSDKPackage.GetInstalledPackages(); RemoveSdkBeforeUpdate(); PackagesInstaller.ImportLatestSDK(installedPackages, LatestSdkVersion, AppCenterEditorPrefsSO.Instance.SdkPath); } catch (Exception exception) { EdExLogger.LoggerInstance.LogError("Failed to upgrade SDK: " + exception); IsUpgrading = false; } } } private static void RemoveSdkBeforeUpdate() { var skippedFiles = new[] { "AppCenterSettings.asset", "AppCenterSettings.asset.meta", "AppCenterSettingsAdvanced.asset", "AppCenterSettingsAdvanced.asset.meta" }; RemoveAndroidSettings(); var toDelete = new List(); toDelete.AddRange(Directory.GetFiles(AppCenterEditorPrefsSO.Instance.SdkPath)); toDelete.AddRange(Directory.GetDirectories(AppCenterEditorPrefsSO.Instance.SdkPath)); foreach (var path in toDelete) { if (!skippedFiles.Contains(Path.GetFileName(path))) { FileUtil.DeleteFileOrDirectory(path); } } } public static void RemoveSdk(bool prompt = true) { if (prompt && !EditorUtility.DisplayDialog("Confirm SDK Removal", "This action will remove the current App Center SDK.", "Confirm", "Cancel")) { return; } EdExLogger.LoggerInstance.LogWithTimeStamp("Removing SDK..."); RemoveAndroidSettings(); if (FileUtil.DeleteFileOrDirectory(AppCenterEditorPrefsSO.Instance.SdkPath)) { FileUtil.DeleteFileOrDirectory(AppCenterEditorPrefsSO.Instance.SdkPath + ".meta"); AppCenterEditor.RaiseStateUpdate(AppCenterEditor.EdExStates.OnSuccess, "App Center SDK removed."); EdExLogger.LoggerInstance.LogWithTimeStamp("App Center SDK removed."); // HACK for 5.4, AssetDatabase.Refresh(); seems to cause the install to fail. if (prompt) { AssetDatabase.Refresh(); } } else { AppCenterEditor.RaiseStateUpdate(AppCenterEditor.EdExStates.OnError, "An unknown error occured and the App Center SDK could not be removed."); } } private static void RemoveAndroidSettings() { if (Directory.Exists(Application.dataPath + "/Plugins/Android/res/values")) { var files = Directory.GetFiles(Application.dataPath + "/Plugins/Android/res/values", "appcenter-settings.xml*", SearchOption.AllDirectories); foreach (var file in files) { FileUtil.DeleteFileOrDirectory(file); } } } private static void CheckSdkVersion() { if (!string.IsNullOrEmpty(InstalledSdkVersion)) return; var packageTypes = new Dictionary(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { foreach (var type in assembly.GetTypes()) { if (type.FullName == Constants.WrapperSdkClassName) { foreach (var field in type.GetFields()) { if (field.Name == Constants.WrapperSdkVersionFieldName) { InstalledSdkVersion = field.GetValue(field).ToString(); break; } } } else { foreach (var package in AppCenterSDKPackage.SupportedPackages) { if (type.FullName == package.TypeName) { package.IsInstalled = true; packageTypes[package] = type; } } } } } catch (ReflectionTypeLoadException) { // For this failure, silently skip this assembly unless we have some expectation that it contains App Center if (assembly.FullName.StartsWith("Assembly-CSharp")) // The standard "source-code in unity proj" assembly name { EdExLogger.LoggerInstance.LogWarning("App Center Editor Extension error, failed to access the main CSharp assembly that probably contains App Center SDK"); } continue; } } foreach (var packageType in packageTypes) { packageType.Key.GetInstalledVersion(packageType.Value, InstalledSdkVersion); } } private static void GetLatestSdkVersion() { var threshold = AppCenterEditorPrefsSO.Instance.EdSet_lastSdkVersionCheck != DateTime.MinValue ? AppCenterEditorPrefsSO.Instance.EdSet_lastSdkVersionCheck.AddHours(1) : DateTime.MinValue; if (DateTime.Today > threshold) { AppCenterEditorHttp.MakeGitHubApiCall("https://api.github.com/repos/Microsoft/AppCenter-SDK-Unity/git/refs/tags", (version) => { LatestSdkVersion = version ?? Constants.UnknownVersion; AppCenterEditorPrefsSO.Instance.EdSet_latestSdkVersion = LatestSdkVersion; }); } else { LatestSdkVersion = AppCenterEditorPrefsSO.Instance.EdSet_latestSdkVersion; } } private static UnityEngine.Object FindSdkAsset() { UnityEngine.Object sdkAsset = null; // look in editor prefs if (AppCenterEditorPrefsSO.Instance.SdkPath != null) { sdkAsset = AssetDatabase.LoadAssetAtPath(AppCenterEditorPrefsSO.Instance.SdkPath, typeof(UnityEngine.Object)); } if (sdkAsset != null) return sdkAsset; sdkAsset = AssetDatabase.LoadAssetAtPath(AppCenterEditorHelper.DEFAULT_SDK_LOCATION, typeof(UnityEngine.Object)); if (sdkAsset != null) return sdkAsset; var fileList = Directory.GetDirectories(Application.dataPath, "*AppCenter", SearchOption.AllDirectories); if (fileList.Length == 0) return null; var relPath = fileList[0].Substring(fileList[0].LastIndexOf("Assets" + Path.DirectorySeparatorChar)); return AssetDatabase.LoadAssetAtPath(relPath, typeof(UnityEngine.Object)); } } }