You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
22 KiB
590 lines
22 KiB
//========= Copyright 2016-2020, HTC Corporation. All rights reserved. ===========
|
|
#pragma warning disable 0649
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.Reflection;
|
|
|
|
#if UNITY_5_4_OR_NEWER
|
|
using UnityEngine.Networking;
|
|
#else
|
|
using UnityWebRequest = UnityEngine.WWW;
|
|
#endif
|
|
|
|
namespace HTC.UnityPlugin.Vive
|
|
{
|
|
[InitializeOnLoad]
|
|
public class VIUVersionCheck : EditorWindow
|
|
{
|
|
[Serializable]
|
|
private struct RepoInfo
|
|
{
|
|
public string tag_name;
|
|
public string body;
|
|
}
|
|
|
|
public interface IPropSetting
|
|
{
|
|
bool SkipCheck();
|
|
void UpdateCurrentValue();
|
|
bool IsIgnored();
|
|
bool IsUsingRecommendedValue();
|
|
bool DoDrawRecommend(); // return true if setting accepted
|
|
void AcceptRecommendValue();
|
|
void DoIgnore();
|
|
void DeleteIgnore();
|
|
}
|
|
|
|
public class RecommendedSetting<T> : IPropSetting
|
|
{
|
|
private const string fmtTitle = "{0} (current = {1})";
|
|
private const string fmtRecommendBtn = "Use recommended ({0})";
|
|
private const string fmtRecommendBtnWithPosefix = "Use recommended ({0}) - {1}";
|
|
|
|
private string m_settingTitle;
|
|
private string m_settingTrimedTitle;
|
|
private string ignoreKey { get { return m_settingTrimedTitle; } }
|
|
|
|
public string settingTitle { get { return m_settingTitle; } set { m_settingTitle = value; m_settingTrimedTitle = value.Replace(" ", ""); } }
|
|
public string recommendBtnPostfix = string.Empty;
|
|
public string toolTip = string.Empty;
|
|
public Func<bool> skipCheckFunc = null;
|
|
public Func<T> recommendedValueFunc = null;
|
|
public Func<T> currentValueFunc = null;
|
|
public Action<T> setValueFunc = null;
|
|
public T currentValue = default(T);
|
|
public T recommendedValue = default(T);
|
|
|
|
public T GetRecommended() { return recommendedValueFunc == null ? recommendedValue : recommendedValueFunc(); }
|
|
|
|
public bool SkipCheck() { return skipCheckFunc == null ? false : skipCheckFunc(); }
|
|
|
|
public bool IsIgnored() { return VIUProjectSettings.HasIgnoreKey(ignoreKey); }
|
|
|
|
public bool IsUsingRecommendedValue() { return EqualityComparer<T>.Default.Equals(currentValue, GetRecommended()); }
|
|
|
|
public void UpdateCurrentValue() { currentValue = currentValueFunc(); }
|
|
|
|
public bool DoDrawRecommend()
|
|
{
|
|
GUILayout.Label(new GUIContent(string.Format(fmtTitle, settingTitle, currentValue), toolTip));
|
|
|
|
GUILayout.BeginHorizontal();
|
|
|
|
bool recommendBtnClicked;
|
|
if (string.IsNullOrEmpty(recommendBtnPostfix))
|
|
{
|
|
recommendBtnClicked = GUILayout.Button(new GUIContent(string.Format(fmtRecommendBtn, GetRecommended()), toolTip));
|
|
}
|
|
else
|
|
{
|
|
recommendBtnClicked = GUILayout.Button(new GUIContent(string.Format(fmtRecommendBtnWithPosefix, GetRecommended(), recommendBtnPostfix), toolTip));
|
|
}
|
|
|
|
if (recommendBtnClicked)
|
|
{
|
|
AcceptRecommendValue();
|
|
}
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button(new GUIContent("Ignore", toolTip)))
|
|
{
|
|
DoIgnore();
|
|
}
|
|
|
|
GUILayout.EndHorizontal();
|
|
|
|
return recommendBtnClicked;
|
|
}
|
|
|
|
public void AcceptRecommendValue()
|
|
{
|
|
setValueFunc(GetRecommended());
|
|
}
|
|
|
|
public void DoIgnore()
|
|
{
|
|
VIUProjectSettings.AddIgnoreKey(ignoreKey);
|
|
}
|
|
|
|
public void DeleteIgnore()
|
|
{
|
|
VIUProjectSettings.RemoveIgnoreKey(ignoreKey);
|
|
}
|
|
}
|
|
|
|
public abstract class RecommendedSettingCollection : List<IPropSetting> { }
|
|
|
|
public const string lastestVersionUrl = "https://api.github.com/repos/ViveSoftware/ViveInputUtility-Unity/releases/latest";
|
|
public const string pluginUrl = "https://github.com/ViveSoftware/ViveInputUtility-Unity/releases";
|
|
public const double versionCheckIntervalMinutes = 30.0;
|
|
|
|
private const string nextVersionCheckTimeKey = "ViveInputUtility.LastVersionCheckTime";
|
|
private const string fmtIgnoreUpdateKey = "DoNotShowUpdate.v{0}";
|
|
private static string ignoreThisVersionKey;
|
|
|
|
private static bool completeCheckVersionFlow = false;
|
|
private static UnityWebRequest webReq;
|
|
private static RepoInfo latestRepoInfo;
|
|
private static System.Version latestVersion;
|
|
private static Vector2 releaseNoteScrollPosition;
|
|
private static Vector2 settingScrollPosition;
|
|
private static bool showNewVersion;
|
|
private static bool toggleSkipThisVersion = false;
|
|
private static VIUVersionCheck windowInstance;
|
|
private static List<IPropSetting> s_settings;
|
|
private static bool editorUpdateRegistered;
|
|
private Texture2D viuLogo;
|
|
|
|
/// <summary>
|
|
/// Count of settings that are ignored
|
|
/// </summary>
|
|
public static int ignoredSettingsCount { get; private set; }
|
|
/// <summary>
|
|
/// Count of settings that are not using recommended value
|
|
/// </summary>
|
|
public static int shouldNotifiedSettingsCount { get; private set; }
|
|
/// <summary>
|
|
/// Count of settings that are not ignored and not using recommended value
|
|
/// </summary>
|
|
public static int notifiedSettingsCount { get; private set; }
|
|
|
|
public static bool recommendedWindowOpened { get { return windowInstance != null; } }
|
|
|
|
static VIUVersionCheck()
|
|
{
|
|
editorUpdateRegistered = true;
|
|
EditorApplication.update += CheckVersionAndSettings;
|
|
|
|
#if UNITY_2017_2_OR_NEWER
|
|
EditorApplication.playModeStateChanged += (mode) =>
|
|
{
|
|
if (mode == PlayModeStateChange.EnteredEditMode && !editorUpdateRegistered)
|
|
{
|
|
#else
|
|
EditorApplication.playmodeStateChanged += () =>
|
|
{
|
|
if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode && !editorUpdateRegistered)
|
|
{
|
|
#endif
|
|
editorUpdateRegistered = true;
|
|
EditorApplication.update += CheckVersionAndSettings;
|
|
}
|
|
};
|
|
}
|
|
|
|
public static void AddRecommendedSetting<T>(RecommendedSetting<T> setting)
|
|
{
|
|
InitializeSettins();
|
|
s_settings.Add(setting);
|
|
}
|
|
|
|
private static void InitializeSettins()
|
|
{
|
|
if (s_settings != null) { return; }
|
|
|
|
s_settings = new List<IPropSetting>();
|
|
|
|
foreach (var type in Assembly.GetAssembly(typeof(RecommendedSettingCollection)).GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(RecommendedSettingCollection))))
|
|
{
|
|
s_settings.AddRange((RecommendedSettingCollection)Activator.CreateInstance(type));
|
|
}
|
|
}
|
|
|
|
private static void VersionCheckLog(string msg)
|
|
{
|
|
#if VIU_PRINT_FETCH_VERSION_LOG
|
|
using (var outputFile = new StreamWriter("VIUVersionCheck.log", true))
|
|
{
|
|
outputFile.WriteLine(DateTime.Now.ToString() + " - " + msg + ". Stop fetching until " + UtcDateTimeFromStr(EditorPrefs.GetString(nextVersionCheckTimeKey)).ToLocalTime().ToString());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// check vive input utility version on github
|
|
private static void CheckVersionAndSettings()
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
EditorApplication.update -= CheckVersionAndSettings;
|
|
editorUpdateRegistered = false;
|
|
return;
|
|
}
|
|
|
|
InitializeSettins();
|
|
|
|
// fetch new version info from github release site
|
|
if (!completeCheckVersionFlow && VIUSettings.autoCheckNewVIUVersion)
|
|
{
|
|
if (webReq == null) // web request not running
|
|
{
|
|
if (EditorPrefs.HasKey(nextVersionCheckTimeKey) && DateTime.UtcNow < UtcDateTimeFromStr(EditorPrefs.GetString(nextVersionCheckTimeKey)))
|
|
{
|
|
VersionCheckLog("Skipped");
|
|
completeCheckVersionFlow = true;
|
|
return;
|
|
}
|
|
|
|
webReq = GetUnityWebRequestAndSend(lastestVersionUrl);
|
|
}
|
|
|
|
if (!webReq.isDone)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// On Windows, PlaterSetting is stored at \HKEY_CURRENT_USER\Software\Unity Technologies\Unity Editor 5.x
|
|
EditorPrefs.SetString(nextVersionCheckTimeKey, UtcDateTimeToStr(DateTime.UtcNow.AddMinutes(versionCheckIntervalMinutes)));
|
|
|
|
if (UrlSuccess(webReq))
|
|
{
|
|
var json = GetWebText(webReq);
|
|
if (!string.IsNullOrEmpty(json))
|
|
{
|
|
latestRepoInfo = JsonUtility.FromJson<RepoInfo>(json);
|
|
VersionCheckLog("Fetched");
|
|
}
|
|
}
|
|
|
|
// parse latestVersion and ignoreThisVersionKey
|
|
if (!string.IsNullOrEmpty(latestRepoInfo.tag_name))
|
|
{
|
|
try
|
|
{
|
|
latestVersion = new System.Version(Regex.Replace(latestRepoInfo.tag_name, "[^0-9\\.]", string.Empty));
|
|
ignoreThisVersionKey = string.Format(fmtIgnoreUpdateKey, latestVersion.ToString());
|
|
}
|
|
catch
|
|
{
|
|
latestVersion = default(System.Version);
|
|
ignoreThisVersionKey = string.Empty;
|
|
}
|
|
}
|
|
|
|
webReq.Dispose();
|
|
webReq = null;
|
|
|
|
completeCheckVersionFlow = true;
|
|
}
|
|
|
|
VIUSettingsEditor.PackageManagerHelper.PreparePackageList();
|
|
if (VIUSettingsEditor.PackageManagerHelper.isPreparingList) { return; }
|
|
|
|
showNewVersion = !string.IsNullOrEmpty(ignoreThisVersionKey) && !VIUProjectSettings.HasIgnoreKey(ignoreThisVersionKey) && latestVersion > VIUVersion.current;
|
|
|
|
UpdateIgnoredNotifiedSettingsCount(false);
|
|
|
|
if (showNewVersion || notifiedSettingsCount > 0)
|
|
{
|
|
TryOpenRecommendedSettingWindow();
|
|
}
|
|
|
|
EditorApplication.update -= CheckVersionAndSettings;
|
|
editorUpdateRegistered = false;
|
|
}
|
|
|
|
public static bool UpdateIgnoredNotifiedSettingsCount(bool drawNotifiedPrompt)
|
|
{
|
|
InitializeSettins();
|
|
|
|
ignoredSettingsCount = 0;
|
|
shouldNotifiedSettingsCount = 0;
|
|
notifiedSettingsCount = 0;
|
|
var hasSettingsAccepted = false;
|
|
|
|
foreach (var setting in s_settings)
|
|
{
|
|
if (setting.SkipCheck()) { continue; }
|
|
|
|
setting.UpdateCurrentValue();
|
|
|
|
var isIgnored = setting.IsIgnored();
|
|
if (isIgnored) { ++ignoredSettingsCount; }
|
|
|
|
if (setting.IsUsingRecommendedValue()) { continue; }
|
|
else { ++shouldNotifiedSettingsCount; }
|
|
|
|
if (!isIgnored)
|
|
{
|
|
++notifiedSettingsCount;
|
|
|
|
if (drawNotifiedPrompt)
|
|
{
|
|
if (notifiedSettingsCount == 1)
|
|
{
|
|
EditorGUILayout.HelpBox("Recommended project settings:", MessageType.Warning);
|
|
|
|
settingScrollPosition = GUILayout.BeginScrollView(settingScrollPosition, GUILayout.ExpandHeight(true));
|
|
}
|
|
|
|
hasSettingsAccepted |= setting.DoDrawRecommend();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return hasSettingsAccepted;
|
|
}
|
|
|
|
// Open recommended setting window (with possible new version prompt)
|
|
// won't do any thing if the window is already opened
|
|
public static void TryOpenRecommendedSettingWindow()
|
|
{
|
|
if (recommendedWindowOpened) { return; }
|
|
|
|
windowInstance = GetWindow<VIUVersionCheck>(true, "Vive Input Utility");
|
|
windowInstance.minSize = new Vector2(240f, 550f);
|
|
var rect = windowInstance.position;
|
|
windowInstance.position = new Rect(Mathf.Max(rect.x, 50f), Mathf.Max(rect.y, 50f), rect.width, 200f + (showNewVersion ? 700f : 400f));
|
|
}
|
|
|
|
private static DateTime UtcDateTimeFromStr(string str)
|
|
{
|
|
var utcTicks = default(long);
|
|
if (string.IsNullOrEmpty(str) || !long.TryParse(str, out utcTicks)) { return DateTime.MinValue; }
|
|
return new DateTime(utcTicks, DateTimeKind.Utc);
|
|
}
|
|
|
|
private static string UtcDateTimeToStr(DateTime utcDateTime)
|
|
{
|
|
return utcDateTime.Ticks.ToString();
|
|
}
|
|
|
|
private static UnityWebRequest GetUnityWebRequestAndSend(string url)
|
|
{
|
|
var webReq = new UnityWebRequest(url);
|
|
#if UNITY_2017_2_OR_NEWER
|
|
webReq.SendWebRequest();
|
|
#elif UNITY_5_4_OR_NEWER
|
|
webReq.Send();
|
|
#endif
|
|
return webReq;
|
|
}
|
|
|
|
private static string GetWebText(UnityWebRequest wr)
|
|
{
|
|
#if UNITY_5_4_OR_NEWER
|
|
return wr != null && wr.downloadHandler != null ? wr.downloadHandler.text : string.Empty;
|
|
#else
|
|
return wr != null ? wr.text : string.Empty;
|
|
#endif
|
|
}
|
|
|
|
private static bool TryGetWebHeaderValue(UnityWebRequest wr, string headerKey, out string headerValue)
|
|
{
|
|
#if UNITY_5_4_OR_NEWER
|
|
headerValue = wr.GetResponseHeader(headerKey);
|
|
return string.IsNullOrEmpty(headerValue);
|
|
#else
|
|
if (wr.responseHeaders == null) { headerValue = string.Empty; return false; }
|
|
return wr.responseHeaders.TryGetValue(headerKey, out headerValue);
|
|
#endif
|
|
}
|
|
|
|
private static bool UrlSuccess(UnityWebRequest wr)
|
|
{
|
|
try
|
|
{
|
|
if (wr == null) { return false; }
|
|
|
|
if (!string.IsNullOrEmpty(wr.error))
|
|
{
|
|
// API rate limit exceeded, see https://developer.github.com/v3/#rate-limiting
|
|
Debug.Log("url:" + wr.url);
|
|
Debug.Log("error:" + wr.error);
|
|
Debug.Log(GetWebText(wr));
|
|
|
|
string responseHeader;
|
|
if (TryGetWebHeaderValue(wr, "X-RateLimit-Limit", out responseHeader))
|
|
{
|
|
Debug.Log("X-RateLimit-Limit:" + responseHeader);
|
|
}
|
|
if (TryGetWebHeaderValue(wr, "X-RateLimit-Remaining", out responseHeader))
|
|
{
|
|
Debug.Log("X-RateLimit-Remaining:" + responseHeader);
|
|
}
|
|
if (TryGetWebHeaderValue(wr, "X-RateLimit-Reset", out responseHeader))
|
|
{
|
|
Debug.Log("X-RateLimit-Reset:" + TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(double.Parse(responseHeader))).ToString());
|
|
}
|
|
VersionCheckLog("Failed. Rate limit exceeded");
|
|
return false;
|
|
}
|
|
|
|
if (Regex.IsMatch(GetWebText(wr), "404 not found", RegexOptions.IgnoreCase))
|
|
{
|
|
Debug.Log("url:" + wr.url);
|
|
Debug.Log("error:" + wr.error);
|
|
Debug.Log(GetWebText(wr));
|
|
VersionCheckLog("Failed. 404 not found");
|
|
return false;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogWarning(e);
|
|
VersionCheckLog("Failed. " + e.ToString());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private string GetResourcePath()
|
|
{
|
|
var ms = MonoScript.FromScriptableObject(this);
|
|
var path = AssetDatabase.GetAssetPath(ms);
|
|
path = Path.GetDirectoryName(path);
|
|
return path.Substring(0, path.Length - "Scripts/Editor".Length) + "Textures/";
|
|
}
|
|
|
|
public void OnGUI()
|
|
{
|
|
#if UNITY_2017_1_OR_NEWER
|
|
if (EditorApplication.isCompiling)
|
|
{
|
|
EditorGUILayout.LabelField("Compiling...");
|
|
return;
|
|
}
|
|
#endif
|
|
if (viuLogo == null)
|
|
{
|
|
var currentDir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this)));
|
|
var texturePath = currentDir.Substring(0, currentDir.Length - "Scripts/Editor".Length) + "Textures/VIU_logo.png";
|
|
viuLogo = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);
|
|
}
|
|
|
|
if (viuLogo != null)
|
|
{
|
|
GUI.DrawTexture(GUILayoutUtility.GetRect(position.width, 124, GUI.skin.box), viuLogo, ScaleMode.ScaleToFit);
|
|
}
|
|
|
|
if (showNewVersion)
|
|
{
|
|
EditorGUILayout.HelpBox("New version available:", MessageType.Warning);
|
|
|
|
GUILayout.Label("Current version: " + VIUVersion.current);
|
|
GUILayout.Label("New version: " + latestVersion);
|
|
|
|
if (!string.IsNullOrEmpty(latestRepoInfo.body))
|
|
{
|
|
GUILayout.Label("Release notes:");
|
|
releaseNoteScrollPosition = GUILayout.BeginScrollView(releaseNoteScrollPosition, GUILayout.Height(250f));
|
|
EditorGUILayout.HelpBox(latestRepoInfo.body, MessageType.None);
|
|
GUILayout.EndScrollView();
|
|
}
|
|
|
|
GUILayout.BeginHorizontal();
|
|
{
|
|
if (GUILayout.Button(new GUIContent("Get Latest Version", "Goto " + pluginUrl)))
|
|
{
|
|
Application.OpenURL(pluginUrl);
|
|
}
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
toggleSkipThisVersion = GUILayout.Toggle(toggleSkipThisVersion, "Do not prompt for this version again.");
|
|
}
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
|
|
var hasSettingsAccepted = UpdateIgnoredNotifiedSettingsCount(true);
|
|
|
|
if (notifiedSettingsCount > 0)
|
|
{
|
|
GUILayout.EndScrollView();
|
|
|
|
if (ignoredSettingsCount > 0)
|
|
{
|
|
if (GUILayout.Button("Clear All Ignores(" + ignoredSettingsCount + ")"))
|
|
{
|
|
foreach (var setting in s_settings) { setting.DeleteIgnore(); }
|
|
}
|
|
}
|
|
|
|
GUILayout.BeginHorizontal();
|
|
{
|
|
if (GUILayout.Button("Accept All(" + notifiedSettingsCount + ")"))
|
|
{
|
|
for (int i = 10; i >= 0 && notifiedSettingsCount > 0; --i)
|
|
{
|
|
foreach (var setting in s_settings) { if (!setting.SkipCheck() && !setting.IsIgnored() && !setting.IsUsingRecommendedValue()) { setting.AcceptRecommendValue(); } }
|
|
|
|
VIUSettingsEditor.ApplySDKChanges();
|
|
|
|
UpdateIgnoredNotifiedSettingsCount(false);
|
|
}
|
|
|
|
hasSettingsAccepted = true;
|
|
}
|
|
|
|
if (GUILayout.Button("Ignore All(" + notifiedSettingsCount + ")"))
|
|
{
|
|
foreach (var setting in s_settings) { if (!setting.SkipCheck() && !setting.IsIgnored() && !setting.IsUsingRecommendedValue()) { setting.DoIgnore(); } }
|
|
}
|
|
}
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
else if (shouldNotifiedSettingsCount > 0)
|
|
{
|
|
EditorGUILayout.HelpBox("Some recommended settings ignored.", MessageType.Warning);
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button("Clear All Ignores(" + ignoredSettingsCount + ")"))
|
|
{
|
|
foreach (var setting in s_settings) { setting.DeleteIgnore(); }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.HelpBox("All recommended settings applied.", MessageType.Info);
|
|
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
VIUSettingsEditor.ApplySDKChanges();
|
|
|
|
if (VIUProjectSettings.hasChanged)
|
|
{
|
|
// save ignore keys
|
|
VIUProjectSettings.Save();
|
|
}
|
|
|
|
if (GUILayout.Button("Close"))
|
|
{
|
|
Close();
|
|
}
|
|
|
|
if (hasSettingsAccepted)
|
|
{
|
|
VRModuleManagement.VRModuleManagerEditor.UpdateScriptingDefineSymbols();
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (viuLogo != null)
|
|
{
|
|
viuLogo = null;
|
|
}
|
|
|
|
if (showNewVersion && toggleSkipThisVersion && !string.IsNullOrEmpty(ignoreThisVersionKey))
|
|
{
|
|
showNewVersion = false;
|
|
VIUProjectSettings.AddIgnoreKey(ignoreThisVersionKey);
|
|
VIUProjectSettings.Save();
|
|
}
|
|
|
|
if (windowInstance == this)
|
|
{
|
|
windowInstance = null;
|
|
}
|
|
}
|
|
}
|
|
}
|