#if ENABLE_CLOUD_SERVICES_ANALYTICS || UNITY_2023_2_OR_NEWER
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit.Inputs;
namespace UnityEditor.XR.Interaction.Toolkit.Analytics.Hooks
{
///
/// Runtime tracker for XR Input Modality Manager modality events.
/// This class is used to track the number of enabled modality managers along with
/// the duration that it's in controller or hand mode during a play mode session.
///
///
///
class InputModalityManagerTracker
{
class InputModeTracker
{
public enum Handedness
{
Left,
Right,
}
public double durationNone { get; private set; }
public double durationTrackedHand { get; private set; }
public double durationMotionController { get; private set; }
public bool trackedHandAssigned { get; private set; }
public bool motionControllerAssigned { get; private set; }
readonly Handedness m_Handedness;
int m_NoneCount;
int m_TrackedHandCount;
int m_MotionControllerCount;
double m_NoneTimestamp;
double m_TrackedHandTimestamp;
double m_MotionControllerTimestamp;
Dictionary m_InputModes =
new Dictionary();
public InputModeTracker(Handedness handedness)
{
m_Handedness = handedness;
}
public void Reset()
{
durationNone = 0d;
durationTrackedHand = 0d;
durationMotionController = 0d;
trackedHandAssigned = false;
motionControllerAssigned = false;
m_NoneCount = 0;
m_TrackedHandCount = 0;
m_MotionControllerCount = 0;
m_NoneTimestamp = 0d;
m_TrackedHandTimestamp = 0d;
m_MotionControllerTimestamp = 0d;
m_InputModes.Clear();
}
public void StartSession(XRInputModalityManager manager, double now)
{
QueryAssignedState(manager);
var inputMode = GetInputMode(manager);
Increment(inputMode, now);
m_InputModes[manager] = inputMode;
}
public void UpdateMode(XRInputModalityManager manager, double now)
{
QueryAssignedState(manager);
if (!m_InputModes.TryGetValue(manager, out var previousMode))
return;
var inputMode = GetInputMode(manager);
if (previousMode == inputMode)
return;
Decrement(previousMode, now);
Increment(inputMode, now);
m_InputModes[manager] = inputMode;
}
public void EndSession(XRInputModalityManager manager, double now)
{
QueryAssignedState(manager);
if (m_InputModes.TryGetValue(manager, out var previousMode))
{
Decrement(previousMode, now);
m_InputModes.Remove(manager);
}
}
void Increment(XRInputModalityManager.InputMode inputMode, double now)
{
switch (inputMode)
{
case XRInputModalityManager.InputMode.None:
m_NoneCount++;
if (m_NoneCount == 1)
m_NoneTimestamp = now;
break;
case XRInputModalityManager.InputMode.TrackedHand:
m_TrackedHandCount++;
if (m_TrackedHandCount == 1)
m_TrackedHandTimestamp = now;
break;
case XRInputModalityManager.InputMode.MotionController:
m_MotionControllerCount++;
if (m_MotionControllerCount == 1)
m_MotionControllerTimestamp = now;
break;
}
}
void Decrement(XRInputModalityManager.InputMode previousMode, double now)
{
switch (previousMode)
{
case XRInputModalityManager.InputMode.None:
m_NoneCount--;
if (m_NoneCount == 0)
durationNone += now - m_NoneTimestamp;
break;
case XRInputModalityManager.InputMode.TrackedHand:
m_TrackedHandCount--;
if (m_TrackedHandCount == 0)
durationTrackedHand += now - m_TrackedHandTimestamp;
break;
case XRInputModalityManager.InputMode.MotionController:
m_MotionControllerCount--;
if (m_MotionControllerCount == 0)
durationMotionController += now - m_MotionControllerTimestamp;
break;
}
}
void QueryAssignedState(XRInputModalityManager manager)
{
trackedHandAssigned |= GetHandGameObject(manager) != null;
motionControllerAssigned |= GetControllerGameObject(manager) != null;
}
XRInputModalityManager.InputMode GetInputMode(XRInputModalityManager manager)
{
switch (m_Handedness)
{
case Handedness.Left:
return manager.leftInputMode;
case Handedness.Right:
return manager.rightInputMode;
default:
return XRInputModalityManager.InputMode.None;
}
}
GameObject GetHandGameObject(XRInputModalityManager manager)
{
switch (m_Handedness)
{
case Handedness.Left:
return manager.leftHand;
case Handedness.Right:
return manager.rightHand;
default:
return null;
}
}
GameObject GetControllerGameObject(XRInputModalityManager manager)
{
switch (m_Handedness)
{
case Handedness.Left:
return manager.leftController;
case Handedness.Right:
return manager.rightController;
default:
return null;
}
}
}
///
/// Duration in seconds of at least one enabled input modality manager during a play mode session.
///
///
/// This duration can range from 0 seconds to the total length of the play mode session,
/// and does not differ based on multiple managers being enabled simultaneously.
///
public double sessionDuration { get; private set; }
///
/// Peak number of enabled input modality managers during a play mode session.
///
public int modalityManagersPeakCount { get; private set; }
///
/// Total count of different input modality managers that were enabled during a play mode session.
///
public int modalityManagersObjectCount => m_AllManagers.Count;
List m_EnabledManagers = new List();
List m_AllManagers = new List();
InputModeTracker m_LeftModeTracker = new InputModeTracker(InputModeTracker.Handedness.Left);
InputModeTracker m_RightModeTracker = new InputModeTracker(InputModeTracker.Handedness.Right);
double m_StartTimestamp;
///
/// Reset the tracker to its initial state.
/// Call this after generating the analytics payload to avoid accumulating data across
/// multiple play mode sessions when domain reload is disabled.
///
public void Cleanup()
{
foreach (var manager in m_AllManagers)
{
if (manager != null)
{
manager.leftInputModeChanged -= OnLeftInputModeChanged;
manager.rightInputModeChanged -= OnRightInputModeChanged;
}
}
sessionDuration = 0d;
modalityManagersPeakCount = 0;
m_EnabledManagers.Clear();
m_AllManagers.Clear();
m_LeftModeTracker.Reset();
m_RightModeTracker.Reset();
m_StartTimestamp = 0d;
}
///
/// Start tracking the manager component, invoked when its OnEnable is called.
///
/// The manager component to track.
/// Current timestamp.
public void StartSession(XRInputModalityManager manager, double now)
{
if (m_EnabledManagers.Contains(manager))
return;
m_EnabledManagers.Add(manager);
if (m_EnabledManagers.Count > modalityManagersPeakCount)
modalityManagersPeakCount = m_EnabledManagers.Count;
if (!m_AllManagers.Contains(manager))
m_AllManagers.Add(manager);
manager.leftInputModeChanged += OnLeftInputModeChanged;
manager.rightInputModeChanged += OnRightInputModeChanged;
m_LeftModeTracker.StartSession(manager, now);
m_RightModeTracker.StartSession(manager, now);
if (m_EnabledManagers.Count == 1)
m_StartTimestamp = now;
}
///
/// End tracking the manager component, invoked when its OnDisable is called.
///
/// The manager component to track.
/// Current timestamp.
public void EndSession(XRInputModalityManager manager, double now)
{
if (m_EnabledManagers.Remove(manager))
{
if (m_EnabledManagers.Count == 0)
sessionDuration += now - m_StartTimestamp;
m_LeftModeTracker.EndSession(manager, now);
m_RightModeTracker.EndSession(manager, now);
manager.leftInputModeChanged -= OnLeftInputModeChanged;
manager.rightInputModeChanged -= OnRightInputModeChanged;
}
}
///
/// Update the analytics payload struct with the data from this tracker.
///
/// The analytics payload to write into.
public void UpdateEventPayload(ref XRIPlayModeEvent.Payload payload)
{
payload.modalityManagersPeakCount = modalityManagersPeakCount;
payload.modalityManagersObjectCount = modalityManagersObjectCount;
payload.modalityManagerDurationSeconds = (float)sessionDuration;
payload.leftModalityInfo = new ModalityRuntimeData
{
handAssigned = m_LeftModeTracker.trackedHandAssigned,
controllerAssigned = m_LeftModeTracker.motionControllerAssigned,
noneDurationSeconds = (float)m_LeftModeTracker.durationNone,
trackedHandDurationSeconds = (float)m_LeftModeTracker.durationTrackedHand,
motionControllerDurationSeconds = (float)m_LeftModeTracker.durationMotionController,
};
payload.rightModalityInfo = new ModalityRuntimeData
{
handAssigned = m_RightModeTracker.trackedHandAssigned,
controllerAssigned = m_RightModeTracker.motionControllerAssigned,
noneDurationSeconds = (float)m_RightModeTracker.durationNone,
trackedHandDurationSeconds = (float)m_RightModeTracker.durationTrackedHand,
motionControllerDurationSeconds = (float)m_RightModeTracker.durationMotionController,
};
}
void OnLeftInputModeChanged(XRInputModalityManager manager, XRInputModalityManager.InputMode inputMode)
{
m_LeftModeTracker.UpdateMode(manager, Time.realtimeSinceStartupAsDouble);
}
void OnRightInputModeChanged(XRInputModalityManager manager, XRInputModalityManager.InputMode inputMode)
{
m_RightModeTracker.UpdateMode(manager, Time.realtimeSinceStartupAsDouble);
}
}
}
#endif