Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

491 lines
14 KiB

#if UNITY_EDITOR_WIN && UNITY_ANDROID
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using UnityEngine;
using UnityEditor;
public class OVRBundleTool : EditorWindow
{
private static List<EditorSceneInfo> buildableScenes;
private static Vector2 debugLogScroll = new Vector2(0, 0);
private static bool invalidBuildableScene;
private static string toolLog;
private static bool useOptionalTransitionApkPackage;
private static GUIStyle logBoxStyle;
private static Vector2 logBoxSize;
private static float logBoxSpacing = 30.0f;
private bool forceRestart = false;
private bool showBundleManagement = false;
private bool showOther = false;
// Needed to ensure that APK checking does happen during editor start up, but will still happen when the window is opened/updated
private static bool panelInitialized = false;
private enum ApkStatus
{
UNKNOWN,
OK,
NOT_INSTALLED,
DEVICE_NOT_CONNECTED,
};
public enum SceneBundleStatus
{
[Description("Unknown")]
UNKNOWN,
[Description("Queued")]
QUEUED,
[Description("Building")]
BUILDING,
[Description("Done")]
DONE,
[Description("Transferring")]
TRANSFERRING,
[Description("Deployed")]
DEPLOYED,
};
public class EditorSceneInfo
{
public string scenePath;
public string sceneName;
public SceneBundleStatus buildStatus;
public EditorSceneInfo(string path, string name)
{
scenePath = path;
sceneName = name;
buildStatus = SceneBundleStatus.UNKNOWN;
}
}
private static ApkStatus currentApkStatus;
[MenuItem("Oculus/OVR Build/OVR Scene Quick Preview %l", false, 10)]
static void Init()
{
currentApkStatus = ApkStatus.UNKNOWN;
EditorWindow.GetWindow(typeof(OVRBundleTool));
invalidBuildableScene = false;
InitializePanel();
OVRPlugin.SetDeveloperMode(OVRPlugin.Bool.True);
OVRPlugin.SendEvent("oculus_bundle_tool", "show_window");
}
public void OnEnable()
{
InitializePanel();
}
public static void InitializePanel()
{
panelInitialized = true;
GetScenesFromBuildSettings();
EditorBuildSettings.sceneListChanged += GetScenesFromBuildSettings;
}
private void OnGUI()
{
this.titleContent.text = "OVR Scene Quick Preview";
if (panelInitialized)
{
CheckForTransitionAPK();
panelInitialized = false;
}
if (logBoxStyle == null)
{
logBoxStyle = new GUIStyle();
logBoxStyle.margin.left = 5;
logBoxStyle.wordWrap = true;
logBoxStyle.normal.textColor = logBoxStyle.focused.textColor = EditorStyles.label.normal.textColor;
logBoxStyle.richText = true;
}
GUILayout.Space(10.0f);
GUILayout.Label("Scenes", EditorStyles.boldLabel);
GUIContent buildSettingsBtnTxt = new GUIContent("Open Build Settings");
if (buildableScenes == null || buildableScenes.Count == 0)
{
string sceneErrorMessage;
if (invalidBuildableScene)
{
sceneErrorMessage = "Invalid scene selection. \nPlease remove OVRTransitionScene in the project's build settings.";
}
else
{
sceneErrorMessage = "No scenes detected. \nTo get started, add scenes in the project's build settings.";
}
GUILayout.Label(sceneErrorMessage);
var buildSettingBtnRt = GUILayoutUtility.GetRect(buildSettingsBtnTxt, GUI.skin.button, GUILayout.Width(150));
if (GUI.Button(buildSettingBtnRt, buildSettingsBtnTxt))
{
OpenBuildSettingsWindow();
}
}
else
{
foreach (EditorSceneInfo scene in buildableScenes)
{
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(scene.sceneName, GUILayout.ExpandWidth(true));
GUILayout.FlexibleSpace();
if (scene.buildStatus != SceneBundleStatus.UNKNOWN)
{
string status = GetEnumDescription(scene.buildStatus);
EditorGUILayout.LabelField(status, GUILayout.Width(70));
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
{
GUIContent sceneBtnTxt = new GUIContent("Build and Deploy Scene(s)");
var sceneBtnRt = GUILayoutUtility.GetRect(sceneBtnTxt, GUI.skin.button, GUILayout.Width(200));
if (GUI.Button(sceneBtnRt, sceneBtnTxt))
{
// Check the latest transition apk status
CheckForTransitionAPK();
// Show a dialog to prompt for building and deploying transition APK
if (currentApkStatus != ApkStatus.OK &&
EditorUtility.DisplayDialog("Build and Deploy OVR Transition APK?",
"OVR Transition APK status not ready, it is required to load your scene bundle for quick preview.",
"Yes",
"No"))
{
PrintLog("Building OVR Transition APK");
OVRBundleManager.BuildDeployTransitionAPK(useOptionalTransitionApkPackage);
CheckForTransitionAPK();
}
for (int i = 0; i < buildableScenes.Count; i++)
{
buildableScenes[i].buildStatus = SceneBundleStatus.QUEUED;
}
OVRBundleManager.BuildDeployScenes(buildableScenes, forceRestart);
}
GUIContent forceRestartLabel = new GUIContent("Force Restart [?]", "Relaunch the application after scene bundles are finished deploying.");
forceRestart = GUILayout.Toggle(forceRestart, forceRestartLabel, GUILayout.ExpandWidth(true));
}
EditorGUILayout.EndHorizontal();
}
GUILayout.Space(10.0f);
GUIContent transitionContent = new GUIContent("Transition APK [?]", "Build and deploy an APK that will transition into the scene you are working on. This enables fast iteration on a specific scene.");
GUILayout.Label(transitionContent, EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
{
GUIStyle statusStyle = EditorStyles.label;
statusStyle.richText = true;
GUILayout.Label("Status: ", statusStyle, GUILayout.ExpandWidth(false));
string statusMesssage;
switch (currentApkStatus)
{
case ApkStatus.OK:
statusMesssage = "<color=green>APK installed. Ready to build and deploy scenes.</color>";
break;
case ApkStatus.NOT_INSTALLED:
statusMesssage = "<color=red>APK not installed. Press build and deploy to install the transition APK.</color>";
break;
case ApkStatus.DEVICE_NOT_CONNECTED:
statusMesssage = "<color=red>Device not connected via ADB. Please connect device and allow debugging.</color>";
break;
case ApkStatus.UNKNOWN:
default:
statusMesssage = "<color=red>Failed to get APK status!</color>";
break;
}
GUILayout.Label(statusMesssage, statusStyle, GUILayout.ExpandWidth(true));
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
{
GUIContent btnTxt = new GUIContent("Build and Deploy App");
var rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.Width(200));
if (GUI.Button(rt, btnTxt))
{
OVRBundleManager.BuildDeployTransitionAPK(useOptionalTransitionApkPackage);
CheckForTransitionAPK();
}
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10.0f);
GUILayout.Label("Utilities", EditorStyles.boldLabel);
showBundleManagement = EditorGUILayout.Foldout(showBundleManagement, "Bundle Management");
if (showBundleManagement)
{
EditorGUILayout.BeginHorizontal();
{
GUIContent clearDeviceBundlesTxt = new GUIContent("Delete Device Bundles");
var clearDeviceBundlesBtnRt = GUILayoutUtility.GetRect(clearDeviceBundlesTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(clearDeviceBundlesBtnRt, clearDeviceBundlesTxt))
{
OVRBundleManager.DeleteRemoteAssetBundles();
}
GUIContent clearLocalBundlesTxt = new GUIContent("Delete Local Bundles");
var clearLocalBundlesBtnRt = GUILayoutUtility.GetRect(clearLocalBundlesTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(clearLocalBundlesBtnRt, clearLocalBundlesTxt))
{
OVRBundleManager.DeleteLocalAssetBundles();
}
}
EditorGUILayout.EndHorizontal();
}
showOther = EditorGUILayout.Foldout(showOther, "Other");
if (showOther)
{
EditorGUILayout.BeginHorizontal();
{
GUIContent useOptionalTransitionPackageLabel = new GUIContent("Use optional APK package name [?]",
"This allows both full build APK and transition APK to be installed on device. However, platform services like Entitlement check may fail.");
EditorGUILayout.LabelField(useOptionalTransitionPackageLabel, GUILayout.ExpandWidth(false));
bool newToggleValue = EditorGUILayout.Toggle(useOptionalTransitionApkPackage);
if (newToggleValue != useOptionalTransitionApkPackage)
{
useOptionalTransitionApkPackage = newToggleValue;
// Update transition APK status after changing package name option
CheckForTransitionAPK();
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
{
GUIContent launchBtnTxt = new GUIContent("Launch App");
var launchBtnRt = GUILayoutUtility.GetRect(launchBtnTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(launchBtnRt, launchBtnTxt))
{
OVRBundleManager.LaunchApplication();
}
var buildSettingBtnRt = GUILayoutUtility.GetRect(buildSettingsBtnTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(buildSettingBtnRt, buildSettingsBtnTxt))
{
OpenBuildSettingsWindow();
}
GUIContent uninstallTxt = new GUIContent("Uninstall APK");
var uninstallBtnRt = GUILayoutUtility.GetRect(uninstallTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(uninstallBtnRt, uninstallTxt))
{
OVRBundleManager.UninstallAPK();
CheckForTransitionAPK();
}
GUIContent clearLogTxt = new GUIContent("Clear Log");
var clearLogBtnRt = GUILayoutUtility.GetRect(clearLogTxt, GUI.skin.button, GUILayout.ExpandWidth(true));
if (GUI.Button(clearLogBtnRt, clearLogTxt))
{
PrintLog("", true);
}
}
EditorGUILayout.EndHorizontal();
}
GUILayout.Space(10.0f);
GUILayout.Label("Log", EditorStyles.boldLabel);
if (!string.IsNullOrEmpty(toolLog))
{
debugLogScroll = EditorGUILayout.BeginScrollView(debugLogScroll, GUILayout.ExpandHeight(true));
EditorGUILayout.SelectableLabel(toolLog, logBoxStyle, GUILayout.Height(logBoxSize.y + logBoxSpacing));
EditorGUILayout.EndScrollView();
}
}
private static void OpenBuildSettingsWindow()
{
EditorWindow.GetWindow(System.Type.GetType("UnityEditor.BuildPlayerWindow,UnityEditor"));
}
public static void UpdateSceneBuildStatus(SceneBundleStatus status, int index = -1)
{
if (index >= 0 && index < buildableScenes.Count)
{
buildableScenes[index].buildStatus = status;
}
else
{
// Update status for all scenes
for (int i = 0; i < buildableScenes.Count; i++)
{
buildableScenes[i].buildStatus = status;
}
}
}
private static void GetScenesFromBuildSettings()
{
invalidBuildableScene = false;
buildableScenes = new List<EditorSceneInfo>();
for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
{
EditorBuildSettingsScene scene = EditorBuildSettings.scenes[i];
if (scene.enabled)
{
if (Path.GetFileNameWithoutExtension(scene.path) != "OVRTransitionScene")
{
EditorSceneInfo sceneInfo = new EditorSceneInfo(scene.path, Path.GetFileNameWithoutExtension(scene.path));
buildableScenes.Add(sceneInfo);
}
else
{
buildableScenes = null;
invalidBuildableScene = true;
return;
}
}
}
}
private static void CheckForTransitionAPK()
{
OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
if (adbTool.isReady)
{
string matchedPackageList, error;
var transitionPackageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
if (useOptionalTransitionApkPackage)
{
transitionPackageName += ".transition";
}
string[] packageCheckCommand = new string[] { "-d shell pm list package", transitionPackageName };
if (adbTool.RunCommand(packageCheckCommand, null, out matchedPackageList, out error) == 0)
{
if (string.IsNullOrEmpty(matchedPackageList))
{
currentApkStatus = ApkStatus.NOT_INSTALLED;
}
else
{
// adb "list package" command returns all package names that contains the given query package name
// Need to check if the transition package name is matched exactly
if (matchedPackageList.Contains("package:" + transitionPackageName + "\r\n"))
{
if (useOptionalTransitionApkPackage)
{
// If optional package name is used, it is deterministic that the transition apk is installed
currentApkStatus = ApkStatus.OK;
}
else
{
// get package info to check for TRANSITION_APK_VERSION_NAME
string[] dumpPackageInfoCommand = new string[] { "-d shell dumpsys package", transitionPackageName };
string packageInfo;
if (adbTool.RunCommand(dumpPackageInfoCommand, null, out packageInfo, out error) == 0 &&
!string.IsNullOrEmpty(packageInfo) &&
packageInfo.Contains(OVRBundleManager.TRANSITION_APK_VERSION_NAME))
{
// Matched package name found, and the package info contains TRANSITION_APK_VERSION_NAME
currentApkStatus = ApkStatus.OK;
}
else
{
currentApkStatus = ApkStatus.NOT_INSTALLED;
}
}
}
else
{
// No matached package name returned
currentApkStatus = ApkStatus.NOT_INSTALLED;
}
}
}
else if (error.Contains("no devices found"))
{
currentApkStatus = ApkStatus.DEVICE_NOT_CONNECTED;
}
else
{
currentApkStatus = ApkStatus.UNKNOWN;
}
}
}
public static void PrintLog(string message, bool clear = false)
{
if (clear)
{
toolLog = message;
}
else
{
toolLog += message + "\n";
}
GUIContent logContent = new GUIContent(toolLog);
logBoxSize = logBoxStyle.CalcSize(logContent);
debugLogScroll.y = logBoxSize.y + logBoxSpacing;
}
public static void PrintError(string error = "")
{
if(!string.IsNullOrEmpty(error))
{
toolLog += "<color=red>Failed!\n</color>" + error + "\n";
}
else
{
toolLog += "<color=red>Failed! Check Unity log for more details.\n</color>";
}
}
public static void PrintWarning(string warning)
{
toolLog += "<color=yellow>Warning!\n" + warning + "</color>\n";
}
public static void PrintSuccess()
{
toolLog += "<color=green>Success!</color>\n";
}
public static string GetEnumDescription(Enum eEnum)
{
Type enumType = eEnum.GetType();
MemberInfo[] memberInfo = enumType.GetMember(eEnum.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return eEnum.ToString();
}
public static bool GetUseOptionalTransitionApkPackage()
{
return useOptionalTransitionApkPackage;
}
}
#endif