using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEditor;
|
|
using UnityEditorInternal;
|
|
using UnityEngine;
|
|
using VRTK;
|
|
|
|
[InitializeOnLoad]
|
|
public sealed class VRTK_UpdatePrompt : EditorWindow
|
|
{
|
|
[Serializable]
|
|
private sealed class LatestRelease
|
|
{
|
|
[NonSerialized]
|
|
public Version version;
|
|
[NonSerialized]
|
|
public DateTime publishedDateTime;
|
|
[NonSerialized]
|
|
public List<string> changelogPages;
|
|
|
|
#pragma warning disable 649
|
|
public string html_url;
|
|
public string tag_name;
|
|
public string name;
|
|
public string published_at;
|
|
public string zipball_url;
|
|
public string body;
|
|
#pragma warning restore 649
|
|
|
|
public static LatestRelease CreateFromJSON(string json)
|
|
{
|
|
LatestRelease latestRelease = JsonUtility.FromJson<LatestRelease>(json);
|
|
latestRelease.version = new Version(latestRelease.tag_name);
|
|
latestRelease.publishedDateTime = DateTime.Parse(latestRelease.published_at);
|
|
|
|
string changelog = latestRelease.body;
|
|
latestRelease.body = null;
|
|
|
|
changelog = Regex.Replace(changelog, @"(?<!(\r\n){2}) \*.*\*{2}(.*)\*{2}", "\n<size=13>$2</size>");
|
|
changelog = Regex.Replace(changelog, @"(\r\n){2} \*.*\*{2}(.*)\*{2}", "\n\n<size=13>$2</size>");
|
|
changelog = new Regex(@"(#+)\s?(.*)\b").Replace(
|
|
changelog,
|
|
match => string.Format(
|
|
"<size={0}>{1}</size>",
|
|
30 - match.Groups[1].Value.Length * 6,
|
|
match.Groups[2].Value
|
|
)
|
|
);
|
|
changelog = changelog.Replace(" *", "*");
|
|
|
|
// Each char gets turned into two triangles and the mesh vertices limit is 2^16.
|
|
// Let's use another 100 to be on the safe side.
|
|
const int textLengthLimit = 65536 / 4 - 100;
|
|
latestRelease.changelogPages = new List<string>((int)Mathf.Ceil(changelog.Length / (float)textLengthLimit));
|
|
|
|
while (true)
|
|
{
|
|
if (changelog.Length > textLengthLimit)
|
|
{
|
|
int startIndex = Math.Min(changelog.Length, textLengthLimit);
|
|
int lastIndexOf = changelog.LastIndexOf("\n", startIndex, StringComparison.Ordinal);
|
|
|
|
if (lastIndexOf == -1)
|
|
{
|
|
lastIndexOf = startIndex;
|
|
}
|
|
|
|
latestRelease.changelogPages.Add(changelog.Substring(0, lastIndexOf));
|
|
changelog = changelog.Substring(lastIndexOf).TrimStart('\n', '\r');
|
|
}
|
|
else
|
|
{
|
|
latestRelease.changelogPages.Add(changelog);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return latestRelease;
|
|
}
|
|
}
|
|
|
|
private const string remoteURL = "https://api.github.com/repos/thestonefox/vrtk/releases/latest";
|
|
private const string assetStoreURL = "/content/64131";
|
|
private const string hidePromptKeyFormat = "VRTK.HideVersionPrompt.v{0}";
|
|
private const string lastCheckKey = "VRTK.VersionPromptUpdate";
|
|
private const int checkUpdateHours = 6;
|
|
|
|
private static bool isManualCheck;
|
|
private static bool versionChecked;
|
|
private static WWW versionResource;
|
|
private static LatestRelease latestRelease;
|
|
private static VRTK_UpdatePrompt promptWindow;
|
|
|
|
private static Vector2 scrollPosition;
|
|
private static Vector2 changelogScrollPosition;
|
|
private static float changelogWidth;
|
|
private static int changelogPageIndex;
|
|
private static bool isChangelogFoldOut = true;
|
|
|
|
static VRTK_UpdatePrompt()
|
|
{
|
|
EditorApplication.update += CheckForUpdate;
|
|
}
|
|
|
|
public void OnGUI()
|
|
{
|
|
using (GUILayout.ScrollViewScope scrollViewScope = new GUILayout.ScrollViewScope(scrollPosition))
|
|
{
|
|
scrollPosition = scrollViewScope.scrollPosition;
|
|
|
|
if (versionResource != null && !versionResource.isDone)
|
|
{
|
|
EditorGUILayout.HelpBox("Checking for updates...", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (latestRelease == null)
|
|
{
|
|
EditorGUILayout.HelpBox("There was a problem checking for updates.", MessageType.Error);
|
|
DrawCheckAgainButton();
|
|
|
|
return;
|
|
}
|
|
|
|
string newVersionName = latestRelease.name == string.Format("Version {0}", latestRelease.version)
|
|
? string.Empty
|
|
: string.Format(" \"{0}\"", latestRelease.name);
|
|
bool isUpToDate = VRTK_Defines.CurrentVersion >= latestRelease.version;
|
|
|
|
EditorGUILayout.HelpBox(
|
|
string.Format(
|
|
"{0}.\n\nInstalled Version: {1}\nAvailable version: {2}{3} (published on {4})",
|
|
isUpToDate ? "Already up to date" : "A new version of VRTK is available",
|
|
VRTK_Defines.CurrentVersion,
|
|
latestRelease.version,
|
|
newVersionName,
|
|
latestRelease.publishedDateTime.ToLocalTime()),
|
|
isUpToDate ? MessageType.Info : MessageType.Warning);
|
|
|
|
DrawCheckAgainButton();
|
|
|
|
isChangelogFoldOut = EditorGUILayout.Foldout(isChangelogFoldOut, "Changelog", true);
|
|
if (isChangelogFoldOut)
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.Space(10);
|
|
|
|
using (new EditorGUILayout.VerticalScope())
|
|
{
|
|
VRTK_EditorUtilities.DrawScrollableSelectableLabel(
|
|
ref changelogScrollPosition,
|
|
ref changelogWidth,
|
|
latestRelease.changelogPages[changelogPageIndex],
|
|
new GUIStyle(EditorStyles.textArea)
|
|
{
|
|
richText = true
|
|
});
|
|
|
|
if (latestRelease.changelogPages.Count > 0)
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
using (new EditorGUI.DisabledGroupScope(changelogPageIndex == 0))
|
|
{
|
|
if (GUILayout.Button("Previous Page"))
|
|
{
|
|
changelogPageIndex = Math.Max(0, --changelogPageIndex);
|
|
changelogScrollPosition = Vector3.zero;
|
|
}
|
|
}
|
|
using (new EditorGUI.DisabledGroupScope(changelogPageIndex == latestRelease.changelogPages.Count - 1))
|
|
{
|
|
if (GUILayout.Button("Next Page"))
|
|
{
|
|
changelogPageIndex = Math.Min(latestRelease.changelogPages.Count - 1, ++changelogPageIndex);
|
|
changelogScrollPosition = Vector3.zero;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("View on GitHub"))
|
|
{
|
|
Application.OpenURL(latestRelease.html_url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isUpToDate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
VRTK_EditorUtilities.AddHeader("Get Latest Version");
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
if (GUILayout.Button("From Asset Store"))
|
|
{
|
|
AssetStore.Open(assetStoreURL);
|
|
Close();
|
|
}
|
|
if (GUILayout.Button("From GitHub"))
|
|
{
|
|
Application.OpenURL(latestRelease.zipball_url);
|
|
}
|
|
}
|
|
|
|
using (EditorGUI.ChangeCheckScope changeCheckScope = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
string key = string.Format(hidePromptKeyFormat, latestRelease.version);
|
|
bool hideToggle = EditorPrefs.HasKey(key);
|
|
|
|
hideToggle = GUILayout.Toggle(hideToggle, "Do not prompt for this version again.");
|
|
|
|
if (changeCheckScope.changed)
|
|
{
|
|
if (hideToggle)
|
|
{
|
|
EditorPrefs.SetBool(key, true);
|
|
}
|
|
else
|
|
{
|
|
EditorPrefs.DeleteKey(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void DrawCheckAgainButton()
|
|
{
|
|
if (GUILayout.Button("Check again"))
|
|
{
|
|
CheckManually();
|
|
}
|
|
}
|
|
|
|
private static void CheckForUpdate()
|
|
{
|
|
changelogScrollPosition = Vector3.zero;
|
|
changelogWidth = 0;
|
|
changelogPageIndex = 0;
|
|
|
|
if (isManualCheck)
|
|
{
|
|
ShowWindow();
|
|
}
|
|
else
|
|
{
|
|
if (versionChecked)
|
|
{
|
|
EditorApplication.update -= CheckForUpdate;
|
|
return;
|
|
}
|
|
|
|
if (EditorPrefs.HasKey(lastCheckKey))
|
|
{
|
|
string lastCheckTicksString = EditorPrefs.GetString(lastCheckKey);
|
|
DateTime lastCheckDateTime = new DateTime(Convert.ToInt64(lastCheckTicksString));
|
|
|
|
if (lastCheckDateTime.AddHours(checkUpdateHours) >= DateTime.UtcNow)
|
|
{
|
|
versionChecked = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
versionResource = versionResource == null ? new WWW(remoteURL) : versionResource;
|
|
if (!versionResource.isDone)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EditorApplication.update -= CheckForUpdate;
|
|
|
|
if (string.IsNullOrEmpty(versionResource.error))
|
|
{
|
|
latestRelease = LatestRelease.CreateFromJSON(versionResource.text);
|
|
}
|
|
|
|
versionResource.Dispose();
|
|
versionResource = null;
|
|
versionChecked = true;
|
|
EditorPrefs.SetString(lastCheckKey, DateTime.UtcNow.Ticks.ToString());
|
|
|
|
// Clean up the existing hidePromptKeys (except the one for the current version)
|
|
new[] { VRTK_Defines.CurrentVersion }
|
|
.Concat(VRTK_Defines.PreviousVersions)
|
|
.Where(version => latestRelease == null || version != latestRelease.version)
|
|
.Select(version => string.Format(hidePromptKeyFormat, version))
|
|
.Where(EditorPrefs.HasKey)
|
|
.ToList()
|
|
.ForEach(EditorPrefs.DeleteKey);
|
|
|
|
if (!isManualCheck
|
|
&& latestRelease != null
|
|
&& (VRTK_Defines.CurrentVersion >= latestRelease.version
|
|
|| EditorPrefs.HasKey(string.Format(hidePromptKeyFormat, latestRelease.version))))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ShowWindow();
|
|
isManualCheck = false;
|
|
}
|
|
|
|
private static void ShowWindow()
|
|
{
|
|
if (promptWindow != null)
|
|
{
|
|
promptWindow.ShowUtility();
|
|
promptWindow.Repaint();
|
|
return;
|
|
}
|
|
|
|
promptWindow = GetWindow<VRTK_UpdatePrompt>(true);
|
|
promptWindow.titleContent = new GUIContent("VRTK Update");
|
|
}
|
|
|
|
[MenuItem("Window/VRTK/Check for Updates")]
|
|
private static void CheckManually()
|
|
{
|
|
isManualCheck = true;
|
|
|
|
if (versionResource == null)
|
|
{
|
|
EditorApplication.update += CheckForUpdate;
|
|
}
|
|
}
|
|
}
|