From 97184c87af69a7e25145c865ec37871f37cb73e8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 1 Jan 2025 23:05:15 -0600 Subject: [PATCH 1/6] Add android emulation options to game cooker android tab. --- Source/Editor/Editor.Build.cs | 1 + Source/Editor/Windows/GameCookerWindow.cs | 223 ++++++++++++++++++++++ 2 files changed, 224 insertions(+) diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index e76cb5dfe..84dfa82dd 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -41,6 +41,7 @@ public class Editor : EditorModule options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter"); options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions"); options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile"); + options.ScriptingAPI.SystemReferences.Add("System.Diagnostics.Process"); // Enable optimizations for Editor, disable this for debugging the editor if (options.Configuration == TargetConfiguration.Development) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index cbe211eca..a44cae6a5 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -8,10 +8,12 @@ using FlaxEditor.Content.Settings; using FlaxEditor.CustomEditors; using FlaxEditor.GUI; using FlaxEditor.GUI.Tabs; +using FlaxEditor.GUI.Tree; using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; +using PlatformType = FlaxEngine.PlatformType; // ReSharper disable InconsistentNaming // ReSharper disable MemberCanBePrivate.Local @@ -343,6 +345,227 @@ namespace FlaxEditor.Windows _buildButton = layout.Button("Build").Button; _buildButton.Clicked += OnBuildClicked; + + // Add emulation options to android tab. + if (_platform == PlatformType.Android) + { + layout.Space(2); + var emulatorGroup = layout.Group("Emulator"); + var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); + if (string.IsNullOrEmpty(sdkPath)) + sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); + emulatorGroup.Label($"SDK path: {sdkPath}"); + + // AVD and starting emulator + var avdGroup = emulatorGroup.Group("AVD"); + avdGroup.Label("Note: Create AVDs using Android Studio."); + avdGroup.Panel.IsClosed = false; + var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button; + var avdListGroup = avdGroup.Group("AVD List"); + var avdListTree = new Tree(false) + { + Parent = avdListGroup.Panel, + }; + refreshAVDListButton.Clicked += () => + { + if (avdListTree.Children.Count > 0) + avdListTree.DisposeChildren(); + + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = "-list-avds", + RedirectStandardOutput = true, + }; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + var output = new string(process.StandardOutput.ReadToEnd()); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = "-list-avds", + HiddenWindow = false, + SaveOutput = true, + WaitForEnd = true, + }; + //processSettings.ShellExecute = true; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + var output = new string(processSettings.Output);*/ + if (output.Length == 0) + { + Debug.LogWarning("No AVDs detected."); + return; + } + var splitOutput = output.Split('\n'); + foreach (var line in splitOutput) + { + if (string.IsNullOrEmpty(line.Trim())) + continue; + var item = new TreeNode + { + Text = line.Trim(), + Parent = avdListTree, + }; + } + avdListGroup.Panel.IsClosed = false; + }; + + avdGroup.Label("Emulator AVD Commands:"); + var commandsTextBox = avdGroup.TextBox().TextBox; + commandsTextBox.IsMultiline = false; + commandsTextBox.Text = "-no-snapshot-load -no-boot-anim"; // TODO: save user changes + + var startEmulatorButton = avdGroup.Button("Start AVD Emulator").Button; + startEmulatorButton.TooltipText = "Starts selected AVD from list."; + startEmulatorButton.Clicked += () => + { + if (avdListTree.Selection.Count == 0) + return; + + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = $"-avd {avdListTree.Selection[0].Text} {commandsTextBox.Text}", + HiddenWindow = true, + SaveOutput = false, + WaitForEnd = false, + }; + processSettings.ShellExecute = true; + FlaxEngine.Platform.CreateProcess(ref processSettings); + }; + + emulatorGroup.Space(2); + + // Device + var installGroup = emulatorGroup.Group("Install"); + installGroup.Panel.IsClosed = false; + installGroup.Label("Note: used to install to AVD or physical devices."); + var refreshDeviceListButton = installGroup.Button("Refresh device list").Button; + var deviceListGroup = installGroup.Group("List of devices"); + var deviceListTree = new Tree(false) + { + Parent = deviceListGroup.Panel, + }; + refreshDeviceListButton.Clicked += () => + { + if (deviceListTree.Children.Count > 0) + deviceListTree.DisposeChildren(); + + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "devices -l", + RedirectStandardOutput = true, + }; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + var output = new string(process.StandardOutput.ReadToEnd()); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "devices -l", + HiddenWindow = false, + //SaveOutput = true, + WaitForEnd = true, + }; + processSettings.SaveOutput = true; + processSettings.ShellExecute = false; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + var output = new string(processSettings.Output); + */ + if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal)) + { + var splitLines = output.Split('\n'); + foreach (var line in splitLines) + { + if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim())) + continue; + + var tab = line.Split("device "); + if (tab.Length < 2) + continue; + var item = new TreeNode + { + Text = $"{tab[0].Trim()} - {tab[1].Trim()}", + Tag = tab[0].Trim(), + Parent = deviceListTree, + }; + } + } + + deviceListGroup.Panel.IsClosed = false; + }; + + var autoStart = installGroup.Checkbox("Try to auto start activity on device."); + var installButton = installGroup.Button("Install APK to Device").Button; + installButton.TooltipText = "Installs APK from the output folder to the selected device."; + installButton.Clicked += () => + { + if (deviceListTree.Selection.Count == 0) + return; + + // Get built APK at output path + string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(proxy.PerPlatformOptions[_platform].Output)); + if (!Directory.Exists(output)) + { + FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist."); + return; + } + + var apkFiles = Directory.GetFiles(output, "*.apk"); + if (apkFiles.Length == 0) + { + FlaxEditor.Editor.LogWarning("Can not copy APK because no .apk files were found in output folder."); + return; + } + + string apkFilesString = string.Empty; + for (int i = 0; i < apkFiles.Length; i++) + { + var file = apkFiles[i]; + if (i == 0) + { + apkFilesString = $"\"{file}\""; + continue; + } + apkFilesString += $" \"{file}\""; + } + + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"-s {deviceListTree.Selection[0].Tag} {(apkFiles.Length > 1 ? "install-multiple" : "install")} {apkFilesString}", + LogOutput = true, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + if (autoStart.CheckBox.Checked) + { + var gameSettings = GameSettings.Load(); + var productName = gameSettings.ProductName.Replace(" ", "").ToLower(); + var companyName = gameSettings.CompanyName.Replace(" ", "").ToLower(); + CreateProcessSettings processSettings1 = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"shell am start -n com.{companyName}.{productName}/com.flaxengine.GameActivity", + LogOutput = true, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings1); + } + }; + } } else { From fa17c49eb1713d7966318b1be53e41d76afe3925 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 7 Jan 2025 21:11:37 -0600 Subject: [PATCH 2/6] Add starting adb log collecting button. --- Source/Editor/Windows/GameCookerWindow.cs | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index a44cae6a5..81b120937 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using FlaxEditor.Content.Settings; @@ -13,6 +14,7 @@ using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; +using Debug = FlaxEngine.Debug; using PlatformType = FlaxEngine.PlatformType; // ReSharper disable InconsistentNaming @@ -376,6 +378,7 @@ namespace FlaxEditor.Windows FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), Arguments = "-list-avds", RedirectStandardOutput = true, + CreateNoWindow = true, }; var process = new System.Diagnostics.Process @@ -462,6 +465,7 @@ namespace FlaxEditor.Windows FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), Arguments = "devices -l", RedirectStandardOutput = true, + CreateNoWindow = true, }; var process = new System.Diagnostics.Process @@ -565,6 +569,35 @@ namespace FlaxEditor.Windows FlaxEngine.Platform.CreateProcess(ref processSettings1); } }; + + var adbLogButton = emulatorGroup.Button("Start adb log collecting").Button; + adbLogButton.TooltipText = "In debug and development builds the engine and game logs can be output directly to the adb."; + adbLogButton.Clicked += () => + { + + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "logcat Flax:I *:S", + }; + processStartInfo.CreateNoWindow = false; + processStartInfo.WindowStyle = ProcessWindowStyle.Normal; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"logcat Flax:I *:S", + //LogOutput = true, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings); + */ + }; } } else From 6fbb5548b90d4a512a8fe18115580a5d8903b6dd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 7 Jan 2025 21:15:35 -0600 Subject: [PATCH 3/6] Small code clean up. --- Source/Editor/Windows/GameCookerWindow.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 81b120937..694e86e5c 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -574,14 +574,13 @@ namespace FlaxEditor.Windows adbLogButton.TooltipText = "In debug and development builds the engine and game logs can be output directly to the adb."; adbLogButton.Clicked += () => { - var processStartInfo = new System.Diagnostics.ProcessStartInfo { FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), Arguments = "logcat Flax:I *:S", + CreateNoWindow = false, + WindowStyle = ProcessWindowStyle.Normal, }; - processStartInfo.CreateNoWindow = false; - processStartInfo.WindowStyle = ProcessWindowStyle.Normal; var process = new System.Diagnostics.Process { @@ -593,7 +592,7 @@ namespace FlaxEditor.Windows { FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), Arguments = $"logcat Flax:I *:S", - //LogOutput = true, + WaitForEnd = false, }; FlaxEngine.Platform.CreateProcess(ref processSettings); */ From 21f1a46c05992cd2b49076cdcff980fe32b6882b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 7 Jan 2025 21:29:27 -0600 Subject: [PATCH 4/6] Small wording changes --- Source/Editor/Windows/GameCookerWindow.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 694e86e5c..8bd3523de 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -351,15 +351,15 @@ namespace FlaxEditor.Windows // Add emulation options to android tab. if (_platform == PlatformType.Android) { - layout.Space(2); - var emulatorGroup = layout.Group("Emulator"); + layout.Space(5); + var emulatorGroup = layout.Group("Tools"); var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); if (string.IsNullOrEmpty(sdkPath)) sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); emulatorGroup.Label($"SDK path: {sdkPath}"); // AVD and starting emulator - var avdGroup = emulatorGroup.Group("AVD"); + var avdGroup = emulatorGroup.Group("AVD Emulator"); avdGroup.Label("Note: Create AVDs using Android Studio."); avdGroup.Panel.IsClosed = false; var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button; @@ -448,7 +448,7 @@ namespace FlaxEditor.Windows // Device var installGroup = emulatorGroup.Group("Install"); installGroup.Panel.IsClosed = false; - installGroup.Label("Note: used to install to AVD or physical devices."); + installGroup.Label("Note: Used to install to AVD or physical devices."); var refreshDeviceListButton = installGroup.Button("Refresh device list").Button; var deviceListGroup = installGroup.Group("List of devices"); var deviceListTree = new Tree(false) From e1dbaebe8ec0933b2d3969e7b0527992f11bf28b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 7 Jan 2025 21:40:05 -0600 Subject: [PATCH 5/6] Add instructions for list populating. --- Source/Editor/Windows/GameCookerWindow.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 8bd3523de..0088cdf86 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -364,6 +364,8 @@ namespace FlaxEditor.Windows avdGroup.Panel.IsClosed = false; var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button; var avdListGroup = avdGroup.Group("AVD List"); + avdListGroup.Panel.IsClosed = false; + var noAvdLabel = avdListGroup.Label("No AVDs detected. Click Refresh.", TextAlignment.Center).Label; var avdListTree = new Tree(false) { Parent = avdListGroup.Panel, @@ -402,9 +404,11 @@ namespace FlaxEditor.Windows var output = new string(processSettings.Output);*/ if (output.Length == 0) { - Debug.LogWarning("No AVDs detected."); + noAvdLabel.Visible = true; + FlaxEditor.Editor.LogWarning("No AVDs detected."); return; } + noAvdLabel.Visible = false; var splitOutput = output.Split('\n'); foreach (var line in splitOutput) { @@ -451,6 +455,8 @@ namespace FlaxEditor.Windows installGroup.Label("Note: Used to install to AVD or physical devices."); var refreshDeviceListButton = installGroup.Button("Refresh device list").Button; var deviceListGroup = installGroup.Group("List of devices"); + deviceListGroup.Panel.IsClosed = false; + var noDevicesLabel = deviceListGroup.Label("No devices found. Click Refresh.", TextAlignment.Center).Label; var deviceListTree = new Tree(false) { Parent = deviceListGroup.Panel, @@ -489,8 +495,10 @@ namespace FlaxEditor.Windows var output = new string(processSettings.Output); */ + if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal)) { + noDevicesLabel.Visible = false; var splitLines = output.Split('\n'); foreach (var line in splitLines) { @@ -508,6 +516,10 @@ namespace FlaxEditor.Windows }; } } + else + { + noDevicesLabel.Visible = true; + } deviceListGroup.Panel.IsClosed = false; }; From a5e8f7d405ef6d70b163e8de93bf87d69c0317a0 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 3 Mar 2025 21:42:54 -0600 Subject: [PATCH 6/6] Move to custom tool method within platform --- Source/Editor/Windows/GameCookerWindow.cs | 536 +++++++++++----------- 1 file changed, 273 insertions(+), 263 deletions(-) diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 0088cdf86..261090d4a 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -196,6 +196,12 @@ namespace FlaxEditor.Windows var label = layout.Label(text, TextAlignment.Center); label.Label.AutoHeight = true; } + + /// + /// Used to add platform specific tools if available. + /// + /// The layout to start the tools at. + public virtual void OnCustomToolsLayout(LayoutElementsContainer layout) { } public virtual void Build() { @@ -237,6 +243,272 @@ namespace FlaxEditor.Windows class Android : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.AndroidARM64; + + /// + public override void OnCustomToolsLayout(LayoutElementsContainer layout) + { + base.OnCustomToolsLayout(layout); + + // Add emulation options to android tab. + layout.Space(5); + var emulatorGroup = layout.Group("Tools"); + var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); + if (string.IsNullOrEmpty(sdkPath)) + sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); + emulatorGroup.Label($"SDK path: {sdkPath}"); + + // AVD and starting emulator + var avdGroup = emulatorGroup.Group("AVD Emulator"); + avdGroup.Label("Note: Create AVDs using Android Studio."); + avdGroup.Panel.IsClosed = false; + var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button; + var avdListGroup = avdGroup.Group("AVD List"); + avdListGroup.Panel.IsClosed = false; + var noAvdLabel = avdListGroup.Label("No AVDs detected. Click Refresh.", TextAlignment.Center).Label; + var avdListTree = new Tree(false) + { + Parent = avdListGroup.Panel, + }; + refreshAVDListButton.Clicked += () => + { + if (avdListTree.Children.Count > 0) + avdListTree.DisposeChildren(); + + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = "-list-avds", + RedirectStandardOutput = true, + CreateNoWindow = true, + }; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + var output = new string(process.StandardOutput.ReadToEnd()); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = "-list-avds", + HiddenWindow = false, + SaveOutput = true, + WaitForEnd = true, + }; + //processSettings.ShellExecute = true; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + var output = new string(processSettings.Output);*/ + if (output.Length == 0) + { + noAvdLabel.Visible = true; + FlaxEditor.Editor.LogWarning("No AVDs detected."); + return; + } + noAvdLabel.Visible = false; + var splitOutput = output.Split('\n'); + foreach (var line in splitOutput) + { + if (string.IsNullOrEmpty(line.Trim())) + continue; + var item = new TreeNode + { + Text = line.Trim(), + Parent = avdListTree, + }; + } + avdListGroup.Panel.IsClosed = false; + }; + + avdGroup.Label("Emulator AVD Commands:"); + var commandsTextBox = avdGroup.TextBox().TextBox; + commandsTextBox.IsMultiline = false; + commandsTextBox.Text = "-no-snapshot-load -no-boot-anim"; // TODO: save user changes + + var startEmulatorButton = avdGroup.Button("Start AVD Emulator").Button; + startEmulatorButton.TooltipText = "Starts selected AVD from list."; + startEmulatorButton.Clicked += () => + { + if (avdListTree.Selection.Count == 0) + return; + + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), + Arguments = $"-avd {avdListTree.Selection[0].Text} {commandsTextBox.Text}", + HiddenWindow = true, + SaveOutput = false, + WaitForEnd = false, + }; + processSettings.ShellExecute = true; + FlaxEngine.Platform.CreateProcess(ref processSettings); + }; + + emulatorGroup.Space(2); + + // Device + var installGroup = emulatorGroup.Group("Install"); + installGroup.Panel.IsClosed = false; + installGroup.Label("Note: Used to install to AVD or physical devices."); + var refreshDeviceListButton = installGroup.Button("Refresh device list").Button; + var deviceListGroup = installGroup.Group("List of devices"); + deviceListGroup.Panel.IsClosed = false; + var noDevicesLabel = deviceListGroup.Label("No devices found. Click Refresh.", TextAlignment.Center).Label; + var deviceListTree = new Tree(false) + { + Parent = deviceListGroup.Panel, + }; + refreshDeviceListButton.Clicked += () => + { + if (deviceListTree.Children.Count > 0) + deviceListTree.DisposeChildren(); + + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "devices -l", + RedirectStandardOutput = true, + CreateNoWindow = true, + }; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + var output = new string(process.StandardOutput.ReadToEnd()); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "devices -l", + HiddenWindow = false, + //SaveOutput = true, + WaitForEnd = true, + }; + processSettings.SaveOutput = true; + processSettings.ShellExecute = false; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + var output = new string(processSettings.Output); + */ + + if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal)) + { + noDevicesLabel.Visible = false; + var splitLines = output.Split('\n'); + foreach (var line in splitLines) + { + if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim())) + continue; + + var tab = line.Split("device "); + if (tab.Length < 2) + continue; + var item = new TreeNode + { + Text = $"{tab[0].Trim()} - {tab[1].Trim()}", + Tag = tab[0].Trim(), + Parent = deviceListTree, + }; + } + } + else + { + noDevicesLabel.Visible = true; + } + + deviceListGroup.Panel.IsClosed = false; + }; + + var autoStart = installGroup.Checkbox("Try to auto start activity on device."); + var installButton = installGroup.Button("Install APK to Device").Button; + installButton.TooltipText = "Installs APK from the output folder to the selected device."; + installButton.Clicked += () => + { + if (deviceListTree.Selection.Count == 0) + return; + + // Get built APK at output path + string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output)); + if (!Directory.Exists(output)) + { + FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist."); + return; + } + + var apkFiles = Directory.GetFiles(output, "*.apk"); + if (apkFiles.Length == 0) + { + FlaxEditor.Editor.LogWarning("Can not copy APK because no .apk files were found in output folder."); + return; + } + + string apkFilesString = string.Empty; + for (int i = 0; i < apkFiles.Length; i++) + { + var file = apkFiles[i]; + if (i == 0) + { + apkFilesString = $"\"{file}\""; + continue; + } + apkFilesString += $" \"{file}\""; + } + + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"-s {deviceListTree.Selection[0].Tag} {(apkFiles.Length > 1 ? "install-multiple" : "install")} {apkFilesString}", + LogOutput = true, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings); + + if (autoStart.CheckBox.Checked) + { + var gameSettings = GameSettings.Load(); + var productName = gameSettings.ProductName.Replace(" ", "").ToLower(); + var companyName = gameSettings.CompanyName.Replace(" ", "").ToLower(); + CreateProcessSettings processSettings1 = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"shell am start -n com.{companyName}.{productName}/com.flaxengine.GameActivity", + LogOutput = true, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings1); + } + }; + + var adbLogButton = emulatorGroup.Button("Start adb log collecting").Button; + adbLogButton.TooltipText = "In debug and development builds the engine and game logs can be output directly to the adb."; + adbLogButton.Clicked += () => + { + var processStartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = "logcat Flax:I *:S", + CreateNoWindow = false, + WindowStyle = ProcessWindowStyle.Normal, + }; + + var process = new System.Diagnostics.Process + { + StartInfo = processStartInfo + }; + process.Start(); + /* + CreateProcessSettings processSettings = new CreateProcessSettings + { + FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), + Arguments = $"logcat Flax:I *:S", + WaitForEnd = false, + }; + FlaxEngine.Platform.CreateProcess(ref processSettings); + */ + }; + } } class Switch : Platform @@ -347,269 +619,7 @@ namespace FlaxEditor.Windows _buildButton = layout.Button("Build").Button; _buildButton.Clicked += OnBuildClicked; - - // Add emulation options to android tab. - if (_platform == PlatformType.Android) - { - layout.Space(5); - var emulatorGroup = layout.Group("Tools"); - var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); - if (string.IsNullOrEmpty(sdkPath)) - sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); - emulatorGroup.Label($"SDK path: {sdkPath}"); - - // AVD and starting emulator - var avdGroup = emulatorGroup.Group("AVD Emulator"); - avdGroup.Label("Note: Create AVDs using Android Studio."); - avdGroup.Panel.IsClosed = false; - var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button; - var avdListGroup = avdGroup.Group("AVD List"); - avdListGroup.Panel.IsClosed = false; - var noAvdLabel = avdListGroup.Label("No AVDs detected. Click Refresh.", TextAlignment.Center).Label; - var avdListTree = new Tree(false) - { - Parent = avdListGroup.Panel, - }; - refreshAVDListButton.Clicked += () => - { - if (avdListTree.Children.Count > 0) - avdListTree.DisposeChildren(); - - var processStartInfo = new System.Diagnostics.ProcessStartInfo - { - FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), - Arguments = "-list-avds", - RedirectStandardOutput = true, - CreateNoWindow = true, - }; - - var process = new System.Diagnostics.Process - { - StartInfo = processStartInfo - }; - process.Start(); - var output = new string(process.StandardOutput.ReadToEnd()); - /* - CreateProcessSettings processSettings = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), - Arguments = "-list-avds", - HiddenWindow = false, - SaveOutput = true, - WaitForEnd = true, - }; - //processSettings.ShellExecute = true; - FlaxEngine.Platform.CreateProcess(ref processSettings); - - var output = new string(processSettings.Output);*/ - if (output.Length == 0) - { - noAvdLabel.Visible = true; - FlaxEditor.Editor.LogWarning("No AVDs detected."); - return; - } - noAvdLabel.Visible = false; - var splitOutput = output.Split('\n'); - foreach (var line in splitOutput) - { - if (string.IsNullOrEmpty(line.Trim())) - continue; - var item = new TreeNode - { - Text = line.Trim(), - Parent = avdListTree, - }; - } - avdListGroup.Panel.IsClosed = false; - }; - - avdGroup.Label("Emulator AVD Commands:"); - var commandsTextBox = avdGroup.TextBox().TextBox; - commandsTextBox.IsMultiline = false; - commandsTextBox.Text = "-no-snapshot-load -no-boot-anim"; // TODO: save user changes - - var startEmulatorButton = avdGroup.Button("Start AVD Emulator").Button; - startEmulatorButton.TooltipText = "Starts selected AVD from list."; - startEmulatorButton.Clicked += () => - { - if (avdListTree.Selection.Count == 0) - return; - - CreateProcessSettings processSettings = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"), - Arguments = $"-avd {avdListTree.Selection[0].Text} {commandsTextBox.Text}", - HiddenWindow = true, - SaveOutput = false, - WaitForEnd = false, - }; - processSettings.ShellExecute = true; - FlaxEngine.Platform.CreateProcess(ref processSettings); - }; - - emulatorGroup.Space(2); - - // Device - var installGroup = emulatorGroup.Group("Install"); - installGroup.Panel.IsClosed = false; - installGroup.Label("Note: Used to install to AVD or physical devices."); - var refreshDeviceListButton = installGroup.Button("Refresh device list").Button; - var deviceListGroup = installGroup.Group("List of devices"); - deviceListGroup.Panel.IsClosed = false; - var noDevicesLabel = deviceListGroup.Label("No devices found. Click Refresh.", TextAlignment.Center).Label; - var deviceListTree = new Tree(false) - { - Parent = deviceListGroup.Panel, - }; - refreshDeviceListButton.Clicked += () => - { - if (deviceListTree.Children.Count > 0) - deviceListTree.DisposeChildren(); - - var processStartInfo = new System.Diagnostics.ProcessStartInfo - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = "devices -l", - RedirectStandardOutput = true, - CreateNoWindow = true, - }; - - var process = new System.Diagnostics.Process - { - StartInfo = processStartInfo - }; - process.Start(); - var output = new string(process.StandardOutput.ReadToEnd()); - /* - CreateProcessSettings processSettings = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = "devices -l", - HiddenWindow = false, - //SaveOutput = true, - WaitForEnd = true, - }; - processSettings.SaveOutput = true; - processSettings.ShellExecute = false; - FlaxEngine.Platform.CreateProcess(ref processSettings); - - var output = new string(processSettings.Output); - */ - - if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal)) - { - noDevicesLabel.Visible = false; - var splitLines = output.Split('\n'); - foreach (var line in splitLines) - { - if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim())) - continue; - - var tab = line.Split("device "); - if (tab.Length < 2) - continue; - var item = new TreeNode - { - Text = $"{tab[0].Trim()} - {tab[1].Trim()}", - Tag = tab[0].Trim(), - Parent = deviceListTree, - }; - } - } - else - { - noDevicesLabel.Visible = true; - } - - deviceListGroup.Panel.IsClosed = false; - }; - - var autoStart = installGroup.Checkbox("Try to auto start activity on device."); - var installButton = installGroup.Button("Install APK to Device").Button; - installButton.TooltipText = "Installs APK from the output folder to the selected device."; - installButton.Clicked += () => - { - if (deviceListTree.Selection.Count == 0) - return; - - // Get built APK at output path - string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(proxy.PerPlatformOptions[_platform].Output)); - if (!Directory.Exists(output)) - { - FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist."); - return; - } - - var apkFiles = Directory.GetFiles(output, "*.apk"); - if (apkFiles.Length == 0) - { - FlaxEditor.Editor.LogWarning("Can not copy APK because no .apk files were found in output folder."); - return; - } - - string apkFilesString = string.Empty; - for (int i = 0; i < apkFiles.Length; i++) - { - var file = apkFiles[i]; - if (i == 0) - { - apkFilesString = $"\"{file}\""; - continue; - } - apkFilesString += $" \"{file}\""; - } - - CreateProcessSettings processSettings = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = $"-s {deviceListTree.Selection[0].Tag} {(apkFiles.Length > 1 ? "install-multiple" : "install")} {apkFilesString}", - LogOutput = true, - }; - FlaxEngine.Platform.CreateProcess(ref processSettings); - - if (autoStart.CheckBox.Checked) - { - var gameSettings = GameSettings.Load(); - var productName = gameSettings.ProductName.Replace(" ", "").ToLower(); - var companyName = gameSettings.CompanyName.Replace(" ", "").ToLower(); - CreateProcessSettings processSettings1 = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = $"shell am start -n com.{companyName}.{productName}/com.flaxengine.GameActivity", - LogOutput = true, - }; - FlaxEngine.Platform.CreateProcess(ref processSettings1); - } - }; - - var adbLogButton = emulatorGroup.Button("Start adb log collecting").Button; - adbLogButton.TooltipText = "In debug and development builds the engine and game logs can be output directly to the adb."; - adbLogButton.Clicked += () => - { - var processStartInfo = new System.Diagnostics.ProcessStartInfo - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = "logcat Flax:I *:S", - CreateNoWindow = false, - WindowStyle = ProcessWindowStyle.Normal, - }; - - var process = new System.Diagnostics.Process - { - StartInfo = processStartInfo - }; - process.Start(); - /* - CreateProcessSettings processSettings = new CreateProcessSettings - { - FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"), - Arguments = $"logcat Flax:I *:S", - WaitForEnd = false, - }; - FlaxEngine.Platform.CreateProcess(ref processSettings); - */ - }; - } + platformObj.OnCustomToolsLayout(layout); } else {