Assignment for RMIT Mixed Reality in 2020
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.
 
 
 

337 lines
12 KiB

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;
}
}
}