I found your question while was searching for the same answer. Since I wrote the solution, I will share it here.
In our project I’m using Reflection to access DeviceSimulator internals and manipulate them. I wrote a class that allows you to quickly select most quirky and most common devices from hotbar in Scene View and also you can iterate through all of them. I’m pretty sure you’ll find everything you need in my solution.
“using Sirenix.Utilities” dependency is not required at all, you can delete it and ditch an Expand call, no worries, it’s just for UI. I tested this code using 2021.3.27f1
Here how it looks in Scene View:
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
namespace ACME.Tools.DeviceSimulatorHelper {
[InitializeOnLoad]
public sealed class DeviceSimulatorHelper {
private const float AreaWidth = 270f;
private const float AreaHeight = 20f;
private static readonly List<SimulatorDeviceInHotBar> _deviceInHotBars = new() {
new() {
FriendlyName = "Samsung Galaxy Z Fold2 5G (Phone)",
DisplayInHotBarName = "Fold 1"
},
new() {
FriendlyName = "Samsung Galaxy Z Fold2 5G (Tablet)",
DisplayInHotBarName = "Fold 2"
},
new() {
FriendlyName = "Apple iPhone 13 Pro Max",
DisplayInHotBarName = "iPhone"
},
new() {
FriendlyName = "Google Pixel 2 XL",
DisplayInHotBarName = "16:9"
},
};
private static bool _isSimulatorDeviceIndexesWarmedUp;
private static Lazy<Assembly> DeviceSimulatorAssembly = new(() => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(assembly => assembly.GetName().Name == "UnityEditor.DeviceSimulatorModule"));
private static Lazy<Type> SimulatorWindowType => new(() = DeviceSimulatorAssembly.Value.GetType("UnityEditor.DeviceSimulation.SimulatorWindow"));
private static Lazy<FieldInfo> SimulatorWindowMainField = new(() => SimulatorWindowType.Value.GetField("m_Main", BindingFlags.Instance | BindingFlags.NonPublic));
private static Lazy<Type> DeviceSimulatorMainType = new(() => DeviceSimulatorAssembly.Value.GetType("UnityEditor.DeviceSimulation.DeviceSimulatorMain"));
private static Lazy<Type> DeviceInfoAssetType = new(() => DeviceSimulatorAssembly.Value.GetType("UnityEditor.DeviceSimulation.DeviceInfoAsset"));
private static Lazy<Type> DeviceInfoType = new(() => DeviceSimulatorAssembly.Value.GetType("UnityEditor.DeviceSimulation.DeviceInfo"));
private static Lazy<FieldInfo> DeviceSimulatorMainDeviceIndexField = new(() => DeviceSimulatorMainType.Value.GetField("m_DeviceIndex", BindingFlags.Instance | BindingFlags.NonPublic));
private static Lazy<PropertyInfo> DeviceSimulatorMainDeviceIndexProperty = new(() => DeviceSimulatorMainType.Value.GetProperty("deviceIndex", BindingFlags.Instance | BindingFlags.Public));
private static Lazy<FieldInfo> DeviceSimulatorMainDevicesField = new(() => DeviceSimulatorMainType.Value.GetField("m_Devices", BindingFlags.Instance | BindingFlags.NonPublic));
private static Lazy<FieldInfo> DeviceInfoAssetDeviceInfoField = new(() => DeviceInfoAssetType.Value.GetField("deviceInfo", BindingFlags.Instance | BindingFlags.Public));
private static Lazy<FieldInfo> DeviceInfoFriendlyNameField = new(() => DeviceInfoType.Value.GetField("friendlyName", BindingFlags.Instance | BindingFlags.Public));
private static Lazy<GUIStyle> SelectedButtonStyle = new(() => new GUIStyle(GUI.skin.button) {
normal = {
textColor = new Color(0.49f, 0.81f, 0.5f),
},
hover = {
textColor = new Color(0.55f, 0.87f, 0.56f),
},
});
private static float _sceneWindowHeight;
private static Lazy<Type> GameViewType = new(() => {
var result = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.FirstOrDefault(type => type.Namespace == "UnityEditor" && type.Name == "PlayModeView");
if (result == null) {
throw new Exception("UnityEditor.PlayModeView does NOT exist in current version.");
}
return result;
});
private static EditorWindow _activeGameView;
private static EditorWindow ActiveGameView {
get {
if (_activeGameView != null && _activeGameView.hasFocus) {
return _activeGameView;
}
var gameViews = Resources.FindObjectsOfTypeAll(GameViewType.Value);
foreach (var gameView in gameViews) {
var current = gameView as EditorWindow;
if (current != null && current.hasFocus) {
_activeGameView = current;
return _activeGameView;
}
}
return null;
}
}
static DeviceSimulatorHelper() {
SceneView.duringSceneGui += OnSceneGUI;
}
private static void OnSceneGUI(SceneView sceneView) {
if (!IsInSimulatorState()) {
return;
}
WarmUpSimulatorDeviceIndexes();
Handles.BeginGUI();
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
var scale = GUILayoutUtility.GetLastRect();
if (scale.height > 1f) {
_sceneWindowHeight = scale.height;
}
var drawAreaRect = new Rect(10f, _sceneWindowHeight - AreaHeight - 6f, AreaWidth, AreaHeight);
GUILayout.BeginArea(drawAreaRect);
GUILayout.BeginHorizontal();
if (GUILayout.Button("<", GUILayout.Width(25))) {
SelectPreviousSimulatorDevice();
}
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
foreach (var deviceInHotBar in _deviceInHotBars) {
if (deviceInHotBar.Index == SimulatorDeviceInHotBar.InvalidIndex) {
continue;
}
var isSelected = (int)DeviceSimulatorMainDeviceIndexField.Value.GetValue(deviceSimulatorMain) == deviceInHotBar.Index;
if (GUILayout.Button(deviceInHotBar.DisplayInHotBarName, isSelected ? SelectedButtonStyle.Value : GUI.skin.button, GUILayout.Width(50))) {
SelectSimulatorDevice(deviceInHotBar.FriendlyName);
}
}
});
if (GUILayout.Button(">", GUILayout.Width(25))) {
SelectNextSimulatorDevice();
}
GUILayout.EndHorizontal();
GUILayout.EndArea();
Handles.EndGUI();
var currentEvent = Event.current;
if (currentEvent.type == EventType.ScrollWheel && drawAreaRect.Expand(15f).Contains(currentEvent.mousePosition)) {
if (currentEvent.delta.y < 0) {
SelectPreviousSimulatorDevice();
}
else {
SelectNextSimulatorDevice();
}
currentEvent.Use();
}
}
private static void WarmUpSimulatorDeviceIndexes() {
if (_isSimulatorDeviceIndexesWarmedUp) {
return;
}
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
var devices = DeviceSimulatorMainDevicesField.Value.GetValue(deviceSimulatorMain) as Array;
var index = 0;
foreach (var device in devices) {
var deviceInfo = DeviceInfoAssetDeviceInfoField.Value.GetValue(device);
var friendlyName = DeviceInfoFriendlyNameField.Value.GetValue(deviceInfo).ToString();
foreach (var deviceInHotBar in _deviceInHotBars) {
if (deviceInHotBar.FriendlyName == friendlyName) {
deviceInHotBar.Index = index;
}
}
++index;
}
_isSimulatorDeviceIndexesWarmedUp = true;
});
}
private static void SelectNextSimulatorDevice() {
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
var currentIndex = DeviceSimulatorMainDeviceIndexField.Value.GetValue(deviceSimulatorMain);
var nextIndex = (int)currentIndex + 1;
var devices = DeviceSimulatorMainDevicesField.Value.GetValue(deviceSimulatorMain) as Array;
if (nextIndex >= devices.Length) {
nextIndex = 0;
}
SelectSimulatorDevice(nextIndex);
});
}
private static void SelectPreviousSimulatorDevice() {
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
var currentIndex = DeviceSimulatorMainDeviceIndexField.Value.GetValue(deviceSimulatorMain);
var prevIndex = (int)currentIndex - 1;
if (prevIndex < 0) {
var devices = DeviceSimulatorMainDevicesField.Value.GetValue(deviceSimulatorMain) as Array;
prevIndex = devices.Length - 1;
}
SelectSimulatorDevice(prevIndex);
});
}
private static void SelectSimulatorDevice(int index) {
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
DeviceSimulatorMainDeviceIndexProperty.Value.SetValue(deviceSimulatorMain, index);
});
}
private static void SelectSimulatorDevice(string deviceName) {
ForActiveDeviceSimulatorMain(deviceSimulatorMain => {
var devices = DeviceSimulatorMainDevicesField.Value.GetValue(deviceSimulatorMain) as Array;
var index = 0;
foreach (var device in devices) {
var deviceInfo = DeviceInfoAssetDeviceInfoField.Value.GetValue(device);
var friendlyName = DeviceInfoFriendlyNameField.Value.GetValue(deviceInfo).ToString();
if (friendlyName == deviceName) {
DeviceSimulatorMainDeviceIndexProperty.Value.SetValue(deviceSimulatorMain, index);
return;
}
++index;
}
});
}
private static void ForActiveDeviceSimulatorMain(Action<object> callback) {
var simulatorWindow = Resources.FindObjectsOfTypeAll(SimulatorWindowType.Value).FirstOrDefault();
if (simulatorWindow == null) {
return;
}
var deviceSimulatorMain = SimulatorWindowMainField.Value.GetValue(simulatorWindow);
if (deviceSimulatorMain != null) {
callback(deviceSimulatorMain);
}
}
private static bool IsInSimulatorState() {
if (ActiveGameView == null) {
return false;
}
return ActiveGameView.titleContent.text == "Simulator";
}
private class SimulatorDeviceInHotBar {
public const int InvalidIndex = -1;
public string FriendlyName;
public string DisplayInHotBarName;
public int Index = InvalidIndex;
}
}
}
#endif