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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text.RegularExpressions;
  5. using UnityEditor;
  6. using UnityEditorInternal;
  7. using UnityEngine;
  8. using VRTK;
  9. [InitializeOnLoad]
  10. public sealed class VRTK_UpdatePrompt : EditorWindow
  11. {
  12. [Serializable]
  13. private sealed class LatestRelease
  14. {
  15. [NonSerialized]
  16. public Version version;
  17. [NonSerialized]
  18. public DateTime publishedDateTime;
  19. [NonSerialized]
  20. public List<string> changelogPages;
  21. #pragma warning disable 649
  22. public string html_url;
  23. public string tag_name;
  24. public string name;
  25. public string published_at;
  26. public string zipball_url;
  27. public string body;
  28. #pragma warning restore 649
  29. public static LatestRelease CreateFromJSON(string json)
  30. {
  31. LatestRelease latestRelease = JsonUtility.FromJson<LatestRelease>(json);
  32. latestRelease.version = new Version(latestRelease.tag_name);
  33. latestRelease.publishedDateTime = DateTime.Parse(latestRelease.published_at);
  34. string changelog = latestRelease.body;
  35. latestRelease.body = null;
  36. changelog = Regex.Replace(changelog, @"(?<!(\r\n){2}) \*.*\*{2}(.*)\*{2}", "\n<size=13>$2</size>");
  37. changelog = Regex.Replace(changelog, @"(\r\n){2} \*.*\*{2}(.*)\*{2}", "\n\n<size=13>$2</size>");
  38. changelog = new Regex(@"(#+)\s?(.*)\b").Replace(
  39. changelog,
  40. match => string.Format(
  41. "<size={0}>{1}</size>",
  42. 30 - match.Groups[1].Value.Length * 6,
  43. match.Groups[2].Value
  44. )
  45. );
  46. changelog = changelog.Replace(" *", "*");
  47. // Each char gets turned into two triangles and the mesh vertices limit is 2^16.
  48. // Let's use another 100 to be on the safe side.
  49. const int textLengthLimit = 65536 / 4 - 100;
  50. latestRelease.changelogPages = new List<string>((int)Mathf.Ceil(changelog.Length / (float)textLengthLimit));
  51. while (true)
  52. {
  53. if (changelog.Length > textLengthLimit)
  54. {
  55. int startIndex = Math.Min(changelog.Length, textLengthLimit);
  56. int lastIndexOf = changelog.LastIndexOf("\n", startIndex, StringComparison.Ordinal);
  57. if (lastIndexOf == -1)
  58. {
  59. lastIndexOf = startIndex;
  60. }
  61. latestRelease.changelogPages.Add(changelog.Substring(0, lastIndexOf));
  62. changelog = changelog.Substring(lastIndexOf).TrimStart('\n', '\r');
  63. }
  64. else
  65. {
  66. latestRelease.changelogPages.Add(changelog);
  67. break;
  68. }
  69. }
  70. return latestRelease;
  71. }
  72. }
  73. private const string remoteURL = "https://api.github.com/repos/thestonefox/vrtk/releases/latest";
  74. private const string assetStoreURL = "/content/64131";
  75. private const string hidePromptKeyFormat = "VRTK.HideVersionPrompt.v{0}";
  76. private const string lastCheckKey = "VRTK.VersionPromptUpdate";
  77. private const int checkUpdateHours = 6;
  78. private static bool isManualCheck;
  79. private static bool versionChecked;
  80. private static WWW versionResource;
  81. private static LatestRelease latestRelease;
  82. private static VRTK_UpdatePrompt promptWindow;
  83. private static Vector2 scrollPosition;
  84. private static Vector2 changelogScrollPosition;
  85. private static float changelogWidth;
  86. private static int changelogPageIndex;
  87. private static bool isChangelogFoldOut = true;
  88. static VRTK_UpdatePrompt()
  89. {
  90. EditorApplication.update += CheckForUpdate;
  91. }
  92. public void OnGUI()
  93. {
  94. using (GUILayout.ScrollViewScope scrollViewScope = new GUILayout.ScrollViewScope(scrollPosition))
  95. {
  96. scrollPosition = scrollViewScope.scrollPosition;
  97. if (versionResource != null && !versionResource.isDone)
  98. {
  99. EditorGUILayout.HelpBox("Checking for updates...", MessageType.Info);
  100. return;
  101. }
  102. if (latestRelease == null)
  103. {
  104. EditorGUILayout.HelpBox("There was a problem checking for updates.", MessageType.Error);
  105. DrawCheckAgainButton();
  106. return;
  107. }
  108. string newVersionName = latestRelease.name == string.Format("Version {0}", latestRelease.version)
  109. ? string.Empty
  110. : string.Format(" \"{0}\"", latestRelease.name);
  111. bool isUpToDate = VRTK_Defines.CurrentVersion >= latestRelease.version;
  112. EditorGUILayout.HelpBox(
  113. string.Format(
  114. "{0}.\n\nInstalled Version: {1}\nAvailable version: {2}{3} (published on {4})",
  115. isUpToDate ? "Already up to date" : "A new version of VRTK is available",
  116. VRTK_Defines.CurrentVersion,
  117. latestRelease.version,
  118. newVersionName,
  119. latestRelease.publishedDateTime.ToLocalTime()),
  120. isUpToDate ? MessageType.Info : MessageType.Warning);
  121. DrawCheckAgainButton();
  122. isChangelogFoldOut = EditorGUILayout.Foldout(isChangelogFoldOut, "Changelog", true);
  123. if (isChangelogFoldOut)
  124. {
  125. using (new EditorGUILayout.HorizontalScope())
  126. {
  127. GUILayout.Space(10);
  128. using (new EditorGUILayout.VerticalScope())
  129. {
  130. VRTK_EditorUtilities.DrawScrollableSelectableLabel(
  131. ref changelogScrollPosition,
  132. ref changelogWidth,
  133. latestRelease.changelogPages[changelogPageIndex],
  134. new GUIStyle(EditorStyles.textArea)
  135. {
  136. richText = true
  137. });
  138. if (latestRelease.changelogPages.Count > 0)
  139. {
  140. using (new EditorGUILayout.HorizontalScope())
  141. {
  142. using (new EditorGUI.DisabledGroupScope(changelogPageIndex == 0))
  143. {
  144. if (GUILayout.Button("Previous Page"))
  145. {
  146. changelogPageIndex = Math.Max(0, --changelogPageIndex);
  147. changelogScrollPosition = Vector3.zero;
  148. }
  149. }
  150. using (new EditorGUI.DisabledGroupScope(changelogPageIndex == latestRelease.changelogPages.Count - 1))
  151. {
  152. if (GUILayout.Button("Next Page"))
  153. {
  154. changelogPageIndex = Math.Min(latestRelease.changelogPages.Count - 1, ++changelogPageIndex);
  155. changelogScrollPosition = Vector3.zero;
  156. }
  157. }
  158. }
  159. }
  160. if (GUILayout.Button("View on GitHub"))
  161. {
  162. Application.OpenURL(latestRelease.html_url);
  163. }
  164. }
  165. }
  166. }
  167. if (isUpToDate)
  168. {
  169. return;
  170. }
  171. GUILayout.FlexibleSpace();
  172. VRTK_EditorUtilities.AddHeader("Get Latest Version");
  173. using (new EditorGUILayout.HorizontalScope())
  174. {
  175. if (GUILayout.Button("From Asset Store"))
  176. {
  177. AssetStore.Open(assetStoreURL);
  178. Close();
  179. }
  180. if (GUILayout.Button("From GitHub"))
  181. {
  182. Application.OpenURL(latestRelease.zipball_url);
  183. }
  184. }
  185. using (EditorGUI.ChangeCheckScope changeCheckScope = new EditorGUI.ChangeCheckScope())
  186. {
  187. string key = string.Format(hidePromptKeyFormat, latestRelease.version);
  188. bool hideToggle = EditorPrefs.HasKey(key);
  189. hideToggle = GUILayout.Toggle(hideToggle, "Do not prompt for this version again.");
  190. if (changeCheckScope.changed)
  191. {
  192. if (hideToggle)
  193. {
  194. EditorPrefs.SetBool(key, true);
  195. }
  196. else
  197. {
  198. EditorPrefs.DeleteKey(key);
  199. }
  200. }
  201. }
  202. }
  203. }
  204. private static void DrawCheckAgainButton()
  205. {
  206. if (GUILayout.Button("Check again"))
  207. {
  208. CheckManually();
  209. }
  210. }
  211. private static void CheckForUpdate()
  212. {
  213. changelogScrollPosition = Vector3.zero;
  214. changelogWidth = 0;
  215. changelogPageIndex = 0;
  216. if (isManualCheck)
  217. {
  218. ShowWindow();
  219. }
  220. else
  221. {
  222. if (versionChecked)
  223. {
  224. EditorApplication.update -= CheckForUpdate;
  225. return;
  226. }
  227. if (EditorPrefs.HasKey(lastCheckKey))
  228. {
  229. string lastCheckTicksString = EditorPrefs.GetString(lastCheckKey);
  230. DateTime lastCheckDateTime = new DateTime(Convert.ToInt64(lastCheckTicksString));
  231. if (lastCheckDateTime.AddHours(checkUpdateHours) >= DateTime.UtcNow)
  232. {
  233. versionChecked = true;
  234. return;
  235. }
  236. }
  237. }
  238. versionResource = versionResource == null ? new WWW(remoteURL) : versionResource;
  239. if (!versionResource.isDone)
  240. {
  241. return;
  242. }
  243. EditorApplication.update -= CheckForUpdate;
  244. if (string.IsNullOrEmpty(versionResource.error))
  245. {
  246. latestRelease = LatestRelease.CreateFromJSON(versionResource.text);
  247. }
  248. versionResource.Dispose();
  249. versionResource = null;
  250. versionChecked = true;
  251. EditorPrefs.SetString(lastCheckKey, DateTime.UtcNow.Ticks.ToString());
  252. // Clean up the existing hidePromptKeys (except the one for the current version)
  253. new[] { VRTK_Defines.CurrentVersion }
  254. .Concat(VRTK_Defines.PreviousVersions)
  255. .Where(version => latestRelease == null || version != latestRelease.version)
  256. .Select(version => string.Format(hidePromptKeyFormat, version))
  257. .Where(EditorPrefs.HasKey)
  258. .ToList()
  259. .ForEach(EditorPrefs.DeleteKey);
  260. if (!isManualCheck
  261. && latestRelease != null
  262. && (VRTK_Defines.CurrentVersion >= latestRelease.version
  263. || EditorPrefs.HasKey(string.Format(hidePromptKeyFormat, latestRelease.version))))
  264. {
  265. return;
  266. }
  267. ShowWindow();
  268. isManualCheck = false;
  269. }
  270. private static void ShowWindow()
  271. {
  272. if (promptWindow != null)
  273. {
  274. promptWindow.ShowUtility();
  275. promptWindow.Repaint();
  276. return;
  277. }
  278. promptWindow = GetWindow<VRTK_UpdatePrompt>(true);
  279. promptWindow.titleContent = new GUIContent("VRTK Update");
  280. }
  281. [MenuItem("Window/VRTK/Check for Updates")]
  282. private static void CheckManually()
  283. {
  284. isManualCheck = true;
  285. if (versionResource == null)
  286. {
  287. EditorApplication.update += CheckForUpdate;
  288. }
  289. }
  290. }