Newer
Older
CGTrack / Assets / Oculus / Platform / Samples / VrVoiceChat / Scripts / PlatformManager.cs
namespace Oculus.Platform.Samples.VrVoiceChat
{
	using UnityEngine;
	using UnityEngine.UI;
	using System.Collections.Generic;
	using Oculus.Platform;
	using Oculus.Platform.Models;

	// This class coordinates communication with the Oculus Platform
	// Service running in your device.
	public class PlatformManager : MonoBehaviour
	{
		// the game object to build the invite list in
		[SerializeField] private GameObject m_invitesList = null;

		// Button to create for the user to answer an invite call
		[SerializeField] private GameObject m_invitePrefab = null;

		// State transition sets the background color as a visual status indication
		[SerializeField] private Camera m_camera = null;

		// GameObject that represents the Head of the remote Avatar
		[SerializeField] private GameObject m_remoteHead = null;

		private State m_currentState;

		private static PlatformManager s_instance = null;
		private RoomManager m_roomManager;
		private P2PManager m_p2pManager;
		private VoipManager m_voipManager;

		// my Application-scoped Oculus ID
		private ulong m_myID;

		// my Oculus user name
		private string m_myOculusID;

		void Update()
		{
			// occasionally poll for new call invites
			if (m_roomManager.ShouldPollInviteList)
			{
				m_roomManager.UpdateActiveInvitesList();
			}

			// occasionally send my transform to my interlocutor
			if (m_p2pManager.ShouldSendHeadUpdate)
			{
				m_p2pManager.SendHeadTransform(m_camera.transform);
			}

			// estimate the remote avatar transforms from the most recent network update
			m_p2pManager.GetRemoteHeadTransform(m_remoteHead.transform);
		}

		#region Initialization and Shutdown

		void Awake()
		{
			// make sure only one instance of this manager ever exists
			if (s_instance != null) {
				Destroy(gameObject);
				return;
			}

			s_instance = this;
			DontDestroyOnLoad(gameObject);

			TransitionToState(State.INITIALIZING);

			Core.Initialize();

			m_roomManager = new RoomManager();
			m_p2pManager = new P2PManager(m_remoteHead.transform);
			m_voipManager = new VoipManager(m_remoteHead);
		}

		void Start ()
		{
			// First thing we should do is perform an entitlement check to make sure
			// we successfully connected to the Oculus Platform Service.
			Entitlements.IsUserEntitledToApplication().OnComplete(IsEntitledCallback);
		}

		void IsEntitledCallback(Message msg)
		{
			if (msg.IsError)
			{
				TerminateWithError(msg);
				return;
			}

			// Next get the identity of the user that launched the Application.
			Users.GetLoggedInUser().OnComplete(GetLoggedInUserCallback);
		}

		void GetLoggedInUserCallback(Message<User> msg)
		{
			if (msg.IsError)
			{
				TerminateWithError(msg);
				return;
			}

			m_myID = msg.Data.ID;
			m_myOculusID = msg.Data.OculusID;

			TransitionToState(State.WAITING_TO_CALL_OR_ANSWER);

			// If the user launched the app by accepting the notification, then we want to
			// join that room.  Otherwise, start polling for invites.
			m_roomManager.CheckForLaunchInvite();
		}

		void OnApplicationQuit()
		{
			m_roomManager.LeaveCurrentRoom();
			m_p2pManager.Disconnect();
			m_voipManager.Disconnect();
		}

		// For most errors we terminate the Application since this example doesn't make
		// sense if the user is disconnected.
		public static void TerminateWithError(Message msg)
		{
			Debug.Log("Error: " + msg.GetError().Message);
			UnityEngine.Application.Quit();
		}

		#endregion

		#region Properties

		public static State CurrentState
		{
			get
			{
				return s_instance.m_currentState;
			}
		}

		public static ulong MyID
		{
			get
			{
				if (s_instance != null)
				{
					return s_instance.m_myID;
				}
				else
				{
					return 0;
				}
			}
		}

		public static string MyOculusID
		{
			get
			{
				if (s_instance != null && s_instance.m_myOculusID != null)
				{
					return s_instance.m_myOculusID;
				}
				else
				{
					return string.Empty;
				}
			}
		}

		#endregion

		#region Button Clicks

		public void CallFriendOnClick()
		{
			if (CurrentState == State.WAITING_TO_CALL_OR_ANSWER)
			{
				m_roomManager.CreateRoomAndLaunchInviteMenu();
			}
		}

		public void HangupOnClick()
		{
			m_roomManager.LeaveCurrentRoom();
		}

		public void QuitOnClick()
		{
			UnityEngine.Application.Quit();
		}

		public static void AnswerCallOnClick(ulong roomID)
		{
			if (s_instance)
			{
				s_instance.m_roomManager.JoinExistingRoom(roomID);
			}
		}

		#endregion

		#region State Management

		public enum State
		{
			// loading platform library, checking application entitlement,
			// getting the local user info
			INITIALIZING,

			// waiting on the user to invite a friend to chat, or
			// accept an invite sent to them
			WAITING_TO_CALL_OR_ANSWER,

			// in this state we've create a room, and hopefully
			// sent some invites, and we're waiting for a response
			WAITING_FOR_ANSWER,

			// we're in a room as the caller or the callee
			CONNECTED_IN_A_ROOM,

			// shutdown any connections and leave the current room
			HANGUP,
		};

		public static void TransitionToState(State newState)
		{
			Debug.LogFormat("State {0} -> {1}", s_instance.m_currentState, newState);

			if (s_instance && s_instance.m_currentState != newState)
			{
				s_instance.m_currentState = newState;

				// state transition logic
				switch (newState)
				{
					case State.HANGUP:
						s_instance.m_roomManager.LeaveCurrentRoom();
						s_instance.m_p2pManager.Disconnect();
						s_instance.m_voipManager.Disconnect();
						break;

					case State.WAITING_TO_CALL_OR_ANSWER:
						break;

					case State.CONNECTED_IN_A_ROOM:
						s_instance.m_p2pManager.ConnectTo(s_instance.m_roomManager.RemoteUserID);
						s_instance.m_voipManager.ConnectTo(s_instance.m_roomManager.RemoteUserID);
						break;
				}
			}

			// set the background color as a visual aid to the connection status
			SetBackgroundColorForState();
		}

		public static void SetBackgroundColorForState()
		{
			switch (s_instance.m_currentState)
			{
				case State.INITIALIZING:
				case State.HANGUP:
					s_instance.m_camera.backgroundColor = Color.black;
					break;

				case State.WAITING_TO_CALL_OR_ANSWER:
					s_instance.m_camera.backgroundColor = new Color(0f, 0f, .3f);
					break;

				case State.WAITING_FOR_ANSWER:
					s_instance.m_camera.backgroundColor = new Color(0, 0, .6f);
					break;

				case State.CONNECTED_IN_A_ROOM:
					float red = s_instance.m_p2pManager.Connected ? 1.0f : 0;
					float green = s_instance.m_voipManager.Connected ? 1.0f : 0;
					s_instance.m_camera.backgroundColor = new Color(red, green, 1.0f);
					break;
			}
		}

		public static void SetActiveInvites(List<RoomManager.Invite> invites)
		{
			if (s_instance && s_instance.m_invitesList && s_instance.m_invitePrefab)
			{
				// first remove all existing Invites
				foreach (Transform child in s_instance.m_invitesList.transform)
				{
					Destroy(child.gameObject);
				}

				foreach (var invite in invites)
				{
					GameObject button = Instantiate(s_instance.m_invitePrefab) as GameObject;
					button.GetComponentInChildren<Text>().text = invite.OwnerID;
					button.name = invite.RoomID.ToString();
					button.GetComponent<Button>().onClick.AddListener(
						() => PlatformManager.AnswerCallOnClick(invite.RoomID));
					button.transform.SetParent(s_instance.m_invitesList.transform, false);
				}
			}
		}

		#endregion
	}
}