using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
namespace Unity.Tutorials.Core.Editor
{
///
/// Contains different utilities used in Tutorials custom editors
///
public static class TutorialEditorUtils
{
///
/// Same as ProjectWindowUtil.GetActiveFolderPath() but works also in 1-panel view.
///
/// Returns path with forward slashes
public static string GetActiveFolderPath()
{
return Selection.assetGUIDs
.Select(AssetDatabase.GUIDToAssetPath)
.Select(path =>
{
if (Directory.Exists(path))
return path;
if (File.Exists(path))
return Path.GetDirectoryName(path);
return path;
})
.FirstOrDefault()
.AsNullIfEmpty()
?? "Assets";
}
///
/// Find assets of type T in the project.
///
/// Type of assets to look for.
/// Assets of type T found in the project.
public static IEnumerable FindAssets() where T : UnityEngine.Object =>
AssetDatabase.FindAssets($"t:{typeof(T).FullName}")
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath);
///
/// Checks if a UnityEvent property is not in a specific execution state
///
/// A property representing the UnityEvent (or derived class)
///
/// True if the event is in the expected state
internal static bool EventIsNotInState(SerializedProperty eventProperty, UnityEngine.Events.UnityEventCallState state)
{
SerializedProperty persistentCalls = eventProperty.FindPropertyRelative("m_PersistentCalls.m_Calls");
for (int i = 0; i < persistentCalls.arraySize; i++)
{
if (persistentCalls.GetArrayElementAtIndex(i).FindPropertyRelative("m_CallState").intValue == (int)state)
continue;
return true;
}
return false;
}
///
/// Renders a warning box about the state of the event
///
internal static void RenderEventStateWarning()
{
EditorGUILayout.HelpBox(Localization.Tr(LocalizationKeys.k_TutorialPageLabelEventStateWarning), MessageType.Warning);
}
///
/// Opens an Url in the browser.
/// Links to Unity's websites will open only if the user is logged in.
///
/// The URL to open
public static void OpenUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
return;
}
string urlWithoutHttpPrefix = RemoveHttpProtocolPrefix(url);
if (IsUnityUrlRequiringAuthentication(urlWithoutHttpPrefix) && UnityConnectSession.loggedIn)
{
//unity websites can only be opened if they have the https prefix, we need to ensure that it exists otherwise URLs like "unity.com" won't be opened at all
UnityConnectSession.OpenAuthorizedURLInWebBrowser(EnsureProtocolPrefixIsPresent(url, "https"));
return;
}
/* Important: as its documentation says, this API is extremely powerful and could be exploited by malicius users trying to run protocols different than HTTP.
* Moreover, it ignores urls that dont' have a 3rd domain level. In order to address both issues, we need to ensure URLs have the https prefix
* otherwise URLs like "google.com" won't be opened at all */
Application.OpenURL(EnsureProtocolPrefixIsPresent(url, "http"));
}
internal static string EnsureProtocolPrefixIsPresent(string url, string protocol)
{
if (url.StartsWith(protocol, System.StringComparison.OrdinalIgnoreCase))
{
return url;
}
return $"{protocol}://{url}";
}
///
/// Removes "http://" and "https://" prefixes from an url
///
///
/// The url without the protocol prefix
internal static string RemoveHttpProtocolPrefix(string url)
{
if (url.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
{
return url.Split(new string[] { "//" }, System.StringSplitOptions.None)[1];
}
return url;
}
///
/// Is this a Unity URL that requires Authentication?
///
///
/// True if the url is a Unity URL that require an authentication
internal static bool IsUnityUrlRequiringAuthentication(string url)
{
// TODO Genesis will provide an API where we can keep a list of Unity URLs that we want to support.
url = RemoveHttpProtocolPrefix(url);
var splitUrl = url.Split('/')[0].ToLower();
return (url.StartsWith("unity.", System.StringComparison.OrdinalIgnoreCase) || splitUrl.Contains(".unity."))
&& !splitUrl.Contains("assetstore");
}
}
}