using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
[CreateAssetMenu(fileName = "MLVoiceIntentsConfiguration", menuName = "Magic Leap/Voice Intents Configuration", order = 1)]
///
/// A Class that will maintain the proper JSON structure needed by the MLVoice API.
///
public class MLVoiceIntentsConfiguration : ScriptableObject
{
[System.Flags]
///
/// The verbal System Intents currently supported.
///
public enum SystemIntentFlags
{
TakeAPhoto = 1 << 0,
TakeAVideo = 1 << 1,
StopRecording = 1 << 2,
CloseAPPNAME = 1 << 3,
OpenHome = 1 << 4,
OpenAPPNAME = 1 << 5,
Mute = 1 << 6,
Unmute = 1 << 7,
TurnTheVolumeDown = 1 << 8,
SetTheVolumeToNUMBERPercent = 1 << 9,
TurnTheVolumeUp = 1 << 10,
Help = 1 << 11,
}
[System.Serializable]
///
/// The current structure of the JSON data that will be sent to the MLVoice API. Subject to change.
///
public struct JSONData
{
public string name;
public string id;
public string value;
}
[System.Serializable]
///
/// The current structure of the System Intents JSON data that will be sent to the MLVoice API. Subject to change.
///
public struct SystemJSONData
{
public List name;
}
[System.Serializable]
///
/// The Simplified Voice Command data needed to add commands from the inspector.
/// The unique name field required in the final JSON will be auto generated based on the unique Id.
///
public struct CustomVoiceIntents
{
[Tooltip("Must Be Unique.")]
public uint Id;
[Tooltip("The command to be spoken. Can have 2 phrases seperated by an | to indicate multiple commands triggering the same event.")]
public string Value;
}
[System.Serializable]
///
/// The Slot Data to be used in the CustomVoiceIntents' value. To use a slot, put the SlotData name between { } inside of the CustomVoiceIntents' value.
///
public struct SlotData
{
public string name;
public List values;
}
[System.Serializable]
public struct SlotDataInternal
{
public string name;
public string value;
}
[System.Serializable]
///
/// The container for the data being changed into a JSON string to send to the MLVoice API.
///
public class JSONContainer
{
public List app_intents;
public SystemJSONData sys_intent_list;
public List app_slots;
}
[System.Serializable]
///
/// JSONContainer to Auto allowing all System Intents.
///
private class JSONContainerAutoSystem
{
public List app_intents;
public List app_slots;
}
///
/// JSONContainer that gets built in setupJSONContainer and used in GetJSONString.
///
private JSONContainer container;
///
/// JSONContainer without defined System Intents to Auto Allow all System Intents.
///
private JSONContainerAutoSystem containerAutoSys;
///
/// Used to auto create a unique name field for the final JSON data with the Unique ID provided in the list VoiceCommandsToAdd.
///
private string autoVoiceIntentName = "UnityApp_VoiceIntent_ID";
[HideInInspector]
///
/// The final data properly laid out to be assigned to the container that will be sent to the MLVoice API.
/// VoiceCommandsToAdd will automatically be added to this in the proper format. Can also manually add to this list if
/// it is not desired to do this through the editor inspector. This list follows the current structure of the JSONData
/// and is subject to change.
///
/// Max Voice Intents Supported: 100
///
public List AllVoiceIntents;
[Header("App Specific Voice Commands")]
[Tooltip("Any word followed by a ? will be optional as will any phrase within [ ]. Values are allowed to use | as an OR. Anything after the pipe will be considered a seperate expression. To just use an OR between two words in an expression surround them with ( ). This can also be used for Slots. To indicate use of a slot, put the slot name within { }. Max Voice Intents Supported: 100")]
///
/// The simplified list of Voice Command data to be filled out in the inspector or manually added to. A unique name field will be created
/// based on the Unique Id provided.
///
/// If GetJSONString or GetValues is called, this data will be altered to fit the current proper layout the API needs to function correctly
/// and will be added to AllVoiceIntents, then this list will be cleared as these commands will no longer need to be added.
///
/// Max Voice Intents Supported: 100
///
public List VoiceCommandsToAdd;
[Header("System Voice Commands")]
[Tooltip("If True, will disregard the SystemCommands selected and allow all System Intents. If no System Inents are desired, leave this false and the SystemCommands empty.")]
public bool AutoAllowAllSystemIntents;
[Tooltip("Flag to indicate which System Intents should be enabled from within the application. In an experimental state as there may be issues using voice commands on any pop-up windows that appear because of the enabled system commands. If no System Inents are desired, leave this list empty.")]
public SystemIntentFlags SystemCommands;
[Header("Slot Data")]
[Tooltip("Slot Name should be unique as it will be used as a reference in CustomVoiceIntents values when placed between { }")]
public List SlotsForVoiceCommands;
///
/// List of the system intent names to be added to the JSON file.
///
private List supportedSystemIntents = new List { "ML_CAPTURE_STILL", "ML_CAPTURE_VIDEO_START", "ML_CAPTURE_VIDEO_STOP", "ML_CLOSE" , "ML_GLOBAL_HOME", "ML_LAUNCH" , "ML_SYSAUDIO_MUTE" , "ML_SYSAUDIO_UNMUTE" , "ML_SYSAUDIO_VOLUME_DOWN" , "ML_SYSAUDIO_VOLUME_SET" , "ML_SYSAUDIO_VOLUME_UP" , "ML_GLOBAL_HELP" };
///
/// Return a string of the proper JSON format needed by the Voice Intents API.
///
public string GetJSONString()
{
SetupJSONContainer();
if(AutoAllowAllSystemIntents)
{
SetupJSONContainerAutoSystem();
return JsonUtility.ToJson(containerAutoSys);
}
return JsonUtility.ToJson(container);
}
///
/// Retrieve a list of the current list of voice commands as a string.
///
public List GetValues()
{
SetupJSONContainer();
List values = new List();
for (int i = 0; i < container.app_intents.Count; i++)
{
values.Add(container.app_intents[i].value);
}
return values;
}
private void SetupJSONContainer()
{
container ??= new JSONContainer();
if (VoiceCommandsToAdd.Count > 0)
{
AddCustomVoiceCommands();
}
ValidationCheck();
container.app_intents = new List();
container.app_intents.AddRange(AllVoiceIntents);
container.sys_intent_list = new SystemJSONData();
container.sys_intent_list.name = new List();
container.app_slots = new List();
AddSlotsToJSON();
int index = 0;
foreach(SystemIntentFlags flag in Enum.GetValues(typeof(SystemIntentFlags)))
{
if(SystemCommands.HasFlag(flag))
{
container.sys_intent_list.name.Add(supportedSystemIntents[index]);
}
index++;
}
}
private void SetupJSONContainerAutoSystem()
{
containerAutoSys ??= new JSONContainerAutoSystem();
containerAutoSys.app_intents = new List();
containerAutoSys.app_intents.AddRange(container.app_intents);
containerAutoSys.app_slots = new List();
containerAutoSys.app_slots.AddRange(container.app_slots);
}
private void AddCustomVoiceCommands()
{
foreach (CustomVoiceIntents customCommand in VoiceCommandsToAdd)
{
JSONData newData = new JSONData();
newData.name = autoVoiceIntentName + customCommand.Id.ToString();
newData.id = customCommand.Id.ToString();
newData.value = customCommand.Value;
AllVoiceIntents.Add(newData);
}
VoiceCommandsToAdd.Clear();
}
private void AddSlotsToJSON()
{
if(SlotsForVoiceCommands.Count == 0)
{
return;
}
foreach (SlotData slot in SlotsForVoiceCommands)
{
SlotDataInternal newSlot = new SlotDataInternal();
newSlot.name = slot.name;
StringBuilder slotValueJSONString = new StringBuilder();
slotValueJSONString.Append($"{string.Join("|", slot.values)}");
newSlot.value = slotValueJSONString.ToString();
container.app_slots.Add(newSlot);
}
}
private void ValidationCheck()
{
List Ids = new List();
List Values = new List();
foreach (JSONData data in AllVoiceIntents)
{
Ids.Add(data.id);
if (data.value.Contains("|"))
{
string[] splitValues = data.value.Split("|", StringSplitOptions.RemoveEmptyEntries);
foreach (string split in splitValues)
{
string final = split.Trim();
final = final.ToLower();
Values.Add(final);
}
}
else
{
Values.Add((data.value.Trim()).ToLower());
}
}
var unique_ids = new HashSet(Ids);
var unique_values = new HashSet(Values);
bool idsUnique = unique_ids.Count == Ids.Count;
bool valuesUnique = unique_values.Count == Values.Count;
if (!idsUnique || !valuesUnique)
{
Debug.LogError("The Voice Intents Configuration File has detected duplicate Values or Ids used. This could cause unwanted behaviour.");
}
}
}