// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) 2022-2023 Magic Leap, Inc. All Rights Reserved. // Use of this file is governed by the Magic Leap 2 Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2 // Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein. // %COPYRIGHT_END% // --------------------------------------------------------------------- // %BANNER_END% using System.Collections; using System.Collections.Generic; using UnityEngine; namespace MagicLeap.Spectator { public class MLSpectatorNotification : MonoBehaviour { #region Inspector variables [SerializeField] private NotificationPopup popupPrefab; #endregion #region Private variables // The fallback time a message remains visiable when ShowNotificationMessage() isn't given a duration private const float DefaultDuration = 5; // A reference to our popup private NotificationPopup popup; // The amount of time the current popup will be shown before being removed private float popupDelay; // The amount of time remaining for the current popup private float popupDelayElapsed; // A referance to our delayed removal coroutine private Coroutine coroutineDelayedRemoval; // A dictionary mapping Spectator states to strings private Dictionary stateMap = new() { { MLSpectator.State.Connecting, "Magic Leap Spectator connecting..." }, { MLSpectator.State.Connected, "Magic Leap Spectator connecting..." }, { MLSpectator.State.LookingForMarker, "Look at the marker, hold still" }, { MLSpectator.State.Running, "Magic Leap Spectator is active" }, { MLSpectator.State.Disconnected, "Magic Leap Spectator disconnected" } }; #endregion #region MonoBehaviour methods private void Awake() { // If we are a root gameobject if (transform.parent == null) { // Make sure we aren't destroyed between scenes DontDestroyOnLoad(this); } else Debug.LogWarning("MLSpectatorNotification: Awake() - Must be root object to persist between scenes"); if (popupPrefab != null) { if (MLSpectator.Instance != null) MLSpectator.Instance.onStateChanged += OnSpectatorStateChanged; else Debug.LogWarning($"MLSpectatorNotification: Awake() - Missing MLSpectator instance"); } else Debug.LogWarning("MLSpectatorNotification: Awake() - Missing popup prefab"); } private void OnDestroy() { if (MLSpectator.Instance != null) MLSpectator.Instance.onStateChanged -= OnSpectatorStateChanged; } #endregion #region Private methods private void OnSpectatorStateChanged(MLSpectator.State newState) { // If we support the current state if (stateMap.TryGetValue(newState, out string notificationMessage)) { // All states should be shown forever until we get the active or disconnected state var showForever = newState != MLSpectator.State.Running && newState != MLSpectator.State.Disconnected; // Show our notification ShowNotificationMessage(notificationMessage, (showForever ? -1 : 0)); } // Otherwise, notify that we do not support this state else Debug.LogWarning($"MLSpectatorNotification: OnSpectatorStateChanged() - No string for state {newState}"); } #endregion #region Public methods // Show a notification message // a negative duration value shows the notification forever // a zero duration uses the default value public void ShowNotificationMessage(string str, float duration = 0) { // If we're already showing a message, we can update the text if (popup != null) { popup.SetText(str); } // Otherwise, we can spawn a new prefab and set the text there else { popup = Instantiate(popupPrefab, transform); popup.SetText(str); } // If this isn't a forever message, we can handle the delayed removal here if (duration > -1) { // If our given duration is 0 use our default duration RemoveMessageAfterDelay(duration == 0 ? DefaultDuration : duration); } // Otherwise, we want to show this forever else { // If we're currently waiting to remove we stop the coroutine here // (if this isn't a normal transition we can handle it here just in case) StopDelayedRemovalCoroutine(); } } // Removes the current notification public void RemoveNotification() { if (popup == null) { Debug.LogWarning("MLSpectatorNotification: RemoveNotification() - No notification to remove"); return; } Destroy(popup.gameObject); popup = null; StopDelayedRemovalCoroutine(); } // Set up our coroutine to remove the current popup after a given time private void RemoveMessageAfterDelay(float delayTime) { StopDelayedRemovalCoroutine(); coroutineDelayedRemoval = StartCoroutine(HandleDelayedRemoval(delayTime)); } private IEnumerator HandleDelayedRemoval(float delayTime) { popupDelayElapsed = 0; popupDelay = delayTime; while (popupDelayElapsed < popupDelay) { popupDelayElapsed += Time.deltaTime; yield return null; } RemoveNotification(); coroutineDelayedRemoval = null; } private void StopDelayedRemovalCoroutine() { if (coroutineDelayedRemoval == null) return; StopCoroutine(coroutineDelayedRemoval); coroutineDelayedRemoval = null; } #endregion } }