static void BuildAndRun() { if (!PXR_ADBTool.GetInstance().CheckADBDevices()) { return; } apkOutputSuccessful = null; syncCancelToken = null; gradleBuildProcess = null; gradleTempExport = Path.Combine(Path.Combine(Application.dataPath, "../Temp"), "PXRGradleTempExport"); gradleExport = Path.Combine(Path.Combine(Application.dataPath, "../Temp"), "PXRGradleExport"); if (!Directory.Exists(gradleExport)) { Directory.CreateDirectory(gradleExport); } var buildResult = UnityBuildPlayer(); if (buildResult.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded) { applicationIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android); #if UNITY_2019_3_OR_NEWER productName = "launcher"; #else productName = Application.productName; #endif BuildRun(); } }
public static bool IsInstalledAPP() { if (!PXR_ADBTool.GetInstance().IsReady()) { return(false); } string matchedPackageList, error; var packageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android); string[] packageCheckCommand = new string[] { "-d shell pm list package", packageName }; if (PXR_ADBTool.GetInstance().RunCommand(packageCheckCommand, null, out matchedPackageList, out error) == 0) { if (string.IsNullOrEmpty(matchedPackageList)) { return(false); } if (!matchedPackageList.Contains("package:" + packageName + "\r\n")) { return(false); } string[] dumpPackageInfoCommand = new string[] { "-d shell dumpsys package", packageName }; string packageInfo; if (PXR_ADBTool.GetInstance().RunCommand(dumpPackageInfoCommand, null, out packageInfo, out error) == 0 && !string.IsNullOrEmpty(packageInfo) && packageInfo.Contains(SQP_APK_VERSION)) { return(true); } return(false); } return(false); }
static void OpenSceneQuickPreviewUI() { GetWindow <PXR_SceneQucikPreviewEW>(); PXR_ADBTool.GetInstance().CheckADBDevices(log => { if (!string.IsNullOrEmpty(log)) { PrintError(log); } }); EditorBuildSettings.sceneListChanged += PXR_BuildToolManager.GetScenesEnabled; }
static void BuildAndRun() { GetWindow(typeof(PXR_BuildAndRunEW)); showCancel = false; buildFailed = false; totalBuildTime = 0; InitializeProgressBar(NUM_BUILD_AND_RUN_STEPS); IncrementProgressBar("Exporting Unity Project . . ."); if (!PXR_ADBTool.GetInstance().CheckADBDevices(log => {})) { buildFailed = true; return; } apkOutputSuccessful = null; syncCancelToken = null; gradleBuildProcess = null; UnityEngine.Debug.Log("PXRBuild: Starting Unity build ..."); SetupDirectories(); // 1. Get scenes to build in Unity, and export gradle project var buildResult = UnityBuildPlayer(); if (buildResult.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded) { totalBuildTime += buildResult.summary.totalTime.TotalSeconds; // Set static variables so build thread has updated data showCancel = true; applicationIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android); #if UNITY_2019_3_OR_NEWER productName = "launcher"; #else productName = Application.productName; #endif BuildRun(); return; } else if (buildResult.summary.result == UnityEditor.Build.Reporting.BuildResult.Cancelled) { UnityEngine.Debug.Log("Build canceled."); } else { UnityEngine.Debug.Log("Build failed."); } buildFailed = true; }
public static bool RestartApp() { if (!PXR_ADBTool.GetInstance().IsReady()) { return(false); } string output, error; string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", PXR_PathHelper.GetPlayerActivityName() }; if (PXR_ADBTool.GetInstance().RunCommand(appStartCommand, null, out output, out error) == 0) { PXR_SceneQuickPreviewEW.PrintLog("App " + " Restart Success!", PXR_SceneQuickPreviewEW.LogType.Success); return(true); } string completeError = "PXRLog Failed to restart App. Try restarting it manually through the device.\n" + (string.IsNullOrEmpty(error) ? output : error); Debug.LogError(completeError); return(false); }
public static bool UninstallAPP() { PXR_SceneQuickPreviewEW.PrintLog("Uninstalling Application . . .", PXR_SceneQuickPreviewEW.LogType.Normal); if (!PXR_ADBTool.GetInstance().IsReady()) { return(false); } string output, error; string appPackageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android); string[] appStartCommand = { "-d shell", "pm uninstall", appPackageName }; if (PXR_ADBTool.GetInstance().RunCommand(appStartCommand, null, out output, out error) == 0) { PXR_SceneQuickPreviewEW.PrintLog("App package " + appPackageName + " is uninstalled.", PXR_SceneQuickPreviewEW.LogType.Success); return(true); } PXR_SceneQuickPreviewEW.PrintLog("Failed to uninstall APK.", PXR_SceneQuickPreviewEW.LogType.Error); return(false); }
void CancelGradleBuild() { Process cancelGradleProcess = new Process(); string arguments = "-Xmx1024m -classpath \"" + PXR_ADBTool.GetInstance().GetGradlePath() + "\" org.gradle.launcher.GradleMain --stop"; var processInfo = new System.Diagnostics.ProcessStartInfo { WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal, FileName = PXR_ADBTool.GetInstance().GetJDKPath(), Arguments = arguments, RedirectStandardInput = true, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, }; cancelGradleProcess.StartInfo = processInfo; cancelGradleProcess.EnableRaisingEvents = true; cancelGradleProcess.OutputDataReceived += new DataReceivedEventHandler( (s, e) => { if (e != null && e.Data != null && e.Data.Length != 0) { UnityEngine.Debug.LogFormat("Gradle: {0}", e.Data); } } ); apkOutputSuccessful = false; cancelGradleProcess.Start(); cancelGradleProcess.BeginOutputReadLine(); cancelGradleProcess.WaitForExit(); buildFailed = true; }
static void OpenSceneQuickPreviewUI() { GetWindow <PXR_SceneQuickPreviewEW>(); PXR_ADBTool.GetInstance().CheckADBDevices(); EditorBuildSettings.sceneListChanged += PXR_BuildToolManager.GetScenesEnabled; }
public static void BuildScenes(bool forceRestart) { if (!PXR_ADBTool.GetInstance().IsReady()) { return; } GetScenesEnabled(); remoteSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android) + "/cache/scenes"; Dictionary <string, string> assetInSceneBundle = new Dictionary <string, string>(); List <AssetBundleBuild> assetBundleBuilds = new List <AssetBundleBuild>(); Dictionary <string, List <string> > extToAssetList = new Dictionary <string, List <string> >(); string[] resDirectories = Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories).ToArray(); if (resDirectories.Length > 0) { string[] resAssetPaths = AssetDatabase.FindAssets("", resDirectories).Select(x => AssetDatabase.GUIDToAssetPath(x)).ToArray(); ProcessAssets(resAssetPaths, "resources", ref assetInSceneBundle, ref extToAssetList); AssetBundleBuild resBundle = new AssetBundleBuild(); resBundle.assetNames = assetInSceneBundle.Keys.ToArray(); resBundle.assetBundleName = PXR_SQPLoader.RESOURCE_BUNDLE_NAME; assetBundleBuilds.Add(resBundle); } foreach (var scene in buildSceneInfoList) { string[] assetDependencies = AssetDatabase.GetDependencies(scene.scenePath); ProcessAssets(assetDependencies, scene.sceneName, ref assetInSceneBundle, ref extToAssetList); string[] sceneAsset = new string[1] { scene.scenePath }; AssetBundleBuild sceneBuild = new AssetBundleBuild(); sceneBuild.assetBundleName = "scene_" + scene.sceneName; sceneBuild.assetNames = sceneAsset; assetBundleBuilds.Add(sceneBuild); } foreach (string ext in extToAssetList.Keys) { int assetCount = extToAssetList[ext].Count; int numChunks = (assetCount + BUNDLE_CHUNK_SIZE - 1) / BUNDLE_CHUNK_SIZE; for (int i = 0; i < numChunks; i++) { List <string> assetChunkList; if (i == numChunks - 1) { int size = BUNDLE_CHUNK_SIZE - (numChunks * BUNDLE_CHUNK_SIZE - assetCount); assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, size); } else { assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, BUNDLE_CHUNK_SIZE); } AssetBundleBuild build = new AssetBundleBuild(); build.assetBundleName = "asset_" + ext + i; build.assetNames = assetChunkList.ToArray(); assetBundleBuilds.Add(build); } } // Build asset bundles BuildPipeline.BuildAssetBundles(PXR_DirectorySyncer.CreateDirectory(SQP_BUNDLE_PATH, SQP_SCENE_BUNDLE), assetBundleBuilds.ToArray(), BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.Android); string tempDirectory = PXR_DirectorySyncer.CreateDirectory(SQP_BUNDLE_PATH, "Temp"); string absoluteTempPath = Path.Combine(Path.Combine(Application.dataPath, ".."), tempDirectory); if (!PullSceneBundles(absoluteTempPath, remoteSceneCache)) { return; } string sceneLoadDataPath = Path.Combine(tempDirectory, SCENE_LOAD_DATA_NAME); if (File.Exists(sceneLoadDataPath)) { File.Delete(sceneLoadDataPath); } StreamWriter writer = new StreamWriter(sceneLoadDataPath, true); long unixTime = (int)(DateTimeOffset.UtcNow.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds; writer.WriteLine(unixTime.ToString()); for (int i = 0; i < buildSceneInfoList.Count; i++) { writer.WriteLine(Path.GetFileNameWithoutExtension(buildSceneInfoList[i].scenePath)); } writer.Close(); string absoluteSceneLoadDataPath = Path.Combine(absoluteTempPath, SCENE_LOAD_DATA_NAME); string[] pushCommand = { "-d push", "\"" + absoluteSceneLoadDataPath + "\"", "\"" + remoteSceneCache + "\"" }; string output, error; if (PXR_ADBTool.GetInstance().RunCommand(pushCommand, null, out output, out error) != 0) { PXR_SceneQuickPreviewEW.PrintLog(string.IsNullOrEmpty(error) ? output : error, PXR_SceneQuickPreviewEW.LogType.Error); return; } if (!IsInstalledAPP()) { InstallAPP(); } if (forceRestart) { RestartApp(); return; } PXR_SceneQuickPreviewEW.PrintLog("Build Scenes.", PXR_SceneQuickPreviewEW.LogType.Success); }
private static bool PullSceneBundles(string absoluteTempPath, string externalSceneCache) { List <string> bundlesToTransfer = new List <string>(); string manifestFilePath = externalSceneCache + "/" + SQP_SCENE_BUNDLE; string[] pullManifestCommand = { "-d pull", "\"" + manifestFilePath + "\"", "\"" + absoluteTempPath + "\"" }; string output, error; if (PXR_ADBTool.GetInstance().RunCommand(pullManifestCommand, null, out output, out error) == 0) { AssetBundle remoteBundle = AssetBundle.LoadFromFile(Path.Combine(absoluteTempPath, SQP_SCENE_BUNDLE)); if (remoteBundle == null) { PXR_SceneQuickPreviewEW.PrintLog("Failed to load remote asset bundle manifest file.", PXR_SceneQuickPreviewEW.LogType.Error); return(false); } AssetBundleManifest remoteManifest = remoteBundle.LoadAsset <AssetBundleManifest>("AssetBundleManifest"); Dictionary <string, Hash128> remoteBundleToHash = new Dictionary <string, Hash128>(); if (remoteManifest != null) { string[] assetBundles = remoteManifest.GetAllAssetBundles(); foreach (string bundleName in assetBundles) { remoteBundleToHash[bundleName] = remoteManifest.GetAssetBundleHash(bundleName); } } remoteBundle.Unload(true); AssetBundle localBundle = AssetBundle.LoadFromFile(SQP_BUNDLE_PATH + "\\" + SQP_SCENE_BUNDLE + "\\" + SQP_SCENE_BUNDLE); if (localBundle == null) { PXR_SceneQuickPreviewEW.PrintLog("Failed to load local asset bundle manifest file.", PXR_SceneQuickPreviewEW.LogType.Error); return(false); } AssetBundleManifest localManifest = localBundle.LoadAsset <AssetBundleManifest>("AssetBundleManifest"); if (localManifest != null) { Hash128 zeroHash = new Hash128(0, 0, 0, 0); string relativeSceneBundlesPath = Path.Combine(SQP_BUNDLE_PATH, SQP_SCENE_BUNDLE); bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, SQP_SCENE_BUNDLE)); string[] assetBundles = localManifest.GetAllAssetBundles(); foreach (string bundleName in assetBundles) { if (!remoteBundleToHash.ContainsKey(bundleName)) { bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName)); } else { if (remoteBundleToHash[bundleName] != localManifest.GetAssetBundleHash(bundleName)) { bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName)); } remoteBundleToHash[bundleName] = zeroHash; } } PXR_SceneQuickPreviewEW.PrintLog(bundlesToTransfer.Count + " dirty bundle(s) will be transferred.\n", PXR_SceneQuickPreviewEW.LogType.Normal); } } else { if (output.Contains("does not exist") || output.Contains("No such file or directory")) { PXR_SceneQuickPreviewEW.PrintLog("Manifest file not found. Transferring all bundles . . . ", PXR_SceneQuickPreviewEW.LogType.Normal); string[] mkdirCommand = { "-d shell", "mkdir -p", "\"" + externalSceneCache + "\"" }; if (PXR_ADBTool.GetInstance().RunCommand(mkdirCommand, null, out output, out error) == 0) { string absoluteSceneBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), Path.Combine(SQP_BUNDLE_PATH, SQP_SCENE_BUNDLE)); string[] assetBundlePaths = Directory.GetFiles(absoluteSceneBundlePath); if (assetBundlePaths.Length == 0) { PXR_SceneQuickPreviewEW.PrintLog("Failed to locate scene bundles to transfer.", PXR_SceneQuickPreviewEW.LogType.Error); return(false); } foreach (string path in assetBundlePaths) { if (!path.Contains(".manifest")) { bundlesToTransfer.Add(path); } } } } } if (!string.IsNullOrEmpty(error) || output.Contains("error")) { PXR_SceneQuickPreviewEW.PrintLog(string.IsNullOrEmpty(error) ? output : error, PXR_SceneQuickPreviewEW.LogType.Error); return(false); } foreach (string bundle in bundlesToTransfer) { string absoluteBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), bundle); string[] pushBundleCommand = { "-d push", "\"" + absoluteBundlePath + "\"", "\"" + externalSceneCache + "\"" }; PXR_ADBTool.GetInstance().RunCommandAsync(pushBundleCommand, null); } return(true); }
public static bool DeployAPK() { if (!PXR_ADBTool.GetInstance().IsReady()) { return(false); } string gradleExportFolder = Path.Combine(Path.Combine(gradleExport, productName), "build\\outputs\\apk\\debug"); gradleExportFolder = gradleExportFolder.Replace("/", "\\"); if (!Directory.Exists(gradleExportFolder)) { return(false); } var apkPathLocal = Path.Combine(gradleExportFolder, productName + "-debug.apk"); if (!File.Exists(apkPathLocal)) { return(false); } string output, error; string[] mkdirCommand = { "-d shell", "mkdir -p", "/data/local/tmp" }; if (PXR_ADBTool.GetInstance().RunCommand(mkdirCommand, null, out output, out error) != 0) { return(false); } var timerStart = DateTime.Now; string[] pushCommand = { "-d push", "\"" + apkPathLocal + "\"", "/data/local/tmp" }; if (PXR_ADBTool.GetInstance().RunCommand(pushCommand, null, out output, out error) != 0) { return(false); } TimeSpan pushTime = System.DateTime.Now - timerStart; bool trivialPush = pushTime.TotalSeconds < 4.0f; long? apkSize = (trivialPush ? (long?)null : new System.IO.FileInfo(apkPathLocal).Length); double? transferSpeed = (apkSize / pushTime.TotalSeconds) / 1048576; bool informLog = transferSpeed.HasValue && transferSpeed.Value < 25.0f; string apkPath = "/data/local/tmp" + "/" + productName + "-debug.apk"; apkPath = apkPath.Replace(" ", "\\ "); string[] installCommand = { "-d shell", "pm install -r", apkPath }; timerStart = DateTime.Now; if (PXR_ADBTool.GetInstance().RunCommand(installCommand, null, out output, out error) != 0) { return(false); } TimeSpan installTime = System.DateTime.Now - timerStart; #if UNITY_2019_3_OR_NEWER string playerActivityName = "\"" + applicationIdentifier + "/com.unity3d.player.UnityPlayerActivity\""; #else string playerActivityName = "\"" + applicationIdentifier + "/" + applicationIdentifier + ".UnityPlayerActivity\""; #endif string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName }; if (PXR_ADBTool.GetInstance().RunCommand(appStartCommand, null, out output, out error) != 0) { return(false); } UnityEngine.Debug.Log("PXRLog Application Start Success"); if (informLog) { return(true); } return(false); }
private static bool BuildGradleProject() { gradleBuildProcess = new Process(); string arguments = "-Xmx4096m -classpath \"" + PXR_ADBTool.GetInstance().GetGradlePath() + "\" org.gradle.launcher.GradleMain assembleDebug -x validateSigningDebug --profile"; #if UNITY_2019_3_OR_NEWER var gradleProjectPath = gradleExport; #else var gradleProjectPath = Path.Combine(gradleExport, productName); #endif var processInfo = new ProcessStartInfo { WorkingDirectory = gradleProjectPath, WindowStyle = ProcessWindowStyle.Normal, FileName = PXR_ADBTool.GetInstance().GetJDKPath(), Arguments = arguments, RedirectStandardInput = true, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, }; gradleBuildProcess.StartInfo = processInfo; gradleBuildProcess.EnableRaisingEvents = true; DateTime gradleStartTime = System.DateTime.Now; DateTime gradleEndTime = System.DateTime.MinValue; gradleBuildProcess.Exited += (s, e) => { UnityEngine.Debug.Log("PXRLog Gradle: Exited"); }; gradleBuildProcess.OutputDataReceived += new DataReceivedEventHandler( (s, e) => { if (e != null && e.Data != null && e.Data.Length != 0 && (e.Data.Contains("BUILD") || e.Data.StartsWith("See the profiling report at:"))) { UnityEngine.Debug.LogFormat("PXRLog Gradle: {0}", e.Data); if (e.Data.Contains("SUCCESSFUL")) { UnityEngine.Debug.LogFormat("PXRLog APK Build Completed: {0}", Path.Combine(Path.Combine(gradleProjectPath, "build\\outputs\\apk\\debug"), productName + "-debug.apk").Replace("/", "\\")); if (!apkOutputSuccessful.HasValue) { apkOutputSuccessful = true; } gradleEndTime = System.DateTime.Now; } else if (e.Data.Contains("FAILED")) { apkOutputSuccessful = false; } } } ); gradleBuildProcess.ErrorDataReceived += new DataReceivedEventHandler( (s, e) => { if (e != null && e.Data != null && e.Data.Length != 0) { UnityEngine.Debug.LogErrorFormat("Gradle: {0}", e.Data); } apkOutputSuccessful = false; } ); gradleBuildProcess.Start(); gradleBuildProcess.BeginOutputReadLine(); gradleBuildProcess.WaitForExit(); Stopwatch timeout = new Stopwatch(); timeout.Start(); while (apkOutputSuccessful == null) { if (timeout.ElapsedMilliseconds > 5000) { UnityEngine.Debug.LogError("PXRLog Gradle has exited unexpectedly."); apkOutputSuccessful = false; } Thread.Sleep(100); } return(apkOutputSuccessful.HasValue && apkOutputSuccessful.Value); }
public static bool DeployAPK() { // Create new instance of ADB Tool if (!PXR_ADBTool.GetInstance().IsReady()) { UnityEngine.Debug.LogError("Could not find the ADB executable in the specified Android SDK directory."); return(false); } string apkPathLocal; string gradleExportFolder = Path.Combine(Path.Combine(gradleExport, productName), "build\\outputs\\apk\\debug"); // Check to see if gradle output directory exists gradleExportFolder = gradleExportFolder.Replace("/", "\\"); if (!Directory.Exists(gradleExportFolder)) { UnityEngine.Debug.LogError("Could not find the gradle project at the expected path: " + gradleExportFolder); return(false); } // Search for output APK in gradle output directory apkPathLocal = Path.Combine(gradleExportFolder, productName + "-debug.apk"); if (!File.Exists(apkPathLocal)) { UnityEngine.Debug.LogError(string.Format("Could not find {0} in the gradle project.", productName + "-debug.apk")); return(false); } string output, error; DateTime timerStart; // Ensure that the temp directory is on the device by making it IncrementProgressBar("Making Temp directory on device"); string[] mkdirCommand = { "-d shell", "mkdir -p", REMOTE_APK_PATH }; if (PXR_ADBTool.GetInstance().RunCommand(mkdirCommand, null, out output, out error) != 0) { return(false); } // Push APK to device, also time how long it takes timerStart = DateTime.Now; IncrementProgressBar("Pushing APK to device . . ."); string[] pushCommand = { "-d push", "\"" + apkPathLocal + "\"", REMOTE_APK_PATH }; if (PXR_ADBTool.GetInstance().RunCommand(pushCommand, null, out output, out error) != 0) { return(false); } // Calculate the transfer speed and determine if user is using USB 2.0 or 3.0 TimeSpan pushTime = System.DateTime.Now - timerStart; bool trivialPush = pushTime.TotalSeconds < TRANSFER_SPEED_CHECK_THRESHOLD; long? apkSize = (trivialPush ? (long?)null : new System.IO.FileInfo(apkPathLocal).Length); double? transferSpeed = (apkSize / pushTime.TotalSeconds) / BYTES_TO_MEGABYTES; bool informLog = transferSpeed.HasValue && transferSpeed.Value < USB_TRANSFER_SPEED_THRES; UnityEngine.Debug.Log("PXR ADB Tool: Push Success"); // Install the APK package on the device IncrementProgressBar("Installing APK . . ."); string apkPath = REMOTE_APK_PATH + "/" + productName + "-debug.apk"; apkPath = apkPath.Replace(" ", "\\ "); string[] installCommand = { "-d shell", "pm install -r", apkPath }; timerStart = DateTime.Now; if (PXR_ADBTool.GetInstance().RunCommand(installCommand, null, out output, out error) != 0) { return(false); } TimeSpan installTime = System.DateTime.Now - timerStart; UnityEngine.Debug.Log("PXR ADB Tool: Install Success"); // Start the application on the device IncrementProgressBar("Launching application on device . . ."); #if UNITY_2019_3_OR_NEWER string playerActivityName = "\"" + applicationIdentifier + "/com.unity3d.player.UnityPlayerActivity\""; #else string playerActivityName = "\"" + applicationIdentifier + "/" + applicationIdentifier + ".UnityPlayerActivity\""; #endif string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName }; if (PXR_ADBTool.GetInstance().RunCommand(appStartCommand, null, out output, out error) != 0) { return(false); } UnityEngine.Debug.Log("PXR ADB Tool: Application Start Success"); // Send back metrics on push and install steps IncrementProgressBar("Success!"); // If the user is using a USB 2.0 cable, inform them about improved transfer speeds and estimate time saved if (informLog) { var usb3Time = apkSize.Value / (USB_3_TRANSFER_SPEED * BYTES_TO_MEGABYTES); UnityEngine.Debug.Log(string.Format("Build has detected slow transfer speeds. A USB 3.0 cable is recommended to reduce the time it takes to deploy your project by approximatly {0:0.0} seconds", pushTime.TotalSeconds - usb3Time)); return(true); } return(false); }