csharp/absurd-joy/Quest-hands-for-Normcore/Assets/Oculus/VR/Editor/OVRBundleManager.cs

OVRBundleManager.cs
#if UNITY_EDITOR_WIN && UNITY_ANDROID
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;

using UnityEngine;
using UnityEditor;
using UnityEditor.Build.Reporting;

public clast OVRBundleManager
{
	public const string TRANSITION_APK_VERSION_NAME = "OVRTransitionAPKVersion";
	private const int BUNDLE_CHUNK_SIZE = 30;
	private const string TRANSITION_SCENE_RELATIVE_PATH = "Scenes/OVRTransitionScene.unity";
	private const string BUNDLE_MANAGER_OUTPUT_PATH = "OVRastetBundles";
	private const string BUNDLE_MANAGER_MASTER_BUNDLE = "OVRMasterBundle";

	private const string EXTERNAL_STORAGE_PATH = "/sdcard/Android/data";
	private const string ADB_TOOL_INITIALIZE_ERROR = "Failed to initialize ADB Tool. Please check Android SDK path in Preferences -> External Tools";

	private static string externalSceneCache;
	private static string transitionScenePath;

	private static string projectDefaultAppIdentifier;
	private static string projectDefaultVersion;
	private static ScriptingImplementation projectScriptImplementation;
	private static ManagedStrippingLevel projectManagedStrippingLevel;
	private static bool projectStripEngineCode;

	public static void BuildDeployTransitionAPK(bool useOptionalTransitionApkPackage)
	{
		OVRBundleTool.PrintLog("Building and deploying transition APK  . . . ", true);

		if (!Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
		{
			Directory.CreateDirectory(BUNDLE_MANAGER_OUTPUT_PATH);
		}

		PrebuildProjectSettingUpdate();

		if (String.IsNullOrEmpty(transitionScenePath))
		{
			// Get current editor script's directory as base path
			string[] res = System.IO.Directory.GetFiles(Application.dataPath, "OVRBundleManager.cs", SearchOption.AllDirectories);
			if (res.Length > 1)
			{
				OVRBundleTool.PrintError("More than one OVRBundleManager editor script has been found, please double check your Oculus SDK import.");
				return;
			}
			else
			{
				// Append Transition Scene's relative path to base path
				var OVREditorScript = Path.GetDirectoryName(res[0]);
				transitionScenePath = Path.Combine(OVREditorScript, TRANSITION_SCENE_RELATIVE_PATH);
			}
		}

		string[] buildScenes = new string[1] { transitionScenePath };
		string apkOutputPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "OVRTransition.apk");
		DateTime apkBuildStart = DateTime.Now;

		var buildPlayerOptions = new BuildPlayerOptions
		{
			scenes = buildScenes,
			locationPathName = apkOutputPath,
			target = BuildTarget.Android,
			options = BuildOptions.Development |
				BuildOptions.AutoRunPlayer
		};

		BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);

		if (report.summary.result == BuildResult.Succeeded)
		{
			OVRBundleTool.PrintSuccess();
		}
		else if (report.summary.result == BuildResult.Failed)
		{
			OVRBundleTool.PrintError();
		}
		OVRPlugin.SendEvent("oculus_bundle_tool", "apk_build_time", (DateTime.Now - apkBuildStart).TotalSeconds.ToString());
		PostbuildProjectSettingUpdate();
	}

	private static void PrebuildProjectSettingUpdate()
	{
		// Modify application identifier for transition APK
		projectDefaultAppIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
		PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, 
			projectDefaultAppIdentifier + GetTransitionApkOptionalIdentifier());

		// Set VersionCode as a unique identifier for transition APK
		projectDefaultVersion = PlayerSettings.bundleVersion;
		PlayerSettings.bundleVersion = TRANSITION_APK_VERSION_NAME;

		// Modify IL2CPP option as it strips script symbols that are necessary for the scenes at runtime
		projectScriptImplementation = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
		if (projectScriptImplementation != ScriptingImplementation.Mono2x)
		{
			// Show message in console to make it more clear to developers
			OVRBundleTool.PrintLog("Build will use Mono as scripting backend.");
			PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, ScriptingImplementation.Mono2x);
		}

		// Avoid stripping managed code that are necessary for the scenes at runtime
		projectManagedStrippingLevel = PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android);
		if (projectManagedStrippingLevel != ManagedStrippingLevel.Disabled)
		{
			OVRBundleTool.PrintLog("Build will set Managed Stripping Level to Disabled.");
			PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, ManagedStrippingLevel.Disabled);
		}

		projectStripEngineCode = PlayerSettings.stripEngineCode;
		if (projectStripEngineCode)
		{
			PlayerSettings.stripEngineCode = false;
		}
	}

	private static void PostbuildProjectSettingUpdate()
	{
		// Restore application identifier
		PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,
			projectDefaultAppIdentifier);

		// Restore version setting
		PlayerSettings.bundleVersion = projectDefaultVersion;

		// Restore scripting backend option
		if (PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup) != projectScriptImplementation)
		{
			PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, projectScriptImplementation);
		}

		// Restore managed stripping level
		if (PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android) != projectManagedStrippingLevel)
		{
			PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, projectManagedStrippingLevel);
		}

		if (PlayerSettings.stripEngineCode != projectStripEngineCode)
		{
			PlayerSettings.stripEngineCode = projectStripEngineCode;
		}
	}

	// Build and deploy a list of scenes. It's suggested to only build and deploy one active scene that's being modified and
	// its dependencies such as scenes that are loaded additively
	public static void BuildDeployScenes(List sceneList, bool forceRestart)
	{
		externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
			+ GetTransitionApkOptionalIdentifier() + "/cache/scenes";

		BuildSceneBundles(sceneList);
		if (DeploySceneBundles(sceneList))
		{
			if (forceRestart)
			{
				LaunchApplication();
				return;
			}
			OVRBundleTool.PrintSuccess();
			return;
		}
	}

	// Build astets that are used by scenes from the given list.
	// This function will automatically de-duplicated same astet file being referenced in multiple scenes.
	// Scene bundles will be created with name pattern: _
	private static void BuildSceneBundles(List sceneList)
	{
		DateTime totalStart = DateTime.Now;
		// Keeps track of dependent astets across scenes 
		// to ensure each astet is only packaged once in one of the scene bundles.
		// uniqueastetInSceneBundle is a map from "astet unique identifier" to the first scene that references the astet.
		// It supports different astets with same file name as "astet unique identifier" contain full qualified astet file path
		Dictionary uniqueastetInSceneBundle = new Dictionary();

		// List of bundle targets for Unity's build pipeline to package
		List astetBundleBuilds = new List();
		// Map that contains "astet type" to list of astets that are of the astet type
		Dictionary extToastetList = new Dictionary();

		// Check if astets in Resources need to be packaged
		if (CheckForResources())
		{
			var resourceDirectories = Directory.GetDirectories("astets", "Resources", SearchOption.AllDirectories).ToArray();
			// Fetch a list of all files in resource directory
			string[] resourceastetPaths = astetDatabase.Findastets("", resourceDirectories).Select(x => astetDatabase.GUIDToastetPath(x)).ToArray();
			Processastets(resourceastetPaths, "resources", ref uniqueastetInSceneBundle, ref extToastetList);

			astetBundleBuild resourceBundle = new astetBundleBuild();
			resourceBundle.astetNames = uniqueastetInSceneBundle.Keys.ToArray();
			resourceBundle.astetBundleName = OVRSceneLoader.resourceBundleName;
			astetBundleBuilds.Add(resourceBundle);
		}

		// Create scene bundle output directory
		string sceneBundleDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
		if (!Directory.Exists(sceneBundleDirectory))
		{
			Directory.CreateDirectory(sceneBundleDirectory);
		}

		OVRBundleTool.PrintLog("Building scene bundles . . . ");
		DateTime labelingStart = DateTime.Now;
		foreach (var scene in sceneList)
		{
			// Get all the astets that the scene depends on and sort them by type
			DateTime getDepStart = DateTime.Now;
			string[] astetDependencies = astetDatabase.GetDependencies(scene.scenePath);
			Debug.Log("[OVRBundleManager] - " + scene.sceneName + " - Calculated scene astet dependencies in: " + (DateTime.Now - getDepStart).TotalSeconds);
			Processastets(astetDependencies, scene.sceneName, ref uniqueastetInSceneBundle, ref extToastetList);

			// Add the scene into it's own bundle
			string[] sceneastet = new string[1] { scene.scenePath };
			astetBundleBuild sceneBuild = new astetBundleBuild();
			sceneBuild.astetBundleName = "scene_" + scene.sceneName;
			sceneBuild.astetNames = sceneastet;
			astetBundleBuilds.Add(sceneBuild);
		}

		// Chunk the astet bundles by number of astets
		foreach (string ext in extToastetList.Keys)
		{
			int astetCount = extToastetList[ext].Count;
			int numChunks = (astetCount + BUNDLE_CHUNK_SIZE - 1) / BUNDLE_CHUNK_SIZE;
			//Debug.Log(ext + " has " + astetCount + " astet(s) that will be split into " + numChunks + " chunk(s)");
			for (int i = 0; i < numChunks; i++)
			{
				List astetChunkList;
				if (i == numChunks - 1)
				{
					int size = BUNDLE_CHUNK_SIZE - (numChunks * BUNDLE_CHUNK_SIZE - astetCount);
					astetChunkList = extToastetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, size);
				}
				else
				{
					astetChunkList = extToastetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, BUNDLE_CHUNK_SIZE);
				}
				astetBundleBuild build = new astetBundleBuild();
				build.astetBundleName = "astet_" + ext + i;
				build.astetNames = astetChunkList.ToArray();
				//Debug.Log("Chunk " + i + " has " + astetChunkList.Count + " astet(s)");
				astetBundleBuilds.Add(build);
			}
		}

		Debug.Log("[OVRBundleManager] - Created chucked scene bundles in: " + (DateTime.Now - labelingStart).TotalSeconds);

		// Build astet bundles
		BuildPipeline.BuildastetBundles(sceneBundleDirectory, astetBundleBuilds.ToArray(),
				BuildastetBundleOptions.UncompressedastetBundle, BuildTarget.Android);

		double bundleBuildTime = (DateTime.Now - totalStart).TotalSeconds;
		Debug.Log("[OVRBundleManager] - Total Time: " + bundleBuildTime);
		OVRPlugin.SendEvent("oculus_bundle_tool", "bundle_build_time", bundleBuildTime.ToString());
	}

	private static void Processastets(string[] astetPaths, 
		string astetParent,
		ref Dictionary uniqueastetInSceneBundle, 
		ref Dictionary extToastetList)
	{
		foreach (string astet in astetPaths)
		{
			string ext = Path.GetExtension(astet);
			if (!string.IsNullOrEmpty(ext))
			{
				ext = ext.Substring(1);
				if (!ext.Equals("cs") && !ext.Equals("unity"))
				{
					// Only process each astet once across all resource and scene bundles
					// Each astet is keyed by full path as a unique identifier
					if (!uniqueastetInSceneBundle.ContainsKey(astet))
					{
						var astetObject = (UnityEngine.Object)astetDatabase.LoadastetAtPath(astet, typeof(UnityEngine.Object));
						if (astetObject == null || (astetObject.hideFlags & HideFlags.DontSaveInBuild) == 0)
						{
							uniqueastetInSceneBundle[astet] = astetParent;

							if (astetParent != "resources")
							{
								if (!extToastetList.ContainsKey(ext))
								{
									extToastetList[ext] = new List();
								}
								extToastetList[ext].Add(astet);
							}
						}
					}
				}
			}
		}
	}

	private static bool DeploySceneBundles(List sceneList)
	{
		// Create Temp directory on local machine if it does not exist
		string tempDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "Temp");
		if (!Directory.Exists(tempDirectory))
		{
			Directory.CreateDirectory(tempDirectory);
		}
		string absoluteTempPath = Path.Combine(Path.Combine(Application.dataPath, ".."), tempDirectory);

		OVRBundleTool.PrintLog("Deploying scene bundles to device . . . ");

		OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
		if (adbTool.isReady)
		{
			DateTime transferStart = DateTime.Now;

			OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.TRANSFERRING);
			// Transfer all scene bundles that are relavent
			if (!TransferSceneBundles(adbTool, absoluteTempPath, externalSceneCache))
			{
				return false;
			}
			OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.DEPLOYED);

			// Create file to tell transition scene APK which scene to load and push it to the device
			string sceneLoadDataPath = Path.Combine(tempDirectory, OVRSceneLoader.sceneLoadDataName);
			if (File.Exists(sceneLoadDataPath))
			{
				File.Delete(sceneLoadDataPath);
			}

			StreamWriter writer = new StreamWriter(sceneLoadDataPath, true);
			// Write version and scene names
			long unixTime = (int)(DateTimeOffset.UtcNow.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
			writer.WriteLine(unixTime.ToString());
			for (int i = 0; i < sceneList.Count; i++)
			{
				writer.WriteLine(Path.GetFileNameWithoutExtension(sceneList[i].scenePath));
			}

			writer.Close();

			string absoluteSceneLoadDataPath = Path.Combine(absoluteTempPath, OVRSceneLoader.sceneLoadDataName);
			string[] pushCommand = { "-d push", "\"" + absoluteSceneLoadDataPath + "\"", "\"" + externalSceneCache + "\"" };
			string output, error;
			if (adbTool.RunCommand(pushCommand, null, out output, out error) == 0)
			{
				Debug.Log("[OVRBundleManager] Scene Load Data Pushed to Device.");
				OVRPlugin.SendEvent("oculus_bundle_tool", "transfer_bundle_time", (DateTime.Now - transferStart).TotalSeconds.ToString());
				return true;
			}
			OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
		}
		else
		{
			OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
		}
		return false;
	}

	private static bool TransferSceneBundles(OVRADBTool adbTool, string absoluteTempPath, string externalSceneCache)
	{
		List bundlesToTransfer = new List();
		List bundlesToDelete = new List();
		string manifestFilePath = externalSceneCache + "/" + BUNDLE_MANAGER_MASTER_BUNDLE;

		string[] pullManifestCommand = { "-d pull", "\"" + manifestFilePath + "\"", "\"" + absoluteTempPath + "\"" };

		string output, error;
		if (adbTool.RunCommand(pullManifestCommand, null, out output, out error) == 0)
		{
			// An existing manifest file was found on device. Load hashes and upload bundles that have changed hashes.
			Debug.Log("[OVRBundleManager] - Scene bundle manifest file found. Decoding changes . . .");

			// Load hashes from remote manifest
			astetBundle remoteBundle = astetBundle.LoadFromFile(Path.Combine(absoluteTempPath, BUNDLE_MANAGER_MASTER_BUNDLE));
			if (remoteBundle == null)
			{
				OVRBundleTool.PrintError("Failed to load remote astet bundle manifest file.");
				return false;
			}
			astetBundleManifest remoteManifest = remoteBundle.Loadastet("astetBundleManifest");

			Dictionary remoteBundleToHash = new Dictionary();
			if (remoteManifest != null)
			{
				string[] astetBundles = remoteManifest.GetAllastetBundles();
				foreach (string bundleName in astetBundles)
				{
					remoteBundleToHash[bundleName] = remoteManifest.GetastetBundleHash(bundleName);
				}
			}
			remoteBundle.Unload(true);

			// Load hashes from local manifest
			astetBundle localBundle = astetBundle.LoadFromFile(BUNDLE_MANAGER_OUTPUT_PATH + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE
					+ "\\" + BUNDLE_MANAGER_MASTER_BUNDLE);
			if (localBundle == null)
			{
				OVRBundleTool.PrintError("Failed to load local astet bundle manifest file.\n");
				return false;
			}
			astetBundleManifest localManifest = localBundle.Loadastet("astetBundleManifest");

			if (localManifest != null)
			{
				Hash128 zeroHash = new Hash128(0, 0, 0, 0);

				// Build a list of dirty bundles that will have to be transfered
				string relativeSceneBundlesPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
				bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, BUNDLE_MANAGER_MASTER_BUNDLE));
				string[] astetBundles = localManifest.GetAllastetBundles();
				foreach (string bundleName in astetBundles)
				{
					if (!remoteBundleToHash.ContainsKey(bundleName))
					{
						bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
					}
					else
					{
						if (remoteBundleToHash[bundleName] != localManifest.GetastetBundleHash(bundleName))
						{
							bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
						}
						remoteBundleToHash[bundleName] = zeroHash;
					}
				}

				OVRBundleTool.PrintLog(bundlesToTransfer.Count + " dirty bundle(s) will be transfered.\n");
			}
		}
		else
		{
			if (output.Contains("does not exist"))
			{
				// Fresh install of astet bundles, transfer all astet bundles
				OVRBundleTool.PrintLog("Manifest file not found. Transfering all bundles . . . ");

				string[] mkdirCommand = { "-d shell", "mkdir -p", "\"" + externalSceneCache + "\"" };
				if (adbTool.RunCommand(mkdirCommand, null, out output, out error) == 0)
				{
					string absoluteSceneBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."),
							Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE));

					string[] astetBundlePaths = Directory.GetFiles(absoluteSceneBundlePath);
					if (astetBundlePaths.Length != 0)
					{
						foreach (string path in astetBundlePaths)
						{
							if (!path.Contains(".manifest"))
							{
								bundlesToTransfer.Add(path);
							}
						}
					}
					else
					{
						OVRBundleTool.PrintError("Failed to locate scene bundles to transfer.");
						return false;
					}
				}
			}
		}

		// If any adb error occured during manifest calculation, print it and return false
		if (!string.IsNullOrEmpty(error) || output.Contains("error"))
		{
			OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
			return false;
		}

		// Transfer bundles to device
		DateTime transferStart = DateTime.Now;
		foreach (string bundle in bundlesToTransfer)
		{
			string absoluteBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), bundle);
			string[] pushBundleCommand = { "-d push", "\"" + absoluteBundlePath + "\"", "\"" + externalSceneCache + "\"" };
			adbTool.RunCommandAsync(pushBundleCommand, null);
		}
		Debug.Log("[OVRBundleManager] - Transfer took " + (DateTime.Now - transferStart).TotalSeconds + " seconds.");

		// Delete stale bundles on device
		if (bundlesToDelete.Count > 0)
		{
			foreach (string bundle in bundlesToDelete)
			{
				string bundlePath = externalSceneCache + "/" + bundle;
				string[] deleteBundleCommand = { "-d shell", "rm", "\"" + bundlePath + "\"" };
				adbTool.RunCommandAsync(deleteBundleCommand, null);
			}
			OVRBundleTool.PrintLog("Deleted " + bundlesToDelete.Count + " bundle(s) that were stale");
		}

		return true;
	}

	public static bool LaunchApplication()
	{
		OVRBundleTool.PrintLog("Launching Application . . . ");

		OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
		if (adbTool.isReady)
		{
			string output, error;
			string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
				+ GetTransitionApkOptionalIdentifier();
			string playerActivityName = "\"" + appPackagename + "/com.unity3d.player.UnityPlayerActivity\"";
			string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
			if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
			{
				OVRBundleTool.PrintSuccess();
				OVRBundleTool.PrintLog("App package " + appPackagename + " is launched.");
				return true;
			}

			string completeError = "Failed to launch application. Try launching it manually through the device.\n" + (string.IsNullOrEmpty(error) ? output : error);
			OVRBundleTool.PrintError(completeError);
		}
		else
		{
			OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
		}
		return false;
	}

	public static bool UninstallAPK()
	{
		OVRBundleTool.PrintLog("Uninstalling Application . . .");

		OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
		if (adbTool.isReady)
		{
			string output, error;
			string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
				+ GetTransitionApkOptionalIdentifier();
			string[] appStartCommand = { "-d shell", "pm uninstall", appPackagename };
			if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
			{
				OVRBundleTool.PrintSuccess();
				OVRBundleTool.PrintLog("App package " + appPackagename + " is uninstalled.");
				return true;
			}

			OVRBundleTool.PrintError("Failed to uninstall APK.");
		}
		else
		{
			OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
		}
		return false;
	}

	public static void DeleteRemoteastetBundles()
	{
		OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
		if (adbTool.isReady)
		{
			bool failure = false;
			string fileExistsError = "No such file or directory";
			OVRBundleTool.PrintLog("Deleting device bundles . . . ");
			string output, error;
			string[] deleteBundleCommand = { "-d shell", "rm -r", externalSceneCache };
			if (adbTool.RunCommand(deleteBundleCommand, null, out output, out error) != 0)
			{
				if (!(output.Contains(fileExistsError) || error.Contains(fileExistsError)))
				{
					failure = true;
				}
			}

			if (failure)
			{
				OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
				OVRBundleTool.PrintError("Failed to delete scene bundle cache directory.");
			}
			else
			{
				OVRBundleTool.PrintSuccess();
			}
		}
		else
		{
			OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
		}
	}

	public static void DeleteLocalastetBundles()
	{
		try
		{
			if (Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
			{
				OVRBundleTool.PrintLog("Deleting local bundles . . . ");
				Directory.Delete(BUNDLE_MANAGER_OUTPUT_PATH, true);
			}
		}
		catch (Exception e)
		{
			OVRBundleTool.PrintError(e.Message);
		}
		OVRBundleTool.PrintSuccess();
	}

	private static bool CheckForResources()
	{
		string[] resourceDirectories = Directory.GetDirectories("astets", "Resources", SearchOption.AllDirectories);
		return resourceDirectories.Length > 0;
	}

	private static string GetTransitionApkOptionalIdentifier()
	{
		string transitionApkOptionalIdentifier;
		// Check option value from editor UI
		if (OVRBundleTool.GetUseOptionalTransitionApkPackage())
		{
			// Append .transition to default app package name to optionally allow both
			// full build apk and transition apk to be installed on device 
			transitionApkOptionalIdentifier = ".transition";
		}
		else
		{
			transitionApkOptionalIdentifier = "";
		}
		return transitionApkOptionalIdentifier;
	}
}
#endif