public CameraConfigDepthSensorUsage GetDepthSensorUsage(IntPtr cameraConfigHandle) { int depthSensorUsage = (int)CameraConfigDepthSensorUsage.DoNotUse; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access ARCamera DepthSensorUsage"); return((CameraConfigDepthSensorUsage)depthSensorUsage); } ExternApi.ArCameraConfig_getDepthSensorUsage( _nativeSession.SessionHandle, cameraConfigHandle, ref depthSensorUsage); return((CameraConfigDepthSensorUsage)depthSensorUsage); }
public void GetImageDimensions(IntPtr cameraConfigHandle, out int width, out int height) { width = 0; height = 0; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access ARCamera image dimensions"); return; } ExternApi.ArCameraConfig_getImageDimensions( m_NativeSession.SessionHandle, cameraConfigHandle, ref width, ref height); }
public void CreateSession(ARCoreSession sessionComponent) { sessionComponent.StartCoroutine(InstantPreviewManager.InitializeIfNeeded()); if (SessionComponent != null) { Debug.LogError("Multiple ARCore session components cannot exist in the scene. " + "Destroying the newest."); GameObject.Destroy(sessionComponent); return; } SessionComponent = sessionComponent; }
public int AddAugmentedImageAtRuntime(IntPtr augmentedImageDatabaseHandle, string name, AugmentedImageSrc imageSrc, float width) { int outIndex = -1; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage( "add images to Augmented Image database"); return(outIndex); } GCHandle grayscaleBytesHandle = ConvertTextureToGrayscaleBytes(imageSrc); if (grayscaleBytesHandle.AddrOfPinnedObject() == IntPtr.Zero) { return(-1); } ApiArStatus status; if (width > 0) { status = ExternApi.ArAugmentedImageDatabase_addImageWithPhysicalSize( _nativeSession.SessionHandle, augmentedImageDatabaseHandle, name, grayscaleBytesHandle.AddrOfPinnedObject(), imageSrc._width, imageSrc._height, imageSrc._width, width, ref outIndex); } else { status = ExternApi.ArAugmentedImageDatabase_addImage( _nativeSession.SessionHandle, augmentedImageDatabaseHandle, name, grayscaleBytesHandle.AddrOfPinnedObject(), imageSrc._width, imageSrc._height, imageSrc._width, ref outIndex); } if (grayscaleBytesHandle.IsAllocated) { grayscaleBytesHandle.Free(); } if (status != ApiArStatus.Success) { Debug.LogWarningFormat( "Failed to add aumented image at runtime with status {0}", status); return(-1); } return(outIndex); }
public void GetFpsRange(IntPtr cameraConfigHandle, out int minFps, out int maxFps) { minFps = 0; maxFps = 0; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access ARCamera FpsRange"); return; } ExternApi.ArCameraConfig_getFpsRange( _nativeSession.SessionHandle, cameraConfigHandle, ref minFps, ref maxFps); }
public void CreateSession(ARCoreSession session) { session.StartCoroutine(InstantPreviewManager.InitializeIfNeeded()); if (m_SessionComponent != null) { //Debug.LogError("Multiple session components cannot exist in the scene. " + //"Destroying the newest."); //GameObject.Destroy(session); //return; } m_SessionComponent = session; EnableSession(); }
public ApiCameraConfigFacingDirection GetFacingDirection(IntPtr cameraConfigHandle) { ApiCameraConfigFacingDirection direction = ApiCameraConfigFacingDirection.Back; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access ARCamera facing direction"); return(direction); } ExternApi.ArCameraConfig_getFacingDirection( m_NativeSession.SessionHandle, cameraConfigHandle, ref direction); return(direction); }
public LostTrackingReason GetLostTrackingReason(IntPtr cameraHandle) { if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("determine tracking failure " + "reasons"); return(LostTrackingReason.None); } ApiTrackingFailureReason apiTrackingFailureReason = ApiTrackingFailureReason.None; ExternApi.ArCamera_getTrackingFailureReason(m_NativeSession.SessionHandle, cameraHandle, ref apiTrackingFailureReason); return(apiTrackingFailureReason.ToLostTrackingReason()); }
public CameraConfig GetCameraConfig() { IntPtr cameraConfigHandle = m_NativeSession.CameraConfigApi.Create(); if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access camera config"); return(new CameraConfig()); } ExternApi.ArSession_getCameraConfig(m_NativeSession.SessionHandle, cameraConfigHandle); CameraConfig currentCameraConfig = _CreateCameraConfig(cameraConfigHandle); m_NativeSession.CameraConfigApi.Destroy(cameraConfigHandle); return(currentCameraConfig); }
private void _SetConfiguration(ARCoreSessionConfig config) { // There is no configuration to set. if (config == null) { return; } // The configuration has not been updated. if (m_CachedConfig != null && config.Equals(m_CachedConfig) && (config.AugmentedImageDatabase == null || !config.AugmentedImageDatabase.m_IsDirty) && !ExperimentManager.Instance.IsConfigurationDirty) { return; } if (InstantPreviewManager.IsProvidingPlatform) { if (config.LightEstimationMode != LightEstimationMode.Disabled) { InstantPreviewManager.LogLimitedSupportMessage("enable 'Light Estimation'"); config.LightEstimationMode = LightEstimationMode.Disabled; } if (config.AugmentedImageDatabase != null) { InstantPreviewManager.LogLimitedSupportMessage("enable 'Augmented Images'"); config.AugmentedImageDatabase = null; } if (config.AugmentedFaceMode == AugmentedFaceMode.Mesh) { InstantPreviewManager.LogLimitedSupportMessage("enable 'Augmented Faces'"); config.AugmentedFaceMode = AugmentedFaceMode.Disabled; } } var prestoConfig = new ApiPrestoConfig(config); ExternApi.ArPresto_setConfiguration(ref prestoConfig); m_CachedConfig = ScriptableObject.CreateInstance <ARCoreSessionConfig>(); m_CachedConfig.CopyFrom(config); }
private void _UpdateTextureIfNeeded() { // If running in editor, updates background texture from Instant Preview only. Texture2D previewBackgroundTexture = BackgroundTexture; if (InstantPreviewManager.UpdateBackgroundTextureIfNeeded(ref previewBackgroundTexture)) { BackgroundTexture = previewBackgroundTexture; return; } IntPtr frameHandle = IntPtr.Zero; ExternApi.ArPresto_getFrame(ref frameHandle); int backgroundTextureId = ExternApi.ArCoreUnity_getBackgroundTextureId(); if (frameHandle == IntPtr.Zero) { // This prevents using a texture that has not been filled out by ARCore. return; } else if (backgroundTextureId == -1) { return; } else if (BackgroundTexture != null && BackgroundTexture.GetNativeTexturePtr().ToInt32() == backgroundTextureId) { return; } else if (BackgroundTexture == null) { // The Unity-cached size and format of the texture (0x0, ARGB) is not the // actual format of the texture. This is okay because the texture is not // accessed by pixels, it is accessed with UV coordinates. BackgroundTexture = Texture2D.CreateExternalTexture(0, 0, TextureFormat.ARGB32, false, false, new IntPtr(backgroundTextureId)); return; } BackgroundTexture.UpdateExternalTexture(new IntPtr(backgroundTextureId)); }
public AsyncTask <ApkAvailabilityStatus> CheckApkAvailability() { Action <ApkAvailabilityStatus> onComplete; AsyncTask <ApkAvailabilityStatus> task = new AsyncTask <ApkAvailabilityStatus>(out onComplete); if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("determine ARCore APK " + "availability"); return(task); } ExternApi.ArPresto_checkApkAvailability(_checkApkAvailabilityResultCallback, IntPtr.Zero); _pendingAvailabilityCheckCallbacks.Add(onComplete); return(task); }
public AsyncTask <ApkInstallationStatus> RequestApkInstallation(bool userRequested) { Action <ApkInstallationStatus> onComplete; AsyncTask <ApkInstallationStatus> task = new AsyncTask <ApkInstallationStatus>(out onComplete); if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("request installation of ARCore " + "APK"); return(task); } ExternApi.ArPresto_requestApkInstallation(userRequested, _requestApkInstallationResultCallback, IntPtr.Zero); _pendingInstallationRequestCallbacks.Add(onComplete); return(task); }
private void _SetCameraDirection(DeviceCameraDirection cameraDirection) { // The camera direction has not changed. if (m_CachedCameraDirection.HasValue && m_CachedCameraDirection.Value == cameraDirection) { return; } if (InstantPreviewManager.IsProvidingPlatform && cameraDirection == DeviceCameraDirection.BackFacing) { return; } else if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("enable front-facing (selfie) camera"); m_CachedCameraDirection = DeviceCameraDirection.BackFacing; if (SessionComponent != null) { SessionComponent.DeviceCameraDirection = DeviceCameraDirection.BackFacing; } return; } if (OnSessionSetEnabled != null) { OnSessionSetEnabled(false); } var apiCameraDirection = cameraDirection == DeviceCameraDirection.BackFacing ? ApiPrestoDeviceCameraDirection.BackFacing : ApiPrestoDeviceCameraDirection.FrontFacing; ExternApi.ArPresto_setDeviceCameraDirection(apiCameraDirection); m_CachedCameraDirection = cameraDirection; if (OnSessionSetEnabled != null) { OnSessionSetEnabled(true); } }
/// <summary> /// Coroutine method that communicates to the Instant Preview plugin /// every frame. /// /// If not running in the editor, this does nothing. /// </summary> /// <returns>Enumerator for a coroutine that updates Instant Preview /// every frame.</returns> public static IEnumerator InitializeIfNeeded() { // Terminates if not running in editor. if (!Application.isEditor) { yield break; } // User may have explicitly disabled Instant Preview. if (ARCoreProjectSettings.Instance != null && !ARCoreProjectSettings.Instance.IsInstantPreviewEnabled) { yield break; } var adbPath = InstantPreviewManager.GetAdbPath(); if (adbPath == null) { Debug.LogError("Instant Preview requires your Unity Android SDK path to be set. Please set it under " + "Preferences/External Tools/Android. You may need to install the Android SDK first."); yield break; } else if (!File.Exists(adbPath)) { Debug.LogErrorFormat("adb not found at \"{0}\". Please add adb to your SDK path and restart the Unity editor.", adbPath); yield break; } string localVersion; if (!StartServer(adbPath, out localVersion)) { yield break; } yield return(InstallApkAndRunIfConnected(adbPath, localVersion)); yield return(UpdateLoop()); }
private bool SetCameraDirection(DeviceCameraDirection cameraDirection) { // The camera direction has not changed. if (_cachedCameraDirection.HasValue && _cachedCameraDirection.Value == cameraDirection) { return(false); } if (InstantPreviewManager.IsProvidingPlatform && cameraDirection == DeviceCameraDirection.BackFacing) { return(false); } else if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage( "enable front-facing (selfie) camera"); _cachedCameraDirection = DeviceCameraDirection.BackFacing; if (SessionComponent != null) { SessionComponent.DeviceCameraDirection = DeviceCameraDirection.BackFacing; } return(false); } _cachedCameraDirection = cameraDirection; ApiPrestoStatus prestoStatus = ApiPrestoStatus.Uninitialized; ExternApi.ArPresto_getStatus(ref prestoStatus); if (prestoStatus == ApiPrestoStatus.ErrorInvalidCameraConfig) { // if the session is paused by invalid camera configuration, // attempt to recover by changing the camera direction: OnBeforeResumeSession(_cachedSessionHandle); } return(true); }
private void _EarlyUpdate() { AsyncTask.OnUpdate(); _UpdateTextureIfNeeded(); if (m_SessionComponent != null) { var config = m_SessionComponent.SessionConfig; if (config != null) { ExternApi.ArPresto_setConfiguration(new ApiPrestoConfig(config)); } } if (m_NativeSession != null) { m_NativeSession.SessionApi.SetDisplayGeometry( Screen.orientation, Screen.width, Screen.height); m_NativeSession.OnUpdate(); } InstantPreviewManager.OnEarlyUpdate(m_SessionComponent); }
public CameraIntrinsics GetTextureIntrinsics(IntPtr cameraHandle) { IntPtr cameraIntrinsicsHandle = IntPtr.Zero; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access GPU texture intrinsics"); return(new CameraIntrinsics()); } ExternApi.ArCameraIntrinsics_create( m_NativeSession.SessionHandle, ref cameraIntrinsicsHandle); ExternApi.ArCamera_getTextureIntrinsics( m_NativeSession.SessionHandle, cameraHandle, cameraIntrinsicsHandle); CameraIntrinsics textureIntrinsics = _GetCameraIntrinsicsFromHandle(cameraIntrinsicsHandle); ExternApi.ArCameraIntrinsics_destroy(cameraIntrinsicsHandle); return(textureIntrinsics); }
public Int32 AddImageAtRuntime( IntPtr databaseHandle, string name, Texture2D image, float width) { Int32 outIndex = -1; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("add images to Augmented Image " + "database"); return(outIndex); } GCHandle grayscaleBytesHandle = _ConvertTextureToGrayscaleBytes(image); if (grayscaleBytesHandle.AddrOfPinnedObject() == IntPtr.Zero) { return(-1); } ApiArStatus status = ExternApi.ArPrestoAugmentedImageDatabase_addImageAtRuntime( databaseHandle, name, grayscaleBytesHandle.AddrOfPinnedObject(), image.width, image.height, image.width, width, ref outIndex); if (grayscaleBytesHandle.IsAllocated) { grayscaleBytesHandle.Free(); } if (status != ApiArStatus.Success) { Debug.LogWarningFormat( "Failed to add aumented image at runtime with status {0}", status); return(-1); } return(outIndex); }
public bool GetAllCameraMetadataTags( IntPtr cameraMetadataHandle, List <CameraMetadataTag> resultList) { IntPtr ndkMetadataHandle = IntPtr.Zero; if (InstantPreviewManager.IsProvidingPlatform) { InstantPreviewManager.LogLimitedSupportMessage("access camera metadata tags"); return(false); } ExternApi.ArImageMetadata_getNdkCameraMetadata(m_NativeSession.SessionHandle, cameraMetadataHandle, ref ndkMetadataHandle); IntPtr tagsHandle = IntPtr.Zero; int tagsCount = 0; NdkCameraStatus status = ExternApi.ACameraMetadata_getAllTags( ndkMetadataHandle, ref tagsCount, ref tagsHandle); if (status != NdkCameraStatus.Ok) { ARDebug.LogErrorFormat( "ACameraMetadata_getAllTags error with native camera error code: {0}", status); return(false); } for (int i = 0; i < tagsCount; i++) { resultList.Add((CameraMetadataTag)Marshal.PtrToStructure( MarshalingHelper.GetPtrToUnmanagedArrayElement <int>(tagsHandle, i), typeof(int))); } return(true); }
private void _OnEarlyUpdate() { _SetCameraTextureName(); // Update session activity before EarlyUpdate. if (m_HaveDisableToEnableTransition) { _SetSessionEnabled(false); _SetSessionEnabled(true); m_HaveDisableToEnableTransition = false; // Avoid firing session enable event twice. if (m_DesiredSessionState.HasValue && m_DesiredSessionState.Value) { m_DesiredSessionState = null; } } if (m_DesiredSessionState.HasValue) { _SetSessionEnabled(m_DesiredSessionState.Value); m_DesiredSessionState = null; } // Perform updates before calling ArPresto_update. if (SessionComponent != null) { IntPtr previousSession = IntPtr.Zero; ExternApi.ArPresto_getSession(ref previousSession); if (UpdateSessionFeatures != null) { UpdateSessionFeatures(); } _SetCameraDirection(SessionComponent.DeviceCameraDirection); IntPtr currentSession = IntPtr.Zero; ExternApi.ArPresto_getSession(ref currentSession); // Fire the session enabled event when the underlying session has been changed // due to session feature update(camera direction etc). if (previousSession != currentSession) { _FireOnSessionSetEnabled(false); _FireOnSessionSetEnabled(true); } // Validate and convert the SessionConfig to a Instant Preview supported config by // logging and disabling limited supported features. if (InstantPreviewManager.IsProvidingPlatform && SessionComponent.SessionConfig != null && !InstantPreviewManager.ValidateSessionConfig(SessionComponent.SessionConfig)) { // A new SessionConfig object will be created based on the original // SessionConfig with all limited support features disabled. SessionComponent.SessionConfig = InstantPreviewManager.GenerateInstantPreviewSupportedConfig( SessionComponent.SessionConfig); } _UpdateConfiguration(SessionComponent.SessionConfig); } _UpdateDisplayGeometry(); // Update ArPresto and potentially ArCore. ExternApi.ArPresto_update(); if (SystemInfo.graphicsMultiThreaded && !InstantPreviewManager.IsProvidingPlatform) { // Synchronize render thread with update call. ExternApi.ARCoreRenderingUtils_CreatePostUpdateFence(); } SessionStatus previousSessionStatus = SessionStatus; // Get state information from ARPresto. ApiPrestoStatus prestoStatus = ApiPrestoStatus.Uninitialized; ExternApi.ArPresto_getStatus(ref prestoStatus); SessionStatus = prestoStatus.ToSessionStatus(); LostTrackingReason = LostTrackingReason.None; if (NativeSession != null && SessionStatus == SessionStatus.LostTracking) { var cameraHandle = NativeSession.FrameApi.AcquireCamera(); LostTrackingReason = NativeSession.CameraApi.GetLostTrackingReason(cameraHandle); NativeSession.CameraApi.Release(cameraHandle); } // If the current status is an error, check if the SessionStatus error state changed. if (SessionStatus.IsError() && previousSessionStatus.IsError() != SessionStatus.IsError()) { // Disable internal session bits so we properly pause the session due to error. _FireOnSessionSetEnabled(false); m_DisabledSessionOnErrorState = true; } else if (SessionStatus.IsValid() && m_DisabledSessionOnErrorState) { if (SessionComponent.enabled) { _FireOnSessionSetEnabled(true); } m_DisabledSessionOnErrorState = false; } // Get the current session from presto and note if it has changed. IntPtr sessionHandle = IntPtr.Zero; ExternApi.ArPresto_getSession(ref sessionHandle); IsSessionChangedThisFrame = m_CachedSessionHandle != sessionHandle; m_CachedSessionHandle = sessionHandle; ExternApi.ArPresto_getFrame(ref m_CachedFrameHandle); // Update the native session with the newest frame. if (NativeSession != null) { NativeSession.OnUpdate(m_CachedFrameHandle); } _UpdateTextureIfNeeded(); if (EarlyUpdate != null) { EarlyUpdate(); } }
/// <summary> /// Coroutine method that communicates to the Instant Preview plugin /// every frame. /// /// If not running in the editor, this does nothing. /// </summary> /// <returns>Enumerator for a coroutine that updates Instant Preview /// every frame.</returns> public static IEnumerator InitializeIfNeeded() { // Terminates if not running in editor. if (!Application.isEditor) { yield break; } // User may have explicitly disabled Instant Preview. if (ARCoreProjectSettings.Instance != null && !ARCoreProjectSettings.Instance.IsInstantPreviewEnabled) { yield break; } #if UNITY_EDITOR // Determine if any augmented image databases need a rebuild. List <AugmentedImageDatabase> databases = new List <AugmentedImageDatabase>(); bool shouldRebuild = false; var augmentedImageDatabaseGuids = AssetDatabase.FindAssets("t:AugmentedImageDatabase"); foreach (var databaseGuid in augmentedImageDatabaseGuids) { var database = AssetDatabase.LoadAssetAtPath <AugmentedImageDatabase>( AssetDatabase.GUIDToAssetPath(databaseGuid)); databases.Add(database); shouldRebuild = shouldRebuild || database.IsBuildNeeded(); } // If the preference is to ask the user to rebuild, ask now. if (shouldRebuild && PromptToRebuildAugmentedImagesDatabase()) { foreach (var database in databases) { string error; database.BuildIfNeeded(out error); if (!string.IsNullOrEmpty(error)) { Debug.LogWarning("Failed to rebuild augmented " + "image database: " + error); } } } #endif var adbPath = InstantPreviewManager.GetAdbPath(); if (adbPath == null) { Debug.LogError("Instant Preview requires your Unity Android SDK path to be set. Please set it under " + "'Preferences > External Tools > Android'. You may need to install the Android SDK first."); yield break; } else if (!File.Exists(adbPath)) { Debug.LogErrorFormat("adb not found at \"{0}\". Please add adb to your SDK path and restart the Unity editor.", adbPath); yield break; } string localVersion; if (!StartServer(adbPath, out localVersion)) { yield break; } yield return(InstallApkAndRunIfConnected(adbPath, localVersion)); yield return(UpdateLoop(adbPath)); }
/// <summary> /// Coroutine method that communicates to the Instant Preview plugin /// every frame. /// /// If not running in the editor, this does nothing. /// </summary> /// <returns>Enumerator for a coroutine that updates Instant Preview /// every frame.</returns> public static IEnumerator InitializeIfNeeded() { // Terminates if not running in editor. if (!Application.isEditor) { yield break; } // User may have explicitly disabled Instant Preview. if (ARCoreProjectSettings.Instance != null && !ARCoreProjectSettings.Instance.IsInstantPreviewEnabled) { yield break; } #if UNITY_EDITOR // When build platform is not Android, verify min game view scale is 1.0x to prevent // confusing 2x scaling when Unity editor is running on a high density display. if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) { float minGameViewScale = GetMinGameViewScaleOrUnknown(); if (minGameViewScale != 1.0) { String viewScaleText = minGameViewScale == k_UnknownGameViewScale ? "<unknown>" : string.Format("{0}x", minGameViewScale); Debug.LogWarningFormat( "Instant Preview disabled, {0} minimum Game view scale unsupported for target build platform" + " '{1}'.\n" + "To use Instant Preview, switch build platform to '{2}' from the 'Build settings' window.", viewScaleText, EditorUserBuildSettings.activeBuildTarget, BuildTarget.Android); yield break; } } // Determine if any augmented image databases need a rebuild. List <AugmentedImageDatabase> databases = new List <AugmentedImageDatabase>(); bool shouldRebuild = false; var augmentedImageDatabaseGuids = AssetDatabase.FindAssets("t:AugmentedImageDatabase"); foreach (var databaseGuid in augmentedImageDatabaseGuids) { var database = AssetDatabase.LoadAssetAtPath <AugmentedImageDatabase>( AssetDatabase.GUIDToAssetPath(databaseGuid)); databases.Add(database); shouldRebuild = shouldRebuild || database.IsBuildNeeded(); } // If the preference is to ask the user to rebuild, ask now. if (shouldRebuild && PromptToRebuildAugmentedImagesDatabase()) { foreach (var database in databases) { string error; database.BuildIfNeeded(out error); if (!string.IsNullOrEmpty(error)) { Debug.LogWarning("Failed to rebuild augmented image database: " + error); } } } #endif var adbPath = InstantPreviewManager.GetAdbPath(); if (adbPath == null) { Debug.LogError("Instant Preview requires your Unity Android SDK path to be set. " + "Please set it under 'Preferences > External Tools > Android'. " + "You may need to install the Android SDK first."); yield break; } else if (!File.Exists(adbPath)) { Debug.LogErrorFormat("adb not found at \"{0}\". Please verify that 'Preferences > " + "External Tools > Android' has the correct Android SDK path that the Android Platform " + "Tools are installed, and that \"{0}\" exists. You may need to install the Android " + "SDK first.", adbPath); yield break; } string localVersion; if (!StartServer(adbPath, out localVersion)) { yield break; } yield return(InstallApkAndRunIfConnected(adbPath, localVersion)); yield return(UpdateLoop(adbPath)); }