/****************************************************************************
* Copyright 2019 Nreal Techonology Limited. All rights reserved.
*
* This file is part of NRSDK.
*
* https://www.nreal.ai/
*
*****************************************************************************/
namespace NRKernal
{
using AOT;
using System;
using System.Collections.Generic;
#if UNITY_ANDROID && !UNITY_EDITOR
using System.Runtime.InteropServices;
using System.Threading;
using UnityEngine;
#endif
/// Manage the HMD device.
public class NRDevice : SingleTon
{
/// Event queue for all listeners interested in OnGlassesStateChanged events.
public static event GlassesEvent OnGlassesStateChanged;
/// Event queue for all listeners interested in OnGlassesDisconnect events.
public static event GlassesDisconnectEvent OnGlassesDisconnect;
/// Values that represent glasses event types.
public enum GlassesEventType
{
/// An enum constant representing the put on option.
PutOn,
/// An enum constant representing the put off option.
PutOff
}
private const float SDK_RELEASE_TIMEOUT = 2f;
private bool m_IsInitialized = false;
private Exception m_InitException = null;
private static NRDeviceSubsystem m_Subsystem;
public static NRDeviceSubsystem Subsystem
{
get
{
if (m_Subsystem == null)
{
string str_match = NRDeviceSubsystemDescriptor.Name;
List descriptors = new List();
NRSubsystemManager.GetSubsystemDescriptors(descriptors);
foreach (var des in descriptors)
{
if (des.id.Equals(str_match))
{
m_Subsystem = des.Create();
}
}
}
return m_Subsystem;
}
}
#if UNITY_ANDROID && !UNITY_EDITOR
private static AndroidJavaObject m_UnityActivity;
#endif
/// Init HMD device.
public void Init()
{
// Keep the exception state.
if (m_InitException != null)
{
throw m_InitException;
}
if (m_IsInitialized)
{
return;
}
NRTools.Init();
MainThreadDispather.Initialize();
#if UNITY_ANDROID && !UNITY_EDITOR
// Init before all actions.
AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
m_UnityActivity = cls_UnityPlayer.GetStatic("currentActivity");
NativeApi.NRSDKInitSetAndroidActivity(m_UnityActivity.GetRawObject());
#endif
try
{
Subsystem.Start();
Subsystem.RegestEvents(OnGlassesWear, OnGlassesDisconnectEvent);
}
catch (Exception e)
{
m_InitException = e;
throw e;
}
m_IsInitialized = true;
}
public void Pause()
{
Subsystem.Pause();
}
public void Resume()
{
Subsystem.Resume();
}
public void Destroy()
{
Subsystem.Stop();
}
private void OnGlassesWear(NRDevice.GlassesEventType eventtype)
{
NRDebugger.Info("[NRDevice] " + (eventtype == GlassesEventType.PutOn ? "Glasses put on" : "Glasses put off"));
OnGlassesStateChanged?.Invoke(eventtype);
NRSessionManager.OnGlassesStateChanged?.Invoke(eventtype);
}
private void OnGlassesDisconnectEvent(GlassesDisconnectReason reason)
{
NRDebugger.Info("[NRDevice] OnGlassesDisconnectEvent:" + reason.ToString());
// If NRSDK release time out in 2 seconds, FoceKill the process.
AsyncTaskExecuter.Instance.RunAction(() =>
{
try
{
OnGlassesDisconnect?.Invoke(reason);
NRSessionManager.OnGlassesDisconnect?.Invoke(reason);
}
catch (Exception e)
{
NRDebugger.Info("[NRDevice] Operate OnGlassesDisconnect event error:" + e.ToString());
throw e;
}
finally
{
bool forceKill = true;
// Some app contains 2D content and 3D content. It may wish to keep process alive after glasses switch mode.
if (reason == GlassesDisconnectReason.NOTIFY_TO_QUIT_APP && !NRSessionManager.Instance.NRSessionBehaviour.SessionConfig.ForceKillWhileGlassSwitchMode)
forceKill = false;
NRDebugger.Info("[NRDevice] OnGlassesDisconnectEvent: forceKill={0}, ForceKillWhileGlassSwitchMode={1}", forceKill, NRSessionManager.Instance.NRSessionBehaviour.SessionConfig.ForceKillWhileGlassSwitchMode);
if (forceKill)
ForceKill(true);
else
Subsystem.ResetStateOnNextResume();
}
}, () =>
{
NRDebugger.Error("[NRDevice] Release sdk timeout, force kill the process!!!");
ForceKill(false);
}, SDK_RELEASE_TIMEOUT);
}
#region Quit
/// Quit the app.
public static void QuitApp()
{
NRDebugger.Info("[NRDevice] Start To Quit Application.");
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
ForceKill();
#endif
}
/// Force kill the app. Avoid timeout to pause UnityEngine.
/// Thrown when an exception error condition occurs.
public static void ForceKill(bool needrelease = true)
{
NRDebugger.Info("[NRDevice] ForceKill: release={0}", needrelease);
try
{
ReleaseSDK();
}
catch (Exception e)
{
throw e;
}
finally
{
#if UNITY_ANDROID && !UNITY_EDITOR
AndroidJNI.AttachCurrentThread();
m_UnityActivity?.Call("finish");
AndroidJavaClass processClass = new AndroidJavaClass("android.os.Process");
int myPid = processClass.CallStatic("myPid");
processClass.CallStatic("killProcess", myPid);
#endif
}
}
public static void ReleaseSDK()
{
NRDebugger.Info("[NRDevice] Start to release sdk");
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
NRSessionManager.Instance.DestroySession();
NRDebugger.Info("[NRDevice] Release sdk end, cost:{0} ms", stopwatch.ElapsedMilliseconds);
}
#endregion
#if UNITY_ANDROID && !UNITY_EDITOR
private struct NativeApi
{
[DllImport(NativeConstants.NRNativeLibrary)]
public static extern NativeResult NRSDKInitSetAndroidActivity(IntPtr android_activity);
}
#endif
}
}