Files
Kizza00232Jera 17532b524f [unity] avatars
2025-06-11 14:45:45 +02:00

400 lines
12 KiB
C#

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#nullable enable
#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR) && !OVRPLUGIN_UNSUPPORTED_PLATFORM
#define USING_XR_SDK
#endif
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Oculus.Avatar2;
using UnityEngine;
using UnityEngine.UI;
public class UILogger : MonoBehaviour, IUIControllerInterface
{
private static readonly int s_logCacheSize = 1000;
private static readonly int s_displayLogCacheSize = 50;
private static readonly Queue<string> s_logCacheQueue = new Queue<string>(s_logCacheSize + 1);
private static readonly string logScope = "UILogger";
[SerializeField] private Text? logText;
[SerializeField] private Dropdown? logLevelDropdown;
private bool _isActive = false;
[SerializeField] private OvrAvatarLog.ELogLevel minLogLevel = OvrAvatarLog.ELogLevel.Error;
[SerializeField] private int logViewCapacity = 10; // TODO: add character limit instead of log count limit
[SerializeField] private Button? exportLogsButton;
[SerializeField] private Button? exportLogsFilteredButton;
private Button? _selectedButton;
private const float SELECTED_SUB_MENU_SCALE_FACTOR = 1.15f;
private static string? s_logQueueText;
private static readonly Queue<string> s_displayLogCacheQueue = new Queue<string>(s_displayLogCacheSize + 1);
private bool _displayLogCacheQueueChanged = false;
#if USING_XR_SDK
private readonly UIInputControllerButton _increaseLogLevelButton = new()
{
button = OVRInput.Button.PrimaryIndexTrigger,
controller = OVRInput.Controller.RTouch,
description = "Increase Log Level",
scope = logScope
};
private readonly UIInputControllerButton _decreaseLogLevelButton = new()
{
button = OVRInput.Button.PrimaryIndexTrigger,
controller = OVRInput.Controller.LTouch,
description = "Decrease Log Level",
scope = logScope
};
#endif
private void Awake()
{
if (logLevelDropdown == null)
{
OvrAvatarLog.LogError("UILogger::Awake : Null logLevelDropdown reference. Make sure this field is assigned in Editor.", logScope);
return;
}
logLevelDropdown.ClearOptions();
var logOptions = new List<string>();
foreach (OvrAvatarLog.ELogLevel logLevel in Enum.GetValues(typeof(OvrAvatarLog.ELogLevel)))
{
if (logLevel > OvrAvatarLog.ELogLevel.Silent)
{
logOptions.Add(AddColorToTextFromLogLevel(logLevel.ToString(), logLevel));
}
}
logLevelDropdown.AddOptions(logOptions);
logLevelDropdown.SetValueWithoutNotify((int)minLogLevel >= 0 ? (int)minLogLevel : logOptions.Count - 1);
logLevelDropdown.onValueChanged.AddListener(HandleDropdownValueChanged);
if (exportLogsButton == null)
{
OvrAvatarLog.LogError("UILogger::Awake : Null exportLogsButton reference. Make sure this field is assigned in Editor.", logScope);
return;
}
if (exportLogsFilteredButton == null)
{
OvrAvatarLog.LogError("UILogger::Awake : Null exportLogsFilteredButton reference. Make sure this field is assigned in Editor.", logScope);
return;
}
exportLogsButton.onClick.AddListener(ExportAllLogs);
exportLogsFilteredButton.onClick.AddListener(ExportFilteredLogs);
}
private void Start()
{
OvrAvatarLog.UILogListener += ReceiveLogMessageForUI;
_isActive = true;
}
private void OnDestroy()
{
if (logLevelDropdown != null)
{
logLevelDropdown.onValueChanged.RemoveListener(HandleDropdownValueChanged);
}
}
private void HandleDropdownValueChanged(int selectedIndex)
{
if (logLevelDropdown == null)
{
OvrAvatarLog.LogError("UILogger::HandleDropdownValueChanged : Null logLevelDropdown reference. Make sure this field is assigned in Editor.", logScope);
return;
}
var selectedOption = logLevelDropdown.options[selectedIndex].text;
selectedOption = RemoveColorTag(selectedOption);
if (!Enum.TryParse(selectedOption, out OvrAvatarLog.ELogLevel logLevel))
{
OvrAvatarLog.LogError($"UILogger::HandleDropdownValueChanged : Log level could not be parsed from logLevelDropdown options.", logScope);
return;
}
SetMinLogLevel(logLevel);
}
private void SetMinLogLevel(OvrAvatarLog.ELogLevel level)
{
minLogLevel = level;
s_displayLogCacheQueue.Clear();
foreach (var log in s_logCacheQueue)
{
if (IsAtOrAboveMinLogLevel(log))
{
s_displayLogCacheQueue.Enqueue(log);
}
}
_displayLogCacheQueueChanged = true;
UpdateLoggerUI(true);
}
public void ActivateUILogger()
{
_isActive = true;
UpdateLoggerUI(true);
UIInputController.SetUISubMenuNavigationEnabled(true);
}
public void DeactivateUILogger()
{
_isActive = false;
UIInputController.SetUISubMenuNavigationEnabled(false);
}
public void SelectNextLogExportOption()
{
if (_selectedButton != null)
{
_selectedButton.transform.localScale = Vector3.one;
}
_selectedButton = _selectedButton == null || _selectedButton == exportLogsFilteredButton
? exportLogsButton
: exportLogsFilteredButton;
if (_selectedButton == null)
{
OvrAvatarLog.LogError("UILogger::SelectNextLogExportOption : Null selected button.", logScope);
return;
}
_selectedButton.transform.localScale = Vector3.one * SELECTED_SUB_MENU_SCALE_FACTOR;
}
public void ApplyCurrentButton()
{
if (_selectedButton == null)
{
return;
}
_selectedButton.onClick.Invoke();
}
private string GetHTMLColorCodeFromLogLevel(OvrAvatarLog.ELogLevel logLevel)
{
var colorString = logLevel switch
{
OvrAvatarLog.ELogLevel.Verbose => "white",
OvrAvatarLog.ELogLevel.Debug => "white",
OvrAvatarLog.ELogLevel.Info => "white",
OvrAvatarLog.ELogLevel.Warn => "yellow",
OvrAvatarLog.ELogLevel.Error => "red",
_ => "white"
};
return colorString;
}
private string GetPostPrefixFromLogLevel(OvrAvatarLog.ELogLevel logLevel)
{
var postPrefix = logLevel switch
{
OvrAvatarLog.ELogLevel.Verbose => "[Verbose]",
OvrAvatarLog.ELogLevel.Debug => "[Debug]",
_ => ""
};
return postPrefix;
}
private string AddColorToTextFromLogLevel(string text, OvrAvatarLog.ELogLevel logLevel)
{
var textColor = GetHTMLColorCodeFromLogLevel(logLevel);
return $"<color=\"{textColor}\">{text}</color>";
}
private string RemoveColorTag(string input)
{
var pattern = @"<color=""[^""]*"">|</color>";
var output = Regex.Replace(input, pattern, "");
return output;
}
public void DetachUILogger()
{
OvrAvatarLog.UILogListener -= ReceiveLogMessageForUI;
}
private void ReceiveLogMessageForUI(OvrAvatarLog.ELogLevel logLevel, string msg, string prefix)
{
if (!_isActive)
{
return;
}
var postPrefix = GetPostPrefixFromLogLevel(logLevel);
var formattedLogText = $"{logLevel}|" + AddColorToTextFromLogLevel($"{prefix}{postPrefix} {msg}", logLevel);
s_logCacheQueue.Enqueue(formattedLogText);
if (s_logCacheQueue.Count > s_logCacheSize)
{
s_logCacheQueue.Dequeue();
}
if (IsAtOrAboveMinLogLevel(formattedLogText))
{
if (s_displayLogCacheQueue.Count > s_displayLogCacheSize)
{
s_displayLogCacheQueue.Dequeue();
}
s_displayLogCacheQueue.Enqueue(formattedLogText);
_displayLogCacheQueueChanged = true;
}
}
private void UpdateLoggerUI(bool forceUpdate = false)
{
if (!_displayLogCacheQueueChanged && !forceUpdate)
{
return;
}
var logs = new string[s_displayLogCacheQueue.Count];
s_displayLogCacheQueue.CopyTo(logs, 0);
var start = Math.Max(0, logs.Length - logViewCapacity);
s_logQueueText = "\n";
for (var i = start; i < logs.Length; i++)
{
s_logQueueText += logs[i].Substring(logs[i].IndexOf('|') + 1) + "\n";
}
if (logText != null)
{
logText.text = s_logQueueText;
}
_displayLogCacheQueueChanged = false;
}
private bool IsAtOrAboveMinLogLevel(string log)
{
if (string.IsNullOrEmpty(log))
{
return false;
}
string[] parts = log.Split('|');
if (parts.Length < 2)
{
OvrAvatarLog.LogError($"UILogger::IsAtOrAboveMinLogLevel : Log message is not in the expected format {log}", logScope);
return false;
}
if (!Enum.TryParse(parts[0], out OvrAvatarLog.ELogLevel logLevel))
{
OvrAvatarLog.LogError($"UILogger::IsAtOrAboveMinLogLevel : Log level could not be parsed from the following log: {log}", logScope);
return false;
}
return logLevel >= minLogLevel;
}
private string GetLogExportFilePath()
{
return Path.Combine(Application.persistentDataPath, "AvatarsSDKUI_Logs_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt");
}
private void ExportAllLogs()
{
var path = GetLogExportFilePath();
using (StreamWriter writer = new StreamWriter(path))
{
string logQueueText = "\n";
foreach (var log in s_logCacheQueue)
{
logQueueText += RemoveColorTag(log.Substring(log.IndexOf('|') + 1)) + "\n";
}
writer.Write(logQueueText);
}
OvrAvatarLog.LogWarning($"UILogger::ExportLogs : Logs exported to \"{path}\"");
}
private void ExportFilteredLogs()
{
var path = GetLogExportFilePath();
using (StreamWriter writer = new StreamWriter(path))
{
string logQueueText = "\n";
foreach (var log in s_logCacheQueue)
{
if (IsAtOrAboveMinLogLevel(log))
{
logQueueText += RemoveColorTag(log.Substring(log.IndexOf('|') + 1)) + "\n";
}
}
writer.Write(logQueueText);
}
OvrAvatarLog.LogWarning($"UILogger::ExportFilteredLogs : Logs exported to \"{path}\"");
}
private void Update()
{
if (!UIManager.IsPaused)
{
return;
}
if (logText != null && _displayLogCacheQueueChanged)
{
UpdateLoggerUI();
}
#if USING_XR_SDK
if (OVRInput.GetUp(_increaseLogLevelButton.button, _increaseLogLevelButton.controller)) // increase log level
{
if (logLevelDropdown != null)
{
logLevelDropdown.value++;
}
}
if (OVRInput.GetUp(_decreaseLogLevelButton.button, _decreaseLogLevelButton.controller)) // decrease log level
{
if (logLevelDropdown != null)
{
logLevelDropdown.value--;
}
}
#endif
}
#if USING_XR_SDK
public List<UIInputControllerButton> GetControlSchema()
{
var buttons = new List<UIInputControllerButton>
{
_increaseLogLevelButton,
_decreaseLogLevelButton,
};
return buttons;
}
#endif
}