AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
BuildDeployWindow.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.IO;
8 using System.Linq;
9 using System.Xml;
10 using UnityEditor;
11 using UnityEngine;
12 using Debug = UnityEngine.Debug;
13 
14 namespace HoloToolkit.Unity
15 {
20  public class BuildDeployWindow : EditorWindow
21  {
22  private const float UpdateBuildsPeriod = 1.0f;
23 
24  private const string SdkVersion =
25 #if UNITY_2017_2_OR_NEWER
26  "10.0.17134.0";
27 #else
28  "10.0.15063.0";
29 #endif
30 
31  private readonly string[] tabNames = { "Unity Build Options", "Appx Build Options", "Deploy Options" };
32 
33  private readonly string[] scriptingBackendNames = { "IL2CPP", ".NET" };
34 
35  private readonly int[] scriptingBackendEnum = { (int)ScriptingImplementation.IL2CPP, (int)ScriptingImplementation.WinRTDotNET };
36 
37  private readonly string[] deviceNames = { "Any Device", "PC", "Mobile", "HoloLens" };
38 
39  private readonly List<string> builds = new List<string>(0);
40 
41  private static readonly List<string> appPackageDirectories = new List<string>(0);
42 
43  #region Labels
44 
45  private readonly GUIContent buildAllThenInstallLabel = new GUIContent("Build all, then Install", "Builds the Unity Project, the APPX, then installs to the target device.");
46 
47  private readonly GUIContent buildAllLabel = new GUIContent("Build all", "Builds the Unity Project and APPX");
48 
49  private readonly GUIContent buildDirectoryLabel = new GUIContent("Build Directory", "It's recommended to use 'UWP'");
50 
51  private readonly GUIContent useCSharpProjectsLabel = new GUIContent("Unity C# Projects", "Generate C# Project References for debugging");
52 
53  private readonly GUIContent autoIncrementLabel = new GUIContent("Auto Increment", "Increases Version Build Number");
54 
55  private readonly GUIContent versionNumberLabel = new GUIContent("Version Number", "Major.Minor.Build.Revision\nNote: Revision should always be zero because it's reserved by Windows Store.");
56 
57  private readonly GUIContent pairHoloLensUsbLabel = new GUIContent("Pair HoloLens", "Pairs the USB connected HoloLens with the Build Window so you can deploy via USB");
58 
59  private readonly GUIContent useSSLLabel = new GUIContent("Use SSL?", "Use SLL to communicate with Device Portal");
60 
61  private readonly GUIContent addConnectionLabel = new GUIContent("+", "Add a remote connection");
62 
63  private readonly GUIContent removeConnectionLabel = new GUIContent("-", "Remove a remote connection");
64 
65  private readonly GUIContent ipAddressLabel = new GUIContent("IpAddress", "Note: Local Machine will install on any HoloLens connected to USB as well.");
66 
67  private readonly GUIContent doAllLabel = new GUIContent(" Do actions on all devices", "Should the build options perform actions on all the connected devices?");
68 
69  private readonly GUIContent uninstallLabel = new GUIContent("Uninstall First", "Uninstall application before installing");
70 
71  #endregion
72 
73  private enum BuildDeployTab
74  {
75  UnityBuildOptions,
76  AppxBuildOptions,
77  DeployOptions
78  }
79 
80  private enum BuildPlatformEnum
81  {
82  x86 = 1,
83  x64 = 2
84  }
85 
86  private enum BuildConfigEnum
87  {
88  Debug = 0,
89  Release = 1,
90  Master = 2
91  }
92 
93  #region Properties
94 
95  private static bool ShouldOpenSLNBeEnabled
96  {
97  get { return !string.IsNullOrEmpty(BuildDeployPrefs.BuildDirectory); }
98  }
99 
100  private static bool ShouldBuildSLNBeEnabled
101  {
102  get { return !string.IsNullOrEmpty(BuildDeployPrefs.BuildDirectory); }
103  }
104 
105  private static bool ShouldBuildAppxBeEnabled
106  {
107  get
108  {
109  return ShouldBuildSLNBeEnabled &&
110  !string.IsNullOrEmpty(BuildDeployPrefs.BuildDirectory) &&
111  !string.IsNullOrEmpty(BuildDeployPrefs.MsBuildVersion) &&
112  !string.IsNullOrEmpty(BuildDeployPrefs.BuildConfig);
113  }
114  }
115 
116  private static bool DevicePortalConnectionEnabled
117  {
118  get { return (portalConnections.Connections.Count > 1 || IsHoloLensConnectedUsb) && !string.IsNullOrEmpty(BuildDeployPrefs.BuildDirectory); }
119  }
120 
121  private static bool CanInstall
122  {
123  get
124  {
125  bool canInstall = true;
126  if (EditorUserBuildSettings.wsaSubtarget == WSASubtarget.HoloLens)
127  {
128  canInstall = DevicePortalConnectionEnabled;
129  }
130 
131  return canInstall && Directory.Exists(BuildDeployPrefs.AbsoluteBuildDirectory);
132  }
133  }
134 
135  private static string familyPackageName;
136 
137  private static bool IsHoloLensConnectedUsb
138  {
139  get
140  {
141  bool isConnected = false;
142 
143  if (USBDeviceListener.USBDevices != null)
144  {
145  if (USBDeviceListener.USBDevices.Any(device => device.Name.Equals("Microsoft HoloLens")))
146  {
147  isConnected = true;
148  }
149 
150  SessionState.SetBool("HoloLensUsbConnected", isConnected);
151  }
152  else
153  {
154  isConnected = SessionState.GetBool("HoloLensUsbConnected", false);
155  }
156 
157  return isConnected;
158  }
159  }
160 
161  #endregion // Properties
162 
163  #region Fields
164 
165  private int halfWidth;
166  private int quarterWidth;
167 
168  private float timeLastUpdatedBuilds;
169 
170  private string[] targetIps;
171  private string[] windowsSdkPaths;
172 
173  private Vector2 scrollPosition;
174 
175  private BuildDeployTab currentTab = BuildDeployTab.UnityBuildOptions;
176 
177  private static bool isAppRunning;
178  private static int currentConnectionInfoIndex;
179  private static DevicePortalConnections portalConnections;
180 
181  #endregion // Fields
182 
183  #region Methods
184 
185  [MenuItem("Mixed Reality Toolkit/Build Window", false, 8)]
186  public static void OpenWindow()
187  {
188  // Dock it next to the Scene View.
189  var window = GetWindow<BuildDeployWindow>(typeof(SceneView));
190  window.titleContent = new GUIContent("Build Window");
191  window.Show();
192  }
193 
194  private void OnEnable()
195  {
196  Setup();
197  }
198 
199  private void Setup()
200  {
201  titleContent = new GUIContent("Build Window");
202  minSize = new Vector2(512, 256);
203 
204  windowsSdkPaths = Directory.GetDirectories(@"C:\Program Files (x86)\Windows Kits\10\Lib");
205 
206  for (int i = 0; i < windowsSdkPaths.Length; i++)
207  {
208  windowsSdkPaths[i] = windowsSdkPaths[i].Substring(windowsSdkPaths[i].LastIndexOf(@"\", StringComparison.Ordinal) + 1);
209  }
210 
211  UpdateBuilds();
212 
213  portalConnections = JsonUtility.FromJson<DevicePortalConnections>(BuildDeployPrefs.DevicePortalConnections);
214  UpdatePortalConnections();
215  }
216 
217  private void OnGUI()
218  {
219  quarterWidth = Screen.width / 4;
220  halfWidth = Screen.width / 2;
221 
222  #region Quick Options
223 
224  if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WSAPlayer)
225  {
226  EditorGUILayout.HelpBox("Build window only available for UWP build target.", MessageType.Warning);
227  GUILayout.BeginVertical();
228  GUILayout.Space(5);
229  EditorGUILayout.BeginHorizontal();
230 
231  // Build directory (and save setting, if it's changed)
232  string curBuildDirectory = BuildDeployPrefs.BuildDirectory;
233  EditorGUILayout.LabelField(buildDirectoryLabel, GUILayout.Width(96));
234  string newBuildDirectory = EditorGUILayout.TextField(curBuildDirectory, GUILayout.Width(64), GUILayout.ExpandWidth(true));
235 
236  if (newBuildDirectory != curBuildDirectory)
237  {
238  BuildDeployPrefs.BuildDirectory = newBuildDirectory;
239  }
240 
241  GUI.enabled = Directory.Exists(BuildDeployPrefs.AbsoluteBuildDirectory);
242 
243  if (GUILayout.Button("Open Build Directory", GUILayout.Width(quarterWidth)))
244  {
245  EditorApplication.delayCall += () => Process.Start(BuildDeployPrefs.AbsoluteBuildDirectory);
246  }
247 
248  GUI.enabled = true;
249 
250  if (GUILayout.Button("Open Player Settings", GUILayout.Width(quarterWidth)))
251  {
252  EditorApplication.ExecuteMenuItem("Edit/Project Settings/Player");
253  }
254 
255  EditorGUILayout.EndHorizontal();
256  GUILayout.EndVertical();
257  return;
258  }
259 
260  GUILayout.BeginVertical();
261  GUILayout.Space(5);
262  GUILayout.Label("Quick Options");
263  EditorGUILayout.BeginHorizontal();
264 
265  EditorUserBuildSettings.wsaSubtarget = (WSASubtarget)EditorGUILayout.Popup((int)EditorUserBuildSettings.wsaSubtarget, deviceNames);
266 
267  bool canInstall = CanInstall;
268 
269  if (EditorUserBuildSettings.wsaSubtarget == WSASubtarget.HoloLens && !IsHoloLensConnectedUsb)
270  {
271  canInstall = IsHoloLensConnectedUsb;
272  }
273 
274  GUI.enabled = ShouldBuildSLNBeEnabled;
275 
276  // Build & Run button...
277  if (GUILayout.Button(CanInstall ? buildAllThenInstallLabel : buildAllLabel, GUILayout.Width(halfWidth - 20)))
278  {
279  EditorApplication.delayCall += () => BuildAll(canInstall);
280  }
281 
282  GUI.enabled = true;
283 
284  if (GUILayout.Button("Open Player Settings", GUILayout.Width(quarterWidth)))
285  {
286  EditorApplication.ExecuteMenuItem("Edit/Project Settings/Player");
287  }
288 
289  EditorGUILayout.EndHorizontal();
290  GUILayout.EndVertical();
291  GUILayout.Space(10);
292 
293  #endregion Quick Options
294 
295  currentTab = (BuildDeployTab)GUILayout.Toolbar(SessionState.GetInt("_MRTK_BuildWindow_Tab", (int)currentTab), tabNames);
296  SessionState.SetInt("_MRTK_BuildWindow_Tab", (int)currentTab);
297 
298  GUILayout.Space(10);
299 
300  switch (currentTab)
301  {
302  case BuildDeployTab.UnityBuildOptions:
303  UnityBuildGUI();
304  break;
305  case BuildDeployTab.AppxBuildOptions:
306  AppxBuildGUI();
307  break;
308  case BuildDeployTab.DeployOptions:
309  DeployGUI();
310  break;
311  default:
312  throw new ArgumentOutOfRangeException();
313  }
314  }
315 
316  private void Update()
317  {
318  if (Time.realtimeSinceStartup - timeLastUpdatedBuilds > UpdateBuildsPeriod)
319  {
320  UpdateBuilds();
321  }
322  }
323 
324  private void UnityBuildGUI()
325  {
326  GUILayout.BeginVertical();
327  EditorGUILayout.BeginHorizontal();
328 
329  // Build directory (and save setting, if it's changed)
330  string curBuildDirectory = BuildDeployPrefs.BuildDirectory;
331  EditorGUILayout.LabelField(buildDirectoryLabel, GUILayout.Width(96));
332  string newBuildDirectory = EditorGUILayout.TextField(
333  curBuildDirectory,
334  GUILayout.Width(64), GUILayout.ExpandWidth(true));
335 
336  if (newBuildDirectory != curBuildDirectory)
337  {
338  BuildDeployPrefs.BuildDirectory = newBuildDirectory;
339  }
340 
341  GUI.enabled = Directory.Exists(BuildDeployPrefs.AbsoluteBuildDirectory);
342 
343  if (GUILayout.Button("Open Build Directory", GUILayout.Width(halfWidth)))
344  {
345  EditorApplication.delayCall += () => Process.Start(BuildDeployPrefs.AbsoluteBuildDirectory);
346  }
347 
348  GUI.enabled = true;
349 
350  EditorGUILayout.EndHorizontal();
351  EditorGUILayout.BeginHorizontal();
352  GUILayout.FlexibleSpace();
353 
354  GUI.enabled = ShouldOpenSLNBeEnabled;
355 
356  if (GUILayout.Button("Open in Visual Studio", GUILayout.Width(halfWidth)))
357  {
358  // Open SLN
359  string slnFilename = Path.Combine(BuildDeployPrefs.BuildDirectory, PlayerSettings.productName + ".sln");
360 
361  if (File.Exists(slnFilename))
362  {
363  EditorApplication.delayCall += () => Process.Start(new FileInfo(slnFilename).FullName);
364  }
365  else if (EditorUtility.DisplayDialog(
366  "Solution Not Found",
367  "We couldn't find the Project's Solution. Would you like to Build the project now?",
368  "Yes, Build", "No"))
369  {
370  EditorApplication.delayCall += () => BuildDeployTools.BuildSLN(BuildDeployPrefs.BuildDirectory);
371  }
372  }
373 
374 
375  EditorGUILayout.EndHorizontal();
376  EditorGUILayout.BeginHorizontal();
377 
378  // Generate C# Project References for debugging
379  GUILayout.FlexibleSpace();
380  var previousLabelWidth = EditorGUIUtility.labelWidth;
381  EditorGUIUtility.labelWidth = 105;
382  bool generateReferenceProjects = EditorUserBuildSettings.wsaGenerateReferenceProjects;
383  bool shouldGenerateProjects = EditorGUILayout.Toggle(useCSharpProjectsLabel, generateReferenceProjects);
384 
385  if (shouldGenerateProjects != generateReferenceProjects)
386  {
387  EditorUserBuildSettings.wsaGenerateReferenceProjects = shouldGenerateProjects;
388  }
389 
390  EditorGUIUtility.labelWidth = previousLabelWidth;
391 
392  // Build Unity Player
393  GUI.enabled = ShouldBuildSLNBeEnabled;
394 
395  if (GUILayout.Button("Build Unity Project", GUILayout.Width(halfWidth)))
396  {
397  EditorApplication.delayCall += () => BuildDeployTools.BuildSLN(BuildDeployPrefs.BuildDirectory);
398  }
399 
400  GUI.enabled = true;
401 
402  EditorGUILayout.EndHorizontal();
403  EditorGUILayout.EndVertical();
404  }
405 
406  private void AppxBuildGUI()
407  {
408  GUILayout.BeginVertical();
409  GUILayout.BeginHorizontal();
410 
411  // SDK and MS Build Version(and save setting, if it's changed)
412  string currentSDKVersion = EditorUserBuildSettings.wsaUWPSDK;
413 
414  int currentSDKVersionIndex = -1;
415 
416  for (var i = 0; i < windowsSdkPaths.Length; i++)
417  {
418  if (string.IsNullOrEmpty(currentSDKVersion))
419  {
420  currentSDKVersionIndex = windowsSdkPaths.Length - 1;
421  }
422  else
423  {
424  if (windowsSdkPaths[i].Equals(SdkVersion))
425  {
426  currentSDKVersionIndex = i;
427  }
428  }
429  }
430 
431  EditorGUILayout.LabelField("Required SDK Version: " + SdkVersion);
432 
433  // Throw exception if user has no Windows 10 SDK installed
434  if (currentSDKVersionIndex < 0)
435  {
436  Debug.LogErrorFormat("Unable to find the required Windows 10 SDK Target!\n" +
437  "Please be sure to install the {0} SDK from Visual Studio Installer.", SdkVersion);
438  GUILayout.EndHorizontal();
439 
440  EditorGUILayout.HelpBox(string.Format("Unable to find the required Windows 10 SDK Target!\n" +
441  "Please be sure to install the {0} SDK from Visual Studio Installer.", SdkVersion), MessageType.Error);
442 
443  GUILayout.BeginHorizontal();
444  }
445 
446  var curScriptingBackend = PlayerSettings.GetScriptingBackend(BuildTargetGroup.WSA);
447 
448  if (curScriptingBackend == ScriptingImplementation.WinRTDotNET)
449  {
450 #if UNITY_2018_2_OR_NEWER
451  EditorGUILayout.HelpBox(".NET Scripting backend is deprecated, please use IL2CPP.", MessageType.Warning);
452 #else
453  EditorGUILayout.HelpBox(".NET Scripting backend will be deprecated in 2018.2, please consider using IL2CPP.", MessageType.Info);
454 #endif
455  GUILayout.EndHorizontal();
456  GUILayout.BeginHorizontal();
457  GUILayout.FlexibleSpace();
458  }
459 
460  var newScriptingBackend = (ScriptingImplementation)EditorGUILayout.IntPopup(
461  "Scripting Backend",
462  (int)curScriptingBackend,
463  scriptingBackendNames,
464  scriptingBackendEnum,
465  GUILayout.Width(halfWidth));
466 
467  if (newScriptingBackend != curScriptingBackend)
468  {
469  bool canUpdate = !Directory.Exists(BuildDeployPrefs.AbsoluteBuildDirectory);
470 
471  if (!canUpdate &&
472  EditorUtility.DisplayDialog("Attention!",
473  string.Format("Build path contains project built with {0} scripting backend, while current project is using {1} scripting backend.\n\n" +
474  "Switching to a new scripting backend requires us to delete all the data currently in your build folder and rebuild the Unity Player!",
475  newScriptingBackend.ToString(),
476  curScriptingBackend.ToString()),
477  "Okay", "Cancel"))
478  {
479  Directory.Delete(BuildDeployPrefs.AbsoluteBuildDirectory, true);
480  canUpdate = true;
481  }
482 
483  if (canUpdate)
484  {
485  PlayerSettings.SetScriptingBackend(BuildTargetGroup.WSA, newScriptingBackend);
486  }
487  }
488 
489  string newSDKVersion = windowsSdkPaths[currentSDKVersionIndex];
490 
491  if (!newSDKVersion.Equals(currentSDKVersion))
492  {
493  EditorUserBuildSettings.wsaUWPSDK = newSDKVersion;
494  }
495 
496  GUILayout.EndHorizontal();
497  GUILayout.BeginHorizontal();
498  GUILayout.FlexibleSpace();
499 
500  // Build config (and save setting, if it's changed)
501  string curBuildConfigString = BuildDeployPrefs.BuildConfig;
502 
503  BuildConfigEnum buildConfigOption;
504  if (curBuildConfigString.ToLower().Equals("master"))
505  {
506  buildConfigOption = BuildConfigEnum.Master;
507  }
508  else if (curBuildConfigString.ToLower().Equals("release"))
509  {
510  buildConfigOption = BuildConfigEnum.Release;
511  }
512  else
513  {
514  buildConfigOption = BuildConfigEnum.Debug;
515  }
516 
517  buildConfigOption = (BuildConfigEnum)EditorGUILayout.EnumPopup("Build Configuration", buildConfigOption, GUILayout.Width(halfWidth));
518 
519  string buildConfigString = buildConfigOption.ToString();
520 
521  if (buildConfigString != curBuildConfigString)
522  {
523  BuildDeployPrefs.BuildConfig = buildConfigString;
524  }
525 
526  GUILayout.EndHorizontal();
527  GUILayout.BeginHorizontal();
528  GUILayout.FlexibleSpace();
529 
530  // Build Platform (and save setting, if it's changed)
531  string curBuildPlatformString = BuildDeployPrefs.BuildPlatform;
532  var buildPlatformOption = BuildPlatformEnum.x86;
533 
534  if (curBuildPlatformString.ToLower().Equals("x86"))
535  {
536  buildPlatformOption = BuildPlatformEnum.x86;
537  }
538  else if (curBuildPlatformString.ToLower().Equals("x64"))
539  {
540  buildPlatformOption = BuildPlatformEnum.x64;
541  }
542 
543  buildPlatformOption = (BuildPlatformEnum)EditorGUILayout.EnumPopup("Build Platform", buildPlatformOption, GUILayout.Width(halfWidth));
544 
545  string newBuildPlatformString;
546 
547  switch (buildPlatformOption)
548  {
549  case BuildPlatformEnum.x86:
550  case BuildPlatformEnum.x64:
551  newBuildPlatformString = buildPlatformOption.ToString();
552  break;
553  default:
554  throw new ArgumentOutOfRangeException();
555  }
556 
557  if (newBuildPlatformString != curBuildPlatformString)
558  {
559  BuildDeployPrefs.BuildPlatform = newBuildPlatformString;
560  }
561 
562  GUILayout.EndHorizontal();
563  GUILayout.BeginHorizontal();
564  GUILayout.FlexibleSpace();
565 
566  var previousLabelWidth = EditorGUIUtility.labelWidth;
567 
568  // Auto Increment version
569  EditorGUIUtility.labelWidth = 96;
570  bool curIncrementVersion = BuildDeployPrefs.IncrementBuildVersion;
571  bool newIncrementVersion = EditorGUILayout.Toggle(autoIncrementLabel, curIncrementVersion);
572 
573  // Restore previous label width
574  EditorGUIUtility.labelWidth = previousLabelWidth;
575 
576  if (newIncrementVersion != curIncrementVersion)
577  {
578  BuildDeployPrefs.IncrementBuildVersion = newIncrementVersion;
579  }
580 
581  EditorGUILayout.LabelField(versionNumberLabel, GUILayout.Width(96));
582  Vector3 newVersion = Vector3.zero;
583 
584  EditorGUI.BeginChangeCheck();
585 
586  newVersion.x = EditorGUILayout.IntField(PlayerSettings.WSA.packageVersion.Major, GUILayout.Width(quarterWidth / 2 - 3));
587  newVersion.y = EditorGUILayout.IntField(PlayerSettings.WSA.packageVersion.Minor, GUILayout.Width(quarterWidth / 2 - 3));
588  newVersion.z = EditorGUILayout.IntField(PlayerSettings.WSA.packageVersion.Build, GUILayout.Width(quarterWidth / 2 - 3));
589 
590  if (EditorGUI.EndChangeCheck())
591  {
592  PlayerSettings.WSA.packageVersion = new Version((int)newVersion.x, (int)newVersion.y, (int)newVersion.z, 0);
593  }
594 
595  GUI.enabled = false;
596  EditorGUILayout.IntField(PlayerSettings.WSA.packageVersion.Revision, GUILayout.Width(quarterWidth / 2 - 3));
597  GUI.enabled = true;
598 
599  GUILayout.EndHorizontal();
600  GUILayout.BeginHorizontal();
601  GUILayout.FlexibleSpace();
602 
603  // Force rebuild
604  previousLabelWidth = EditorGUIUtility.labelWidth;
605  EditorGUIUtility.labelWidth = 50;
606  bool curForceRebuildAppx = BuildDeployPrefs.ForceRebuild;
607  bool newForceRebuildAppx = EditorGUILayout.Toggle("Rebuild", curForceRebuildAppx);
608 
609  if (newForceRebuildAppx != curForceRebuildAppx)
610  {
611  BuildDeployPrefs.ForceRebuild = newForceRebuildAppx;
612  }
613 
614  // Restore previous label width
615  EditorGUIUtility.labelWidth = previousLabelWidth;
616 
617  // Build APPX
618  GUI.enabled = ShouldBuildAppxBeEnabled;
619 
620  if (GUILayout.Button("Build APPX", GUILayout.Width(halfWidth)))
621  {
622  // Check if SLN exists
623  string slnFilename = Path.Combine(BuildDeployPrefs.BuildDirectory, PlayerSettings.productName + ".sln");
624 
625  if (File.Exists(slnFilename))
626  {
627  // Build APPX
628  EditorApplication.delayCall += () =>
629  {
631  PlayerSettings.productName,
638  };
639  }
640  else if (EditorUtility.DisplayDialog("Solution Not Found", "We couldn't find the solution. Would you like to Build it?", "Yes, Build", "No"))
641  {
642  // Build SLN then APPX
643  EditorApplication.delayCall += () => BuildAll(install: false);
644  }
645 
646  GUI.enabled = true;
647  }
648 
649  GUILayout.EndHorizontal();
650  GUILayout.BeginHorizontal();
651  GUILayout.FlexibleSpace();
652 
653  // Open AppX packages location
654  string appxDirectory = curScriptingBackend == ScriptingImplementation.IL2CPP ? "/AppPackages/" + PlayerSettings.productName : "/" + PlayerSettings.productName + "/AppPackages";
655  string appxBuildPath = Path.GetFullPath(BuildDeployPrefs.BuildDirectory + appxDirectory);
656  GUI.enabled = builds.Count > 0 && !string.IsNullOrEmpty(appxBuildPath);
657 
658  if (GUILayout.Button("Open APPX Packages Location", GUILayout.Width(halfWidth)))
659  {
660  EditorApplication.delayCall += () => Process.Start("explorer.exe", "/f /open," + appxBuildPath);
661  }
662 
663  GUI.enabled = true;
664 
665  GUILayout.EndHorizontal();
666  GUILayout.EndVertical();
667  }
668 
669  private void DeployGUI()
670  {
671  Debug.Assert(portalConnections.Connections.Count != 0);
672  Debug.Assert(currentConnectionInfoIndex >= 0);
673 
674  GUILayout.BeginVertical();
675  EditorGUI.BeginChangeCheck();
676  GUILayout.BeginHorizontal();
677 
678  // Launch app...
679  GUI.enabled = IsHoloLensConnectedUsb;
680 
681  if (GUILayout.Button(pairHoloLensUsbLabel, GUILayout.Width(quarterWidth)))
682  {
683  EditorApplication.delayCall += () =>
684  {
685  var newConnection = default(ConnectInfo);
686 
687  foreach (var targetDevice in portalConnections.Connections)
688  {
689  if (!IsLocalConnection(targetDevice))
690  {
691  continue;
692  }
693 
694  var machineName = BuildDeployPortal.GetMachineName(targetDevice);
695  var networkInfo = BuildDeployPortal.GetNetworkInfo(targetDevice);
696 
697  if (networkInfo != null)
698  {
699  var newIps = new List<string>();
700  foreach (var adapter in networkInfo.Adapters)
701  {
702  newIps.AddRange(from address in adapter.IpAddresses where !address.IpAddress.Contains("0.0.0.0") select address.IpAddress);
703  }
704 
705  if (newIps.Count == 0)
706  {
707  Debug.LogWarning("This HoloLens is not connected to any networks and cannot be paired.");
708  }
709 
710  foreach (var ip in newIps)
711  {
712  if (portalConnections.Connections.Any(connection => connection.IP == ip))
713  {
714  Debug.LogFormat("Already paired");
715  continue;
716  }
717 
718  newConnection.IP = ip;
719  newConnection.User = targetDevice.User;
720  newConnection.Password = targetDevice.Password;
721 
722  if (machineName != null)
723  {
724  newConnection.MachineName = machineName.ComputerName;
725  }
726  }
727  }
728  }
729 
730  if (IsValidIpAddress(newConnection.IP))
731  {
732  portalConnections.Connections.Add(newConnection);
733  for (var i = 0; i < portalConnections.Connections.Count; i++)
734  {
735  if (portalConnections.Connections[i].IP == newConnection.IP)
736  {
737  currentConnectionInfoIndex = i;
738  SessionState.SetInt("_MRTK_BuildWindow_CurrentDeviceIndex", currentConnectionInfoIndex);
739  break;
740  }
741  }
742 
743  UpdatePortalConnections();
744  }
745  };
746  }
747 
748  GUI.enabled = true;
749 
750  GUILayout.FlexibleSpace();
751 
752  var previousLabelWidth = EditorGUIUtility.labelWidth;
753  EditorGUIUtility.labelWidth = 64;
754  bool useSSL = EditorGUILayout.Toggle(useSSLLabel, BuildDeployPrefs.UseSSL);
755  EditorGUIUtility.labelWidth = previousLabelWidth;
756 
757  currentConnectionInfoIndex = EditorGUILayout.Popup(
758  SessionState.GetInt("_MRTK_BuildWindow_CurrentDeviceIndex", 0), targetIps, GUILayout.Width(halfWidth - 48));
759 
760  var currentConnection = portalConnections.Connections[currentConnectionInfoIndex];
761 
762  bool currentConnectionIsLocal = IsLocalConnection(currentConnection);
763 
764  if (currentConnectionIsLocal)
765  {
766  currentConnection.MachineName = "Local Machine";
767  }
768 
769  GUI.enabled = IsValidIpAddress(currentConnection.IP);
770 
771  if (GUILayout.Button(addConnectionLabel, GUILayout.Width(20)))
772  {
773  portalConnections.Connections.Add(new ConnectInfo("0.0.0.0", currentConnection.User, currentConnection.Password));
774  currentConnectionInfoIndex++;
775  currentConnection = portalConnections.Connections[currentConnectionInfoIndex];
776  UpdatePortalConnections();
777  }
778 
779  GUI.enabled = portalConnections.Connections.Count > 1 && currentConnectionInfoIndex != 0;
780 
781  if (GUILayout.Button(removeConnectionLabel, GUILayout.Width(20)))
782  {
783  portalConnections.Connections.RemoveAt(currentConnectionInfoIndex);
784  currentConnectionInfoIndex--;
785  currentConnection = portalConnections.Connections[currentConnectionInfoIndex];
786  UpdatePortalConnections();
787  }
788 
789  GUI.enabled = true;
790 
791  GUILayout.EndHorizontal();
792  GUILayout.Space(5);
793  GUILayout.BeginHorizontal();
794  GUILayout.FlexibleSpace();
795 
796  GUILayout.Label(currentConnection.MachineName, GUILayout.Width(halfWidth));
797 
798  GUILayout.EndHorizontal();
799 
800  previousLabelWidth = EditorGUIUtility.labelWidth;
801  EditorGUIUtility.labelWidth = 64;
802  GUILayout.BeginHorizontal();
803  GUILayout.FlexibleSpace();
804 
805  GUI.enabled = !currentConnectionIsLocal;
806  currentConnection.IP = EditorGUILayout.TextField(
807  ipAddressLabel,
808  currentConnection.IP,
809  GUILayout.Width(halfWidth));
810  GUI.enabled = true;
811 
812  GUILayout.EndHorizontal();
813  GUILayout.BeginHorizontal();
814  GUILayout.FlexibleSpace();
815  currentConnection.User = EditorGUILayout.TextField("Username", currentConnection.User, GUILayout.Width(halfWidth));
816  GUILayout.EndHorizontal();
817  GUILayout.BeginHorizontal();
818  GUILayout.FlexibleSpace();
819  currentConnection.Password = EditorGUILayout.PasswordField("Password", currentConnection.Password, GUILayout.Width(halfWidth));
820  GUILayout.EndHorizontal();
821  GUILayout.BeginHorizontal();
822 
823  EditorGUIUtility.labelWidth = 152;
824 
825  bool processAll = EditorGUILayout.Toggle(
826  doAllLabel,
828  GUILayout.Width(176));
829 
830  EditorGUIUtility.labelWidth = 86;
831 
832  bool fullReinstall = EditorGUILayout.Toggle(
833  uninstallLabel,
835  GUILayout.ExpandWidth(false));
836  EditorGUIUtility.labelWidth = previousLabelWidth;
837 
838  if (EditorGUI.EndChangeCheck())
839  {
840  SessionState.SetInt("_MRTK_BuildWindow_CurrentDeviceIndex", currentConnectionInfoIndex);
842  BuildDeployPrefs.FullReinstall = fullReinstall;
843  BuildDeployPrefs.UseSSL = useSSL;
844 
845  // Format our local connection
846  if (currentConnection.IP.Contains("127.0.0.1"))
847  {
848  currentConnection.IP = "Local Machine";
849  }
850 
851  portalConnections.Connections[currentConnectionInfoIndex] = currentConnection;
852  UpdatePortalConnections();
853  Repaint();
854  }
855 
856  GUILayout.FlexibleSpace();
857 
858  // Connect
859  if (!IsLocalConnection(currentConnection))
860  {
861  GUI.enabled = IsValidIpAddress(currentConnection.IP) && IsCredentialsValid(currentConnection);
862 
863  if (GUILayout.Button("Connect", GUILayout.Width(quarterWidth)))
864  {
865  EditorApplication.delayCall += () =>
866  {
867  var machineName = BuildDeployPortal.GetMachineName(currentConnection);
868 
869  if (machineName != null)
870  {
871  currentConnection.MachineName = machineName.ComputerName;
872  }
873 
874  portalConnections.Connections[currentConnectionInfoIndex] = currentConnection;
875  UpdatePortalConnections();
876  Repaint();
877  };
878  }
879 
880  GUI.enabled = true;
881  }
882 
883  GUI.enabled = DevicePortalConnectionEnabled && CanInstall;
884 
885  // Open web portal
886  if (GUILayout.Button("Open Device Portal", GUILayout.Width(quarterWidth)))
887  {
888  EditorApplication.delayCall += () => OpenDevicePortal(portalConnections);
889  }
890 
891  GUI.enabled = true;
892 
893  GUILayout.EndHorizontal();
894 
895  // Build list
896  if (builds.Count == 0)
897  {
898  GUILayout.Label("*** No builds found in build directory", EditorStyles.boldLabel);
899  }
900  else
901  {
902  EditorGUILayout.Separator();
903  GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
904  scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
905 
906  foreach (var fullBuildLocation in builds)
907  {
908  int lastBackslashIndex = fullBuildLocation.LastIndexOf("\\", StringComparison.Ordinal);
909 
910  var directoryDate = Directory.GetLastWriteTime(fullBuildLocation).ToString("yyyy/MM/dd HH:mm:ss");
911  string packageName = fullBuildLocation.Substring(lastBackslashIndex + 1);
912 
913  GUILayout.Space(2);
914  EditorGUILayout.BeginHorizontal();
915  GUILayout.Space(12);
916 
917  GUI.enabled = CanInstall;
918  if (GUILayout.Button("Install", GUILayout.Width(96)))
919  {
920  EditorApplication.delayCall += () =>
921  {
922  if (processAll)
923  {
924  InstallAppOnDevicesList(fullBuildLocation, portalConnections);
925  }
926  else
927  {
928  InstallOnTargetDevice(fullBuildLocation, currentConnection);
929  }
930  };
931  }
932 
933  GUI.enabled = true;
934 
935  // Uninstall...
936  GUI.enabled = CanInstall;
937 
938  if (GUILayout.Button("Uninstall", GUILayout.Width(96)))
939  {
940  EditorApplication.delayCall += () =>
941  {
942  if (processAll)
943  {
944  UninstallAppOnDevicesList(portalConnections);
945  }
946  else
947  {
948  UninstallAppOnTargetDevice(familyPackageName, currentConnection);
949  }
950  };
951  }
952 
953  GUI.enabled = true;
954 
955  bool canLaunchLocal = currentConnectionInfoIndex == 0 && IsHoloLensConnectedUsb;
956  bool canLaunchRemote = DevicePortalConnectionEnabled && CanInstall && currentConnectionInfoIndex != 0;
957 
958  // Launch app...
959  GUI.enabled = canLaunchLocal || canLaunchRemote;
960 
961  if (GUILayout.Button(new GUIContent(isAppRunning ? "Kill App" : "Launch App", "These are remote commands only"), GUILayout.Width(96)))
962  {
963  EditorApplication.delayCall += () =>
964  {
965  if (isAppRunning)
966  {
967  if (processAll)
968  {
969  KillAppOnDeviceList(portalConnections);
970  isAppRunning = false;
971  }
972  else
973  {
974  isAppRunning = !KillAppOnTargetDevice(currentConnection);
975  }
976  }
977  else
978  {
979  if (processAll)
980  {
981  LaunchAppOnDeviceList(portalConnections);
982  isAppRunning = true;
983  }
984  else
985  {
986  isAppRunning = LaunchAppOnTargetDevice(currentConnection);
987  }
988  }
989  };
990  }
991 
992  GUI.enabled = true;
993 
994  // Log file
995  string localLogPath = string.Format("%USERPROFILE%\\AppData\\Local\\Packages\\{0}\\TempState\\UnityPlayer.log", PlayerSettings.productName);
996  bool localLogExists = File.Exists(localLogPath);
997 
998  GUI.enabled = localLogExists || canLaunchRemote || canLaunchLocal;
999 
1000  if (GUILayout.Button("View Log", GUILayout.Width(96)))
1001  {
1002  EditorApplication.delayCall += () =>
1003  {
1004  if (processAll)
1005  {
1006  OpenLogFilesOnDeviceList(portalConnections, localLogPath);
1007  }
1008  else
1009  {
1010  OpenLogFileForTargetDevice(currentConnection, localLogPath);
1011  }
1012  };
1013  }
1014 
1015  GUI.enabled = true;
1016 
1017  GUILayout.Space(8);
1018  GUILayout.Label(new GUIContent(packageName + " (" + directoryDate + ")"));
1019  EditorGUILayout.EndHorizontal();
1020  }
1021 
1022  GUILayout.EndScrollView();
1023  GUILayout.EndVertical();
1024  }
1025 
1026  GUILayout.EndVertical();
1027  }
1028 
1029  #endregion // Methods
1030 
1031  #region Utilities
1032 
1033  private void BuildAll(bool install = true)
1034  {
1035  // First build SLN
1037  {
1038  return;
1039  }
1040 
1041  // Next, APPX
1043  PlayerSettings.productName,
1050  showDialog: !install))
1051  {
1052  return;
1053  }
1054 
1055  // Next, Install
1056  if (install)
1057  {
1058  string fullBuildLocation = CalcMostRecentBuild();
1059 
1061  {
1062  InstallAppOnDevicesList(fullBuildLocation, portalConnections);
1063  }
1064  else
1065  {
1066  InstallOnTargetDevice(fullBuildLocation, portalConnections.Connections[currentConnectionInfoIndex]);
1067  }
1068  }
1069  }
1070 
1071  private void UpdateBuilds()
1072  {
1073  builds.Clear();
1074 
1075  var curScriptingBackend = PlayerSettings.GetScriptingBackend(BuildTargetGroup.WSA);
1076  string appxDirectory = curScriptingBackend == ScriptingImplementation.IL2CPP ? "AppPackages\\" + PlayerSettings.productName : PlayerSettings.productName + "\\AppPackages";
1077 
1078  try
1079  {
1080  appPackageDirectories.Clear();
1081  string[] buildList = Directory.GetDirectories(BuildDeployPrefs.AbsoluteBuildDirectory, "*", SearchOption.AllDirectories);
1082  foreach (string appBuild in buildList)
1083  {
1084  if (appBuild.Contains(appxDirectory) && !appBuild.Contains(appxDirectory + "\\"))
1085  {
1086  appPackageDirectories.AddRange(Directory.GetDirectories(appBuild));
1087  }
1088  }
1089 
1090  IEnumerable<string> selectedDirectories =
1091  from string directory in appPackageDirectories
1092  orderby Directory.GetLastWriteTime(directory) descending
1093  select Path.GetFullPath(directory);
1094  builds.AddRange(selectedDirectories);
1095  }
1096  catch (DirectoryNotFoundException)
1097  {
1098  // unused
1099  }
1100 
1101  familyPackageName = CalcPackageFamilyName();
1102 
1103  timeLastUpdatedBuilds = Time.realtimeSinceStartup;
1104  }
1105 
1106  private string CalcMostRecentBuild()
1107  {
1108  UpdateBuilds();
1109  DateTime mostRecent = DateTime.MinValue;
1110  string mostRecentBuild = string.Empty;
1111 
1112  foreach (var fullBuildLocation in builds)
1113  {
1114  DateTime directoryDate = Directory.GetLastWriteTime(fullBuildLocation);
1115 
1116  if (directoryDate > mostRecent)
1117  {
1118  mostRecentBuild = fullBuildLocation;
1119  mostRecent = directoryDate;
1120  }
1121  }
1122 
1123  return mostRecentBuild;
1124  }
1125 
1126  private void UpdatePortalConnections()
1127  {
1128  targetIps = new string[portalConnections.Connections.Count];
1129  if (currentConnectionInfoIndex > portalConnections.Connections.Count - 1)
1130  {
1131  currentConnectionInfoIndex = portalConnections.Connections.Count - 1;
1132  }
1133 
1134  targetIps[0] = "Local Machine";
1135  for (int i = 1; i < targetIps.Length; i++)
1136  {
1137  targetIps[i] = portalConnections.Connections[i].MachineName;
1138  }
1139 
1140  BuildDeployPrefs.DevicePortalConnections = JsonUtility.ToJson(portalConnections);
1141  Repaint();
1142  }
1143 
1144  private static bool IsLocalConnection(ConnectInfo connection)
1145  {
1146  return connection.IP.Contains("Local Machine") || connection.IP.Contains("127.0.0.1");
1147  }
1148 
1149  private static bool IsCredentialsValid(ConnectInfo connection)
1150  {
1151  return !string.IsNullOrEmpty(connection.User) && !string.IsNullOrEmpty(connection.IP);
1152  }
1153 
1154  private static bool IsValidIpAddress(string ip)
1155  {
1156  if (string.IsNullOrEmpty(ip))
1157  {
1158  return false;
1159  }
1160 
1161  if (ip.Contains("Local Machine"))
1162  {
1163  return true;
1164  }
1165 
1166  if (ip.Contains("0.0.0.0"))
1167  {
1168  return false;
1169  }
1170 
1171  var subAddresses = ip.Split('.');
1172  return subAddresses.Length > 3;
1173  }
1174 
1175  private static string CalcPackageFamilyName()
1176  {
1177  if (appPackageDirectories.Count == 0)
1178  {
1179  return string.Empty;
1180  }
1181 
1182  // Find the manifest
1183  string[] manifests = Directory.GetFiles(BuildDeployPrefs.AbsoluteBuildDirectory, "Package.appxmanifest", SearchOption.AllDirectories);
1184 
1185  if (manifests.Length == 0)
1186  {
1187  Debug.LogError("Unable to find manifest file for build (in path - " + BuildDeployPrefs.AbsoluteBuildDirectory + ")");
1188  return string.Empty;
1189  }
1190 
1191  string manifest = manifests[0];
1192 
1193  // Parse it
1194  using (var reader = new XmlTextReader(manifest))
1195  {
1196  while (reader.Read())
1197  {
1198  switch (reader.NodeType)
1199  {
1200  case XmlNodeType.Element:
1201  if (reader.Name.Equals("identity", StringComparison.OrdinalIgnoreCase))
1202  {
1203  while (reader.MoveToNextAttribute())
1204  {
1205  if (reader.Name.Equals("name", StringComparison.OrdinalIgnoreCase))
1206  {
1207  return reader.Value;
1208  }
1209  }
1210  }
1211 
1212  break;
1213  }
1214  }
1215  }
1216 
1217  Debug.LogError("Unable to find PackageFamilyName in manifest file (" + manifest + ")");
1218  return string.Empty;
1219  }
1220 
1221  #endregion
1222 
1223  #region Device Portal Commands
1224 
1225  private static void OpenDevicePortal(DevicePortalConnections targetDevices)
1226  {
1227  MachineName usbMachine = null;
1228 
1229  if (IsHoloLensConnectedUsb)
1230  {
1231  usbMachine = BuildDeployPortal.GetMachineName(targetDevices.Connections.FirstOrDefault(targetDevice => targetDevice.IP.Contains("Local Machine")));
1232  }
1233 
1234  for (int i = 0; i < targetDevices.Connections.Count; i++)
1235  {
1236  bool isLocalMachine = IsLocalConnection(targetDevices.Connections[i]);
1237 
1238  if (isLocalMachine && !IsHoloLensConnectedUsb)
1239  {
1240  continue;
1241  }
1242 
1243  if (IsHoloLensConnectedUsb)
1244  {
1245  if (isLocalMachine || usbMachine != null && usbMachine.ComputerName != targetDevices.Connections[i].MachineName)
1246  {
1247  BuildDeployPortal.OpenWebPortal(targetDevices.Connections[i]);
1248  }
1249  }
1250  else
1251  {
1252  if (!isLocalMachine)
1253  {
1254  BuildDeployPortal.OpenWebPortal(targetDevices.Connections[i]);
1255  }
1256  }
1257  }
1258  }
1259 
1260  private static void InstallOnTargetDevice(string buildPath, ConnectInfo targetDevice)
1261  {
1262  isAppRunning = false;
1263 
1264  string packageFamilyName = CalcPackageFamilyName();
1265 
1266  if (string.IsNullOrEmpty(packageFamilyName))
1267  {
1268  return;
1269  }
1270 
1271  if (IsLocalConnection(targetDevice) && !IsHoloLensConnectedUsb || buildPath.Contains("x64"))
1272  {
1273  FileInfo[] installerFiles = new DirectoryInfo(buildPath).GetFiles("*.ps1");
1274  if (installerFiles.Length == 1)
1275  {
1276  var pInfo = new ProcessStartInfo
1277  {
1278  FileName = "powershell.exe",
1279  CreateNoWindow = false,
1280  Arguments = string.Format("-executionpolicy bypass -File \"{0}\"", installerFiles[0].FullName)
1281  };
1282 
1283  var process = new Process { StartInfo = pInfo };
1284 
1285  process.Start();
1286  }
1287 
1288  return;
1289  }
1290 
1291  if (buildPath.Contains("x64"))
1292  {
1293  return;
1294  }
1295 
1296  // Get the appx path
1297  FileInfo[] files = new DirectoryInfo(buildPath).GetFiles("*.appx");
1298  files = files.Length == 0 ? new DirectoryInfo(buildPath).GetFiles("*.appxbundle") : files;
1299 
1300  if (files.Length == 0)
1301  {
1302  Debug.LogErrorFormat("No APPX found in folder build folder ({0})", buildPath);
1303  return;
1304  }
1305 
1306  BuildDeployPortal.IsAppInstalled(packageFamilyName, targetDevice);
1307 
1308  // Kick off the install
1309  BuildDeployPortal.InstallApp(files[0].FullName, targetDevice);
1310  }
1311 
1312  private static void InstallAppOnDevicesList(string buildPath, DevicePortalConnections targetList)
1313  {
1314  string packageFamilyName = CalcPackageFamilyName();
1315 
1316  if (string.IsNullOrEmpty(packageFamilyName))
1317  {
1318  return;
1319  }
1320 
1322  {
1323  UninstallAppOnDevicesList(targetList);
1324  }
1325 
1326  try
1327  {
1328  for (int i = 0; i < targetList.Connections.Count; i++)
1329  {
1330  EditorUtility.DisplayProgressBar("Installing on devices",
1331  string.Format("Installing on {0}", targetList.Connections[i].MachineName),
1332  i / (float)targetList.Connections.Count);
1333 
1334  InstallOnTargetDevice(buildPath, targetList.Connections[i]);
1335  }
1336  }
1337  catch (Exception e)
1338  {
1339  Debug.LogError(e.Message);
1340  }
1341 
1342  EditorUtility.ClearProgressBar();
1343  }
1344 
1345  private static void UninstallAppOnTargetDevice(string packageFamilyName, ConnectInfo currentConnection, bool showDialog = true)
1346  {
1347  isAppRunning = false;
1348 
1349  if (IsLocalConnection(currentConnection) && !IsHoloLensConnectedUsb)
1350  {
1351  var pInfo = new ProcessStartInfo
1352  {
1353  FileName = "powershell.exe",
1354  CreateNoWindow = true,
1355  Arguments = string.Format("-windowstyle hidden -nologo Get-AppxPackage *{0}* | Remove-AppxPackage", packageFamilyName)
1356  };
1357 
1358  var process = new Process { StartInfo = pInfo };
1359  process.Start();
1360  }
1361  else
1362  {
1363  if (BuildDeployPortal.IsAppInstalled(packageFamilyName, currentConnection))
1364  {
1365  BuildDeployPortal.UninstallApp(packageFamilyName, currentConnection, showDialog);
1366  }
1367  }
1368  }
1369 
1370  private static void UninstallAppOnDevicesList(DevicePortalConnections targetList)
1371  {
1372  string packageFamilyName = CalcPackageFamilyName();
1373 
1374  if (string.IsNullOrEmpty(packageFamilyName))
1375  {
1376  return;
1377  }
1378 
1379  try
1380  {
1381  for (int i = 0; i < targetList.Connections.Count; i++)
1382  {
1383  EditorUtility.DisplayProgressBar("Uninstalling on devices", string.Format("Uninstalling ({0})", targetList.Connections[i].IP), i / (float)targetList.Connections.Count);
1384  UninstallAppOnTargetDevice(packageFamilyName, targetList.Connections[i], false);
1385  }
1386  }
1387  catch (Exception e)
1388  {
1389  Debug.LogError(e.ToString());
1390  }
1391 
1392  EditorUtility.ClearProgressBar();
1393  }
1394 
1395  private static bool LaunchAppOnTargetDevice(ConnectInfo targetDevice, bool showDialog = true)
1396  {
1397  string packageFamilyName = CalcPackageFamilyName();
1398 
1399  if (string.IsNullOrEmpty(packageFamilyName))
1400  {
1401  return false;
1402  }
1403 
1404  if (IsLocalConnection(targetDevice) && !IsHoloLensConnectedUsb)
1405  {
1406  return false;
1407  }
1408 
1409  if (showDialog)
1410  {
1411  EditorUtility.DisplayProgressBar("Launching Application", string.Format("Launching {0} on {1}", packageFamilyName, targetDevice.MachineName), 0.25f);
1412  }
1413 
1414  bool success = !BuildDeployPortal.IsAppRunning(PlayerSettings.productName, targetDevice) &&
1415  BuildDeployPortal.LaunchApp(packageFamilyName, targetDevice, false);
1416 
1417  if (showDialog)
1418  {
1419  EditorUtility.ClearProgressBar();
1420  }
1421 
1422  return success;
1423  }
1424 
1425  private static void LaunchAppOnDeviceList(DevicePortalConnections targetDevices)
1426  {
1427  for (int i = 0; i < targetDevices.Connections.Count; i++)
1428  {
1429  EditorUtility.DisplayProgressBar("Launching App on devices",
1430  string.Format("Launching on {0}", targetDevices.Connections[i].IP),
1431  i / (float)targetDevices.Connections.Count);
1432 
1433  LaunchAppOnTargetDevice(targetDevices.Connections[i], false);
1434  }
1435  }
1436 
1437  private static bool KillAppOnTargetDevice(ConnectInfo targetDevice, bool showDialog = true)
1438  {
1439  string packageFamilyName = CalcPackageFamilyName();
1440 
1441  if (string.IsNullOrEmpty(packageFamilyName))
1442  {
1443  return false;
1444  }
1445 
1446  if (IsLocalConnection(targetDevice) && !IsHoloLensConnectedUsb)
1447  {
1448  return false;
1449  }
1450 
1451  if (showDialog)
1452  {
1453  EditorUtility.DisplayProgressBar("Stopping Application", string.Format("Stopping {0} on {1}", packageFamilyName, targetDevice.MachineName), 0.5f);
1454  }
1455 
1456  bool success = BuildDeployPortal.IsAppRunning(PlayerSettings.productName, targetDevice) &&
1457  BuildDeployPortal.KillApp(packageFamilyName, targetDevice, false);
1458 
1459  if (showDialog)
1460  {
1461  EditorUtility.ClearProgressBar();
1462  }
1463 
1464  return success;
1465  }
1466 
1467  private static void KillAppOnDeviceList(DevicePortalConnections targetDevices)
1468  {
1469  for (int i = 0; i < targetDevices.Connections.Count; i++)
1470  {
1471  EditorUtility.DisplayProgressBar("Stopping Application on devices",
1472  string.Format("Stopping on {0}", targetDevices.Connections[i].MachineName),
1473  i / (float)targetDevices.Connections.Count);
1474  KillAppOnTargetDevice(targetDevices.Connections[i], false);
1475  }
1476  }
1477 
1478  private static void OpenLogFileForTargetDevice(ConnectInfo targetDevice, string localLogPath)
1479  {
1480  string packageFamilyName = CalcPackageFamilyName();
1481 
1482  if (string.IsNullOrEmpty(packageFamilyName))
1483  {
1484  return;
1485  }
1486 
1487  if (IsLocalConnection(targetDevice) && File.Exists(localLogPath))
1488  {
1489  Process.Start(localLogPath);
1490  return;
1491  }
1492 
1493  if (!IsLocalConnection(targetDevice) || IsHoloLensConnectedUsb)
1494  {
1495  BuildDeployPortal.DeviceLogFile_View(packageFamilyName, targetDevice);
1496  return;
1497  }
1498 
1499  Debug.Log("No Log Found");
1500  }
1501 
1502  private static void OpenLogFilesOnDeviceList(DevicePortalConnections targetDevices, string localLogPath)
1503  {
1504  for (int i = 0; i < targetDevices.Connections.Count; i++)
1505  {
1506  OpenLogFileForTargetDevice(targetDevices.Connections[i], localLogPath);
1507  }
1508  }
1509 
1510  #endregion
1511  }
1512 }
static NetworkInfo GetNetworkInfo(ConnectInfo targetDevice)
Returns the NetworkInfo for the target device.
static bool BuildAppxFromSLN(string productName, string msBuildVersion, bool forceRebuildAppx, string buildConfig, string buildPlatform, string buildDirectory, bool incrementVersion, bool showDialog=true)
static USBDeviceInfo [] USBDevices
static MachineName GetMachineName(ConnectInfo targetDevice)
Gets the MachineName of the target device.
static bool BuildSLN()
Do a build configured for Mixed Reality Applications, returns the error from BuildPipeline.BuildPlayer
static bool IsAppInstalled(string packageFamilyName, string targetIp)
static bool InstallApp(string appFullPath, ConnectInfo targetDevice, bool waitForDone=true)
Installs the target application on the target device.
Function used to communicate with the device through the REST API
static bool UninstallApp(string packageFamilyName, string targetIp)
static bool KillApp(string packageFamilyName, string targetIp)
Build window - supports SLN creation, APPX from SLN, Deploy on device, and misc helper utilities asso...
static void OpenWebPortal(ConnectInfo targetDevice)
Opens the Device Portal for the target device.
static bool DeviceLogFile_View(string packageFamilyName, string targetIp)
static bool IsAppRunning(string appName, string targetDevice)
Contains utility functions for building for the device
static bool LaunchApp(string packageFamilyName, ConnectInfo targetDevice, bool showDialog=true)
Launches the target application on the target device.