NRNotificationListener.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /****************************************************************************
  2. * Copyright 2019 Nreal Techonology Limited. All rights reserved.
  3. *
  4. * This file is part of NRSDK.
  5. *
  6. * https://www.nreal.ai/
  7. *
  8. *****************************************************************************/
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using UnityEngine;
  13. namespace NRKernal
  14. {
  15. /// <summary> A nr notification listener. </summary>
  16. public class NRNotificationListener : MonoBehaviour
  17. {
  18. /// <summary> Values that represent levels. </summary>
  19. public enum Level
  20. {
  21. All = 0,
  22. Low,
  23. Middle,
  24. High,
  25. Off
  26. }
  27. // Only the message which level is higher than notifLevel would be shown.
  28. public Level notifLevel = Level.All;
  29. /// <summary> Notification object base. </summary>
  30. public class Notification
  31. {
  32. /// <summary> The notification listener. </summary>
  33. protected NRNotificationListener NotificationListener;
  34. /// <summary> Constructor. </summary>
  35. /// <param name="listener"> The listener.</param>
  36. public Notification(NRNotificationListener listener)
  37. {
  38. this.NotificationListener = listener;
  39. }
  40. /// <summary> Updates the state. </summary>
  41. public virtual void UpdateState() { }
  42. /// <summary> Executes the 'state changed' action. </summary>
  43. /// <param name="level"> The level.</param>
  44. public virtual void Trigger(Level level)
  45. {
  46. NotificationListener.Dispath(this, level);
  47. }
  48. }
  49. /// <summary> A low power notification. </summary>
  50. public class LowPowerNotification : Notification
  51. {
  52. /// <summary> Values that represent power states. </summary>
  53. public enum PowerState
  54. {
  55. /// <summary> An enum constant representing the full option. </summary>
  56. Full,
  57. /// <summary> An enum constant representing the middle option. </summary>
  58. Middle,
  59. /// <summary> An enum constant representing the low option. </summary>
  60. Low
  61. }
  62. /// <summary> The current state. </summary>
  63. private PowerState currentState = PowerState.Full;
  64. /// <summary> Constructor. </summary>
  65. /// <param name="listener"> The listener.</param>
  66. public LowPowerNotification(NRNotificationListener listener) : base(listener)
  67. {
  68. }
  69. /// <summary> Gets state by value. </summary>
  70. /// <param name="val"> The value.</param>
  71. /// <returns> The state by value. </returns>
  72. private PowerState GetStateByValue(float val)
  73. {
  74. if (val < 0.3f)
  75. {
  76. return PowerState.Low;
  77. }
  78. else if (val < 0.4f)
  79. {
  80. return PowerState.Middle;
  81. }
  82. return PowerState.Full;
  83. }
  84. /// <summary> Updates the state. </summary>
  85. public override void UpdateState()
  86. {
  87. #if !UNITY_EDITOR
  88. var state = GetStateByValue(SystemInfo.batteryLevel);
  89. #else
  90. var state = GetStateByValue(1f);
  91. #endif
  92. if (state != currentState)
  93. {
  94. if (state == PowerState.Low)
  95. {
  96. this.Trigger(Level.High);
  97. }
  98. else if (state == PowerState.Middle)
  99. {
  100. this.Trigger(Level.Middle);
  101. }
  102. this.currentState = state;
  103. }
  104. }
  105. }
  106. /// <summary> A slam state notification. </summary>
  107. public class SlamStateNotification : Notification
  108. {
  109. /// <summary> Values that represent slam states. </summary>
  110. private enum SlamState
  111. {
  112. /// <summary> An enum constant representing the none option. </summary>
  113. None,
  114. /// <summary> An enum constant representing the lost tracking option. </summary>
  115. LostTracking,
  116. /// <summary> An enum constant representing the tracking ready option. </summary>
  117. TrackingReady
  118. }
  119. /// <summary> The current state. </summary>
  120. private SlamState m_CurrentState = SlamState.None;
  121. /// <summary> Constructor. </summary>
  122. /// <param name="listener"> The listener.</param>
  123. public SlamStateNotification(NRNotificationListener listener) : base(listener)
  124. {
  125. NRHMDPoseTracker.OnHMDLostTracking += OnHMDLostTracking;
  126. NRHMDPoseTracker.OnHMDPoseReady += OnHMDPoseReady;
  127. }
  128. /// <summary> Executes the 'hmd pose ready' action. </summary>
  129. private void OnHMDPoseReady()
  130. {
  131. NRDebugger.Info("[SlamStateNotification] OnHMDPoseReady.");
  132. m_CurrentState = SlamState.TrackingReady;
  133. }
  134. /// <summary> Executes the 'hmd lost tracking' action. </summary>
  135. private void OnHMDLostTracking()
  136. {
  137. NRDebugger.Info("[SlamStateNotification] OnHMDLostTracking.");
  138. if (m_CurrentState != SlamState.LostTracking)
  139. {
  140. this.Trigger(Level.Middle);
  141. m_CurrentState = SlamState.LostTracking;
  142. }
  143. }
  144. }
  145. /// <summary> A temperature level notification. </summary>
  146. public class TemperatureLevelNotification : Notification
  147. {
  148. /// <summary> The current state. </summary>
  149. private GlassesTemperatureLevel currentState = GlassesTemperatureLevel.TEMPERATURE_LEVEL_NORMAL;
  150. /// <summary> Constructor. </summary>
  151. /// <param name="listener"> The listener.</param>
  152. public TemperatureLevelNotification(NRNotificationListener listener) : base(listener)
  153. {
  154. }
  155. /// <summary> Updates the state. </summary>
  156. public override void UpdateState()
  157. {
  158. base.UpdateState();
  159. var level = NRDevice.Subsystem.TemperatureLevel;
  160. if (currentState != level)
  161. {
  162. if (level != GlassesTemperatureLevel.TEMPERATURE_LEVEL_NORMAL)
  163. {
  164. this.Trigger(level == GlassesTemperatureLevel.TEMPERATURE_LEVEL_HOT
  165. ? Level.High : Level.Middle);
  166. }
  167. this.currentState = level;
  168. }
  169. }
  170. }
  171. /// <summary> Native interface error. </summary>
  172. public class NativeErrorNotification : Notification
  173. {
  174. public NRKernalError KernalError { get; private set; }
  175. /// <summary> Constructor. </summary>
  176. /// <param name="listener"> The listener.</param>
  177. public NativeErrorNotification(NRNotificationListener listener) : base(listener)
  178. {
  179. NRSessionManager.OnKernalError += OnSessionError;
  180. }
  181. private void OnSessionError(NRKernalError exception)
  182. {
  183. KernalError = exception;
  184. // Trigger the notification window
  185. if (KernalError is NRRGBCameraDeviceNotFindError
  186. || KernalError is NRPermissionDenyError
  187. || KernalError is NRUnSupportedHandtrackingCalculationError)
  188. {
  189. this.Trigger(Level.High);
  190. }
  191. }
  192. public string ErrorTitle
  193. {
  194. get
  195. {
  196. if (KernalError is NRRGBCameraDeviceNotFindError)
  197. {
  198. return "RGBCamera is disabled";
  199. }
  200. else if (KernalError is NRPermissionDenyError)
  201. {
  202. return "Permission Deny";
  203. }
  204. else if (KernalError is NRUnSupportedHandtrackingCalculationError)
  205. {
  206. return "Not support hand tracking calculation";
  207. }
  208. else
  209. {
  210. return KernalError.GetType().ToString();
  211. }
  212. }
  213. }
  214. public string ErrorContent
  215. {
  216. get
  217. {
  218. if (KernalError is NRRGBCameraDeviceNotFindError)
  219. {
  220. return NativeConstants.RGBCameraDeviceNotFindErrorTip;
  221. }
  222. else if (KernalError is NRUnSupportedHandtrackingCalculationError)
  223. {
  224. return NativeConstants.UnSupportedHandtrackingCalculationErrorTip;
  225. }
  226. else
  227. {
  228. return KernalError.GetErrorMsg();
  229. }
  230. }
  231. }
  232. }
  233. /// <summary> True to enable, false to disable the low power tips. </summary>
  234. [Header("Whether to open the low power prompt")]
  235. public bool EnableLowPowerTips;
  236. /// <summary> The low power notification prefab. </summary>
  237. public NRNotificationWindow LowPowerNotificationPrefab;
  238. /// <summary> True to enable, false to disable the slam state tips. </summary>
  239. [Header("Whether to open the slam state prompt")]
  240. public bool EnableSlamStateTips;
  241. /// <summary> The slam state notification prefab. </summary>
  242. public NRNotificationWindow SlamStateNotificationPrefab;
  243. /// <summary> True to enable, false to disable the high temporary tips. </summary>
  244. [Header("Whether to open the over temperature prompt")]
  245. public bool EnableHighTempTips;
  246. /// <summary> The high temporary notification prefab. </summary>
  247. public NRNotificationWindow HighTempNotificationPrefab;
  248. [Header("Whether to open the native interface error prompt")]
  249. public bool EnableNativeNotifyTips;
  250. public NRNotificationWindow NativeErrorNotificationPrefab;
  251. /// <summary> List of notifications. </summary>
  252. protected Dictionary<Notification, NRNotificationWindow> NotificationDict = new Dictionary<Notification, NRNotificationWindow>();
  253. /// <summary> The tips last time. </summary>
  254. private Dictionary<Level, float> TipsLastTime = new Dictionary<Level, float>() {
  255. { Level.High,3.5f},
  256. { Level.Middle,2.5f},
  257. { Level.Low,1.5f}
  258. };
  259. /// <summary> A notification message. </summary>
  260. public struct NotificationMsg
  261. {
  262. /// <summary> The notification. </summary>
  263. public Notification notification;
  264. /// <summary> The level. </summary>
  265. public Level level;
  266. }
  267. /// <summary> Queue of notifications. </summary>
  268. private Queue<NotificationMsg> NotificationQueue = new Queue<NotificationMsg>();
  269. private float m_LockTime = 0f;
  270. private const float UpdateInterval = 1f;
  271. private float m_TimeLast = 0f;
  272. void Awake()
  273. {
  274. LowPowerNotificationPrefab.gameObject.SetActive(false);
  275. SlamStateNotificationPrefab.gameObject.SetActive(false);
  276. HighTempNotificationPrefab.gameObject.SetActive(false);
  277. NativeErrorNotificationPrefab.gameObject.SetActive(false);
  278. }
  279. void Start()
  280. {
  281. DontDestroyOnLoad(gameObject);
  282. RegistNotification();
  283. }
  284. /// <summary> Regist notification. </summary>
  285. protected virtual void RegistNotification()
  286. {
  287. if (NRSessionManager.Instance.NRSessionBehaviour.SessionConfig.EnableNotification)
  288. {
  289. if (EnableLowPowerTips) BindNotificationWindow(new LowPowerNotification(this), LowPowerNotificationPrefab);
  290. if (EnableSlamStateTips) BindNotificationWindow(new SlamStateNotification(this), SlamStateNotificationPrefab);
  291. if (EnableHighTempTips) BindNotificationWindow(new TemperatureLevelNotification(this), HighTempNotificationPrefab);
  292. }
  293. if (EnableNativeNotifyTips) BindNotificationWindow(new NativeErrorNotification(this), NativeErrorNotificationPrefab);
  294. }
  295. public void BindNotificationWindow(Notification notification, NRNotificationWindow window)
  296. {
  297. if (NotificationDict.ContainsKey(notification))
  298. {
  299. NRDebugger.Error("[NRNotificationListener] Already has the notification.");
  300. return;
  301. }
  302. NotificationDict.Add(notification, window);
  303. }
  304. /// <summary>
  305. /// Close all notification windows.
  306. /// </summary>
  307. public void ClearAll()
  308. {
  309. notifLevel = Level.Off;
  310. }
  311. void Update()
  312. {
  313. // For Editor test
  314. //if (Input.GetKeyDown(KeyCode.M))
  315. //{
  316. // var notifys = NotificationDict.Keys.ToArray();
  317. // this.Dispath(notifys[UnityEngine.Random.Range(0, notifys.Length - 1)], Level.Middle);
  318. //}
  319. //if (Input.GetKeyDown(KeyCode.N))
  320. //{
  321. // var notifys = NotificationDict.Keys.ToArray();
  322. // this.Dispath(notifys[UnityEngine.Random.Range(0, notifys.Length - 1)], Level.High);
  323. //}
  324. //if (Input.GetKeyDown(KeyCode.B))
  325. //{
  326. // var notifys = NotificationDict.Keys.ToArray();
  327. // this.Dispath(notifys[3], Level.High);
  328. //}
  329. m_TimeLast += Time.deltaTime;
  330. if (m_TimeLast < UpdateInterval)
  331. {
  332. return;
  333. }
  334. m_TimeLast = 0;
  335. foreach (var item in NotificationDict)
  336. {
  337. item.Key.UpdateState();
  338. }
  339. if (m_LockTime < float.Epsilon)
  340. {
  341. if (NotificationQueue.Count != 0)
  342. {
  343. var msg = NotificationQueue.Dequeue();
  344. this.OprateNotificationMsg(msg);
  345. m_LockTime = TipsLastTime[msg.level];
  346. }
  347. }
  348. else
  349. {
  350. m_LockTime -= UpdateInterval;
  351. }
  352. }
  353. /// <summary> Dispaths notification message. </summary>
  354. /// <param name="notification"> The notification.</param>
  355. /// <param name="lev"> The level.</param>
  356. public void Dispath(Notification notification, Level lev)
  357. {
  358. if (lev >= notifLevel)
  359. {
  360. NotificationQueue.Enqueue(new NotificationMsg()
  361. {
  362. notification = notification,
  363. level = lev
  364. });
  365. }
  366. }
  367. /// <summary> Oprate notification message. </summary>
  368. /// <param name="msg"> The message.</param>
  369. protected virtual void OprateNotificationMsg(NotificationMsg msg)
  370. {
  371. NRNotificationWindow prefab = NotificationDict[msg.notification];
  372. Notification notification_obj = msg.notification;
  373. Level notification_level = msg.level;
  374. float duration = TipsLastTime[notification_level];
  375. Action onConfirm = null;
  376. // Notification window will not be destroyed automatic when lowpower and high level warning
  377. // Set it's duration to -1
  378. if (notification_obj is LowPowerNotification)
  379. {
  380. if (notification_level == Level.High)
  381. {
  382. duration = -1f;
  383. onConfirm = () =>
  384. {
  385. NRDevice.QuitApp();
  386. };
  387. }
  388. }
  389. if (prefab != null)
  390. {
  391. NRDebugger.Info("[NRNotificationListener] Dispath:" + notification_obj.GetType().ToString());
  392. NRNotificationWindow notification = Instantiate(prefab);
  393. notification.gameObject.SetActive(true);
  394. notification.transform.SetParent(transform);
  395. if (notification_obj is NativeErrorNotification)
  396. {
  397. string title = ((NativeErrorNotification)notification_obj).ErrorTitle;
  398. string content = ((NativeErrorNotification)notification_obj).ErrorContent;
  399. notification.SetLevle(notification_level)
  400. .SetDuration(duration)
  401. .SetTitle(title)
  402. .SetContent(content)
  403. .SetConfirmAction(onConfirm)
  404. .Build();
  405. }
  406. else
  407. {
  408. notification.SetLevle(notification_level)
  409. .SetDuration(duration)
  410. .SetConfirmAction(onConfirm)
  411. .Build();
  412. }
  413. }
  414. }
  415. }
  416. }