Add Commands Window for running ADB and shell commands#213
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new “Commands” EditorWindow to the Android Logcat package, enabling users to build/manage and execute ADB/shell commands directly from the Unity Editor, with persistence via AndroidLogcatUserSettings.
Changes:
- Added Commands window UI (favorites + general lists, edit mode operations, import/export, inline output panel).
- Added supporting UI/components: placeholder resolution dialog, command search window, built-in ADB command catalog, and a command output pop-out window.
- Integrated the window into the existing Logcat console Tools menu and persisted data in user settings.
Reviewed changes
Copilot reviewed 10 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| com.unity.mobile.android-logcat/Editor/AndroidLogcatUserSettings.cs | Adds persisted CommandsSettingsData for favorites/general command lists. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatPlaceholderDialog.cs | New dialog to resolve <placeholder> tokens before execution. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatPlaceholderDialog.cs.meta | Meta for new placeholder dialog asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatContextMenu.cs | Adds ToolsContextMenu.Commands enum entry. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatConsoleWindow.cs | Hooks the new Commands window into the Tools menu. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs | Main Commands window implementation (lists, execution, import/export, output). |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs.meta | Meta for new commands window asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandSearchWindow.cs | Catalog + local search window for adding/running commands. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandSearchWindow.cs.meta | Meta for new search window asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandOutputWindow.cs | Dedicated output window (copy-all, auto-scroll). |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandOutputWindow.cs.meta | Meta for new output window asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandEntry.cs | Defines command entry model + import/export DTOs/helpers. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandEntry.cs.meta | Meta for new command entry asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatAddCommandDialog.cs | New dialog for adding/editing multi-line commands. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatAddCommandDialog.cs.meta | Meta for new add/edit dialog asset. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatAdbCommandCatalog.cs | New built-in catalog of predefined ADB commands. |
| com.unity.mobile.android-logcat/Editor/AndroidLogcatAdbCommandCatalog.cs.meta | Meta for new catalog asset. |
Files not reviewed (7)
- com.unity.mobile.android-logcat/Editor/AndroidLogcatAdbCommandCatalog.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatAddCommandDialog.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandEntry.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandOutputWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandSearchWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatPlaceholderDialog.cs.meta: Language not supported
Comments suppressed due to low confidence (8)
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:196
- Row click selection is checking GUILayoutUtility.GetLastRect() immediately after BeginHorizontal, before any controls in this row have been laid out. This rect will refer to the previously drawn control/row and can make selection unreliable. Capture the row rect explicitly (e.g., using GUILayoutUtility.GetRect / a selectable label/button spanning the row) and test Event.current.mousePosition against that rect instead.
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox, GUILayout.Height(kRowHeight));
if (isSelected)
GUI.backgroundColor = Color.white;
// Click to select
if (Event.current.type == EventType.MouseDown && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
{
selectedIndex = index;
Repaint();
}
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:307
- The splitter drag logic uses GUIUtility.hotControl != 0 as the only guard. hotControl can be non-zero for other controls (text fields, scroll views, etc.), so dragging elsewhere in the window can inadvertently resize the output area. Track a dedicated splitter control ID (store it on MouseDown) or a boolean like m_DraggingSplitter and only resize while that specific control is active.
void DoOutputSplitter(float y)
{
var splitterRect = new Rect(0, y, position.width, 4);
EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeVertical);
if (Event.current.type == EventType.MouseDown && splitterRect.Contains(Event.current.mousePosition))
GUIUtility.hotControl = GUIUtility.GetControlID(FocusType.Passive);
if (GUIUtility.hotControl != 0 && Event.current.type == EventType.MouseDrag)
{
m_OutputHeight = Mathf.Clamp(position.height - Event.current.mousePosition.y, 50, position.height - 150);
Repaint();
}
if (Event.current.type == EventType.MouseUp)
GUIUtility.hotControl = 0;
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:331
- A new GUIStyle is allocated for every output line on every OnGUI repaint. With up to 3000 lines this will generate a lot of GC pressure and can make the window sluggish. Cache the style once (e.g., a field initialized lazily) and only update the color via GUIStyleState or use GUI.contentColor/EditorGUI.DrawRect approaches that avoid per-line allocations.
for (int i = 0; i < m_OutputLines.Count; i++)
{
var line = m_OutputLines[i];
var style = new GUIStyle(EditorStyles.label)
{
wordWrap = true,
richText = false
};
style.normal.textColor = line.color;
EditorGUILayout.LabelField(line.text, style);
}
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:212
- A new GUIStyle is allocated for each command row (cmdStyle) on every repaint. With large command lists this can create avoidable GC churn. Consider caching this style (or using a shared static style) instead of constructing it in the row loop.
var cmdStyle = new GUIStyle(EditorStyles.label);
cmdStyle.normal.textColor = kCommandColor;
EditorGUILayout.LabelField(cmdDisplay, cmdStyle);
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:396
- Command execution is fully synchronous on the UI thread (ADB.Run / Shell.RunProcess inside the OnGUI button path). Commands like
adb logcat, largedumpsys, network hangs, etc. can freeze the Unity Editor until completion. Consider running commands asynchronously (background process with incremental output + cancellation) or at least warning/guarding against known long-running commands.
void ExecuteCommand(string resolvedCommand)
{
var lines = resolvedCommand.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
m_Running = true;
Repaint();
var device = m_DeviceSelection != null ? m_DeviceSelection.SelectedDevice : null;
var deviceId = device != null ? device.Id : null;
foreach (var rawLine in lines)
{
var cmd = rawLine.Trim();
if (string.IsNullOrEmpty(cmd))
continue;
AppendOutput($"> {cmd}", kAccentColor);
try
{
string result;
if (cmd.StartsWith("adb ", StringComparison.OrdinalIgnoreCase))
{
var adbArgs = cmd.Substring(4);
// Inject device serial if we have a selected device and the command doesn't already specify -s
if (!string.IsNullOrEmpty(deviceId) && !adbArgs.TrimStart().StartsWith("-s "))
adbArgs = $"-s {deviceId} {adbArgs}";
result = m_Runtime.Tools.ADB.Run(new[] { adbArgs }, "");
}
else
{
var parts = cmd.Split(new[] { ' ' }, 2);
var fileName = parts[0];
var arguments = parts.Length > 1 ? parts[1] : "";
var shellResult = Shell.RunProcess(fileName, arguments);
result = shellResult.GetStandardOut();
var err = shellResult.GetStandardErr();
if (!string.IsNullOrEmpty(err))
result = string.IsNullOrEmpty(result) ? err : result + "\n" + err;
}
AppendOutput(result, GUI.skin.label.normal.textColor);
}
catch (Exception ex)
{
AppendOutput($"[Error] {ex.Message}", kErrorColor);
}
}
m_Running = false;
Repaint();
}
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:380
- Non-ADB command parsing splits on the first space to get the executable name (parts[0]). This breaks common cases like quoted executables/paths with spaces (e.g. "C:\Program Files...\tool.exe" args). Consider using a proper command-line parser that respects quotes/escaping (or restrict the feature to adb/shell explicitly and validate inputs).
var parts = cmd.Split(new[] { ' ' }, 2);
var fileName = parts[0];
var arguments = parts.Length > 1 ? parts[1] : "";
var shellResult = Shell.RunProcess(fileName, arguments);
result = shellResult.GetStandardOut();
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:405
- Output lines are split only on '\n' when appending, so Windows CRLF output will leave a trailing '\r' character in each line (rendering as a stray character in the UI). Consider trimming '\r' per line or splitting on "\r\n"/"\n" robustly.
foreach (var line in text.Split('\n'))
m_OutputLines.Add(new OutputLine { text = line, color = color });
com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs:497
- Imported JSON commands are accepted without validation/sanitization. If an entry has null/empty name or command (or the array contains null elements), later code paths like entry.command.IndexOfAny(...) and MatchesSearch(...) will throw NullReferenceException and break the window. Consider filtering out null entries and validating required fields (name/command) during import (and/or when loading from user settings).
AndroidLogcatCommandExportData data;
try
{
var json = File.ReadAllText(path);
data = JsonUtility.FromJson<AndroidLogcatCommandExportData>(json);
}
catch (Exception ex)
{
EditorUtility.DisplayDialog("Import Failed",
$"Could not parse the selected file:\n{ex.Message}", "OK");
return;
}
if ((data.favorites == null || data.favorites.Length == 0) &&
(data.general == null || data.general.Length == 0))
{
EditorUtility.DisplayDialog("Import Failed",
"The file contains no commands.", "OK");
return;
}
if (!EditorUtility.DisplayDialog("Import Commands",
"This will replace all your current commands. Continue?", "Import", "Cancel"))
return;
m_Favorites.Clear();
if (data.favorites != null)
m_Favorites.AddRange(data.favorites);
m_GeneralCommands.Clear();
if (data.general != null)
m_GeneralCommands.AddRange(data.general);
SaveCommands();
Debug.Log($"[Android Logcat] Commands imported from {path}");
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var resolved = m_OriginalCommand; | ||
| foreach (var placeholder in m_Placeholders) | ||
| { | ||
| resolved = resolved.Replace($"<{placeholder.token}>", placeholder.value); | ||
| } |
| if (GUILayout.Button("Add Command", EditorStyles.toolbarButton)) | ||
| OnAddCommand(); | ||
|
|
||
| var editLabel = m_EditMode ? "Edit Mode: ON" : "Edit Mode: OFF"; | ||
| if (GUILayout.Button(editLabel, EditorStyles.toolbarButton)) | ||
| m_EditMode = !m_EditMode; | ||
|
|
||
| if (GUILayout.Button("Import", EditorStyles.toolbarButton)) | ||
| OnImportCommands(); | ||
|
|
||
| if (GUILayout.Button("Export", EditorStyles.toolbarButton)) | ||
| OnExportCommands(); | ||
|
|
||
| if (GUILayout.Button("Search Catalog", EditorStyles.toolbarButton)) | ||
| OpenSearchWindow(); | ||
|
|
||
| GUILayout.FlexibleSpace(); | ||
|
|
||
| if (m_Running) | ||
| GUILayout.Label("Running...", EditorStyles.miniLabel); | ||
|
|
||
| EditorGUILayout.EndHorizontal(); |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Implemented in commit 1c361bb: added an explicit Open Output button in the Commands top toolbar that calls AndroidLogcatCommandOutputWindow.Open(m_OutputLines), so the pop-out output window is directly reachable.
Screenshot: Commands window toolbar now includes the new Open Output action next to Search Catalog.
| for (int i = 0; i < m_Lines.Count; i++) | ||
| { | ||
| var line = m_Lines[i]; | ||
| var style = new GUIStyle(EditorStyles.label) | ||
| { | ||
| wordWrap = true, | ||
| richText = false | ||
| }; | ||
| style.normal.textColor = line.color; | ||
| EditorGUILayout.LabelField(line.text, style); | ||
| } |
| static readonly Regex s_PlaceholderRegex = new Regex(@"<([^>]+)>", RegexOptions.Compiled); | ||
|
|
||
| string m_OriginalCommand; | ||
| Action<string> m_OnConfirm; | ||
| List<PlaceholderInfo> m_Placeholders = new List<PlaceholderInfo>(); | ||
|
|
||
| class PlaceholderInfo | ||
| { | ||
| internal string token; | ||
| internal string value; | ||
| } | ||
|
|
||
| internal static void Show(string command, Action<string> onConfirm) | ||
| { | ||
| var matches = s_PlaceholderRegex.Matches(command); | ||
| if (matches.Count == 0) | ||
| { | ||
| onConfirm?.Invoke(command); | ||
| return; | ||
| } | ||
|
|
||
| var wnd = CreateInstance<AndroidLogcatPlaceholderDialog>(); | ||
| wnd.titleContent = new GUIContent("Resolve Placeholders"); | ||
| wnd.m_OriginalCommand = command; | ||
| wnd.m_OnConfirm = onConfirm; | ||
| wnd.minSize = new Vector2(400, 150); | ||
| wnd.maxSize = new Vector2(600, 400); | ||
|
|
||
| var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
| foreach (Match match in matches) | ||
| { | ||
| var token = match.Groups[1].Value; | ||
| if (!seen.Add(token)) | ||
| continue; | ||
|
|
||
| var defaultValue = ""; | ||
| if (token.Equals("package", StringComparison.OrdinalIgnoreCase)) | ||
| defaultValue = PlayerSettings.applicationIdentifier; | ||
|
|
||
| wnd.m_Placeholders.Add(new PlaceholderInfo { token = token, value = defaultValue }); | ||
| } |
There was a problem hiding this comment.
The review identified critical concerns regarding the execution of ADB commands on the Main Thread, which will lead to Editor freezes and broken UI feedback. Additionally, there are potential lifecycle issues that could cause NullReferenceException when child windows outlive their parent. Several performance optimizations regarding memory allocation and list manipulation are also recommended to ensure a smooth user experience.
🤖 Helpful? 👍/👎
| m_Runtime.Closing -= OnDisable; | ||
| m_DeviceSelection.Dispose(); | ||
| m_DeviceSelection = null; | ||
| m_Runtime = null; |
There was a problem hiding this comment.
When this window is closed, m_Runtime and m_DeviceSelection are set to null. However, spawned child windows like AndroidLogcatCommandSearchWindow, AndroidLogcatAddCommandDialog, and AndroidLogcatPlaceholderDialog can remain open. If a user interacts with them after closing this window (e.g., clicking 'Add', 'Save', or 'Run'), their callbacks will execute on this disposed context, leading to a NullReferenceException inside SaveCommands() or ExecuteCommand().
Consider either closing these child windows inside OnDisable or adding null checks (if (m_Runtime == null) return;) inside the passed callbacks.
🤖 Helpful? 👍/👎
| { | ||
| var lines = resolvedCommand.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | ||
|
|
||
| m_Running = true; |
There was a problem hiding this comment.
The execution of shell and ADB commands here is synchronous and blocks the Unity Main Thread. This causes the Editor UI to completely freeze until the command finishes (and for commands like adb wait-for-device, it could freeze indefinitely).
As a consequence, the m_Running = true; Repaint(); state change never has a chance to render, meaning the "Running..." UI feedback will never be visible to the user. Have you considered using a background thread (e.g., via the existing AndroidLogcatDispatcher) to execute the commands without blocking the Editor?
📚 Additional Context
com.unity.mobile.android-logcat/Editor/AndroidTools/Shell.cscom.unity.mobile.android-logcat/Editor/AndroidTools/AndroidBridge.cscom.unity.mobile.android-logcat/Editor/Tasks/AndroidLogcatDispatcher.cs
🤖 Helpful? 👍/👎
| public AndroidLogcatCommandEntry[] general = Array.Empty<AndroidLogcatCommandEntry>(); | ||
| } | ||
|
|
||
| internal static class AndroidLogcatCommandJsonHelper |
| s_Instance.m_Lines.Add(new OutputLine { text = line, color = color }); | ||
| } | ||
|
|
||
| while (s_Instance.m_Lines.Count > kMaxLines) |
There was a problem hiding this comment.
Using RemoveAt(0) in a while loop requires shifting all remaining elements in the underlying array on every iteration, leading to O(N * M) performance. This can cause noticeable UI lag when appending large command outputs. Consider using RemoveRange instead to drop the oldest lines in a single operation.
Suggested change (apply manually):
if (s_Instance.m_Lines.Count > kMaxLines)
s_Instance.m_Lines.RemoveRange(0, s_Instance.m_Lines.Count - kMaxLines);🤖 Helpful? 👍/👎
|
|
||
| // Command preview | ||
| var cmdDisplay = entry.command; | ||
| if (cmdDisplay.IndexOfAny(new[] { '\r', '\n' }) >= 0) |
There was a problem hiding this comment.
| foreach (var line in text.Split('\n')) | ||
| m_OutputLines.Add(new OutputLine { text = line, color = color }); | ||
|
|
||
| while (m_OutputLines.Count > kMaxOutputLines) |
There was a problem hiding this comment.
Similar to the command output window, repeatedly calling RemoveAt(0) requires shifting the remaining list elements on every iteration. Using RemoveRange is significantly more efficient.
Suggested change (apply manually):
if (m_OutputLines.Count > kMaxOutputLines)
m_OutputLines.RemoveRange(0, m_OutputLines.Count - kMaxOutputLines);🤖 Helpful? 👍/👎
| localResults.Add((e, "General")); | ||
|
|
||
| // Collect catalog results (excluding already-saved commands) | ||
| var savedCommands = new HashSet<string>( |
There was a problem hiding this comment.
Creating a HashSet and evaluating LINQ queries (Concat, Select) inside OnGUI will execute on every UI event (repaint, mouse move, etc.) whenever a search query is active. This can cause significant garbage collection pressure and frame drops. Consider caching savedCommands and catalogResults, updating them only when the search query or command lists actually change.
🤖 Helpful? 👍/👎
AlexBedardReidU3D
left a comment
There was a problem hiding this comment.
Some of the included PR Bot suggestions are good ones, and should be considered.
If you can include screenshots of the new UI windows, and menus into the PR description, that would be very helpful
todi1856
left a comment
There was a problem hiding this comment.
I think during discussion we agreed this will be implemented with UIToolkit? But I see lots of IMGUI code, is that expected?
The problem is - sooner or later we'll need to rewrite logcat to use UIToolkit, thus it wouldn't be good to add more IMGUI code :)
Ah, i mixed up which UI we were going to, and kept it consistent with the current, let me address that and the screenshots, thank you! |
|
I had no chance to try it out yet, but reading through the changes raised a question: |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 27 changed files in this pull request and generated 8 comments.
Files not reviewed (12)
- com.unity.mobile.android-logcat/Editor/AndroidLogcatAdbCommandCatalog.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatAddCommandDialog.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandEntry.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandOutputWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandSearchWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatCommandsWindow.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/AndroidLogcatPlaceholderDialog.cs.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/UI/Layouts/AndroidLogcatAddCommand.uxml.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/UI/Layouts/AndroidLogcatCommandOutput.uxml.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/UI/Layouts/AndroidLogcatCommandSearch.uxml.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/UI/Layouts/AndroidLogcatCommands.uxml.meta: Language not supported
- com.unity.mobile.android-logcat/Editor/UI/Layouts/AndroidLogcatPlaceholder.uxml.meta: Language not supported
| if (!AndroidBridge.AndroidExtensionsInstalled) | ||
| return; |
| if (m_RunningLabel != null) | ||
| m_RunningLabel.style.display = DisplayStyle.Flex; | ||
|
|
||
| var deviceId = m_DeviceSelection?.SelectedDevice?.Id; | ||
|
|
||
| foreach (var rawLine in lines) | ||
| { | ||
| var cmd = rawLine.Trim(); | ||
| if (string.IsNullOrEmpty(cmd)) | ||
| continue; | ||
|
|
||
| AppendOutput($"> {cmd}", kAccentColor); | ||
|
|
||
| try | ||
| { | ||
| string result; | ||
| if (cmd.StartsWith("adb ", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| var adbArgs = cmd.Substring(4); | ||
| if (!string.IsNullOrEmpty(deviceId) && !adbArgs.TrimStart().StartsWith("-s ")) | ||
| adbArgs = $"-s {deviceId} {adbArgs}"; | ||
| result = m_Runtime.Tools.ADB.Run(new[] { adbArgs }, ""); | ||
| } | ||
| else | ||
| { | ||
| var parts = cmd.Split(new[] { ' ' }, 2); | ||
| var shellResult = Shell.RunProcess(parts[0], parts.Length > 1 ? parts[1] : ""); | ||
| result = shellResult.GetStandardOut(); | ||
| var err = shellResult.GetStandardErr(); | ||
| if (!string.IsNullOrEmpty(err)) | ||
| result = string.IsNullOrEmpty(result) ? err : result + "\n" + err; | ||
| } | ||
|
|
||
| AppendOutput(result, GUI.skin.label.normal.textColor); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| AppendOutput($"[Error] {ex.Message}", kErrorColor); | ||
| } | ||
| } | ||
|
|
||
| if (m_RunningLabel != null) | ||
| m_RunningLabel.style.display = DisplayStyle.None; |
| void AppendOutput(string text, Color color) | ||
| { | ||
| if (string.IsNullOrEmpty(text)) | ||
| return; | ||
|
|
||
| foreach (var line in text.Split('\n')) | ||
| { | ||
| m_OutputLines.Add(new OutputLine { text = line, color = color }); | ||
| if (m_OutputContainer != null) | ||
| { | ||
| var label = new Label(line); | ||
| label.style.color = new StyleColor(color); | ||
| label.style.whiteSpace = WhiteSpace.Normal; | ||
| m_OutputContainer.Add(label); | ||
| } | ||
| } |
| var parts = cmd.Split(new[] { ' ' }, 2); | ||
| var shellResult = Shell.RunProcess(parts[0], parts.Length > 1 ? parts[1] : ""); |
| if ((data.favorites == null || data.favorites.Length == 0) && (data.general == null || data.general.Length == 0)) | ||
| { | ||
| EditorUtility.DisplayDialog("Import Failed", "The file contains no commands.", "OK"); | ||
| return; | ||
| } | ||
|
|
||
| if (!EditorUtility.DisplayDialog("Import Commands", "This will replace all your current commands. Continue?", "Import", "Cancel")) | ||
| return; | ||
|
|
||
| m_Favorites.Clear(); | ||
| if (data.favorites != null) m_Favorites.AddRange(data.favorites); | ||
| m_GeneralCommands.Clear(); | ||
| if (data.general != null) m_GeneralCommands.AddRange(data.general); | ||
|
|
| internal static void AppendIfOpen(string text, Color color) | ||
| { | ||
| if (s_Instance == null || string.IsNullOrEmpty(text)) | ||
| return; | ||
|
|
||
| foreach (var line in text.Split('\n')) | ||
| { | ||
| var ol = new AndroidLogcatCommandsWindow.OutputLine { text = line, color = color }; | ||
| s_Instance.m_Lines.Add(ol); | ||
| s_Instance.AddLineElement(ol); | ||
| } |
| return entry.name.ToLowerInvariant().Contains(terms) | ||
| || entry.command.ToLowerInvariant().Contains(terms); |
| static readonly Regex s_PlaceholderRegex = new Regex(@"<([^>]+)>", RegexOptions.Compiled); | ||
|
|
||
| string m_OriginalCommand; | ||
| Action<string> m_OnConfirm; | ||
| List<PlaceholderInfo> m_Placeholders = new List<PlaceholderInfo>(); | ||
|
|
||
| class PlaceholderInfo | ||
| { | ||
| internal string token; | ||
| internal string value; | ||
| } | ||
|
|
||
| internal static void Show(string command, Action<string> onConfirm) | ||
| { | ||
| var matches = s_PlaceholderRegex.Matches(command); | ||
| if (matches.Count == 0) | ||
| { | ||
| onConfirm?.Invoke(command); | ||
| return; | ||
| } | ||
|
|
||
| var wnd = CreateInstance<AndroidLogcatPlaceholderDialog>(); | ||
| wnd.titleContent = new GUIContent("Resolve Placeholders"); | ||
| wnd.m_OriginalCommand = command; | ||
| wnd.m_OnConfirm = onConfirm; | ||
| wnd.minSize = new Vector2(400, 150); | ||
| wnd.maxSize = new Vector2(600, 400); | ||
|
|
||
| var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
| foreach (Match match in matches) | ||
| { | ||
| var token = match.Groups[1].Value; | ||
| if (!seen.Add(token)) | ||
| continue; | ||
|
|
||
| var defaultValue = ""; | ||
| if (token.Equals("package", StringComparison.OrdinalIgnoreCase)) | ||
| defaultValue = PlayerSettings.applicationIdentifier; | ||
|
|
||
| wnd.m_Placeholders.Add(new PlaceholderInfo { token = token, value = defaultValue }); | ||
| } | ||
|
|
||
| wnd.ShowUtility(); | ||
| } |
Agent-Logs-Url: https://github.com/Unity-Technologies/com.unity.mobile.logcat/sessions/a5e3af95-34cf-46cc-aeb3-cbe930bb81e2 Co-authored-by: DiannaUnity <28332453+DiannaUnity@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Purpose of PR
Type of change:
Description of the feature
Adds a new Commands window to the Android Logcat package, accessible via Tools > Commands in the logcat console toolbar. It provides a way to build, organize, and execute ADB and shell commands directly from the Unity Editor without needing an external terminal.
Halo effect
Performance Impact
Package requirements
Checklist for PR maker
CHANGELOG.mdfileTesting status
No new automation tests added
Put any relevant Yamato links here
N/A
Testing done
What environment did you test on?
macOS, Unity Editor 6000.6.0a2_64de5ee36373
Projects
N/A
Testing checklist
Note to guardian
N/A
Comments to reviewers
This is a self-contained new feature — a Commands window for running ADB and shell commands from the Editor. The changes to existing files are minimal and purely additive (a menu entry, an enum value, and a settings block for persistence). No existing behavior was modified. The bulk of the PR is the new window and its supporting dialogs/utilities.
dockable command window pop-out

Create adb commands with multiple functions

Searching a catalog of 100+ adb commands
