using MagicLeap.Settings.Android;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine.UIElements;
using static MagicLeap.Settings.Android.Permission;
namespace MagicLeap.Settings
{
internal class PermissionsSettingsProviderPresenter : SettingsProvider
{
private const string PathToUss =
@"Packages\com.magicleap.unitysdk\Editor\SettingsProviders\Permissions\SettingsProviderStyle.uss";
private const string instructionsText = "Use this page to add or remove permissions to your AndroidManifest.xml template located " +
"in Assets/Plugins/Android. Change the Minimum API Level dropdown to filter the list of permissions by their minimum API level.";
private const string missingManifestText = "AndroidManifest.xml not found. If you want to add or remove permissions from this page, " +
"you should first create a template AndroidManifest.xml under Assets/Plugins/Android. Go to Player Settings -> Android -> Publishing " +
"Settings and enable \"Custom Main Manifest\" to automatically generate the template.";
private VisualElement root;
private VisualElement scrollView;
private DropdownField ApiLevelDropdown;
private readonly string settingName;
private readonly Dictionary<ProtectionLevel, Foldout> foldoutGroups = new();
private readonly Dictionary<string, PermissionSettingsProperty> renderedSettingProperties = new();
private List<Permission> permissions = new List<Permission>();
private PermissionGroup[] permissionGroups;
private string savedMinimumApiLevelChoice = null;
private List<int> apiLevelDropdownChoices = new List<int>();
private int minimumApiLevelChosen = 1;
private readonly static IReadOnlyCollection<string> mandatoryPermissions = new HashSet<string> { };
AndroidManifestXml androidManifest;
internal PermissionsSettingsProviderPresenter(
string path,
string settingName) : base(path,
SettingsScope.Project)
{
this.settingName = settingName;
if (File.Exists(AndroidManifestXml.AssetPath))
{
androidManifest = new AndroidManifestXml(AndroidManifestXml.AssetPath);
foreach (var mandatoryPermission in mandatoryPermissions)
{
androidManifest.AddPermission(mandatoryPermission);
}
androidManifest.Save();
}
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
base.OnActivate(searchContext, rootElement);
rootElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(PathToUss));
root = rootElement;
PermissionSettingsLoader permissionSettingsLoader = new PermissionSettingsLoader();
PermissionJson[] jsonEntries = permissionSettingsLoader.settingJson.Settings;
permissions = PermissionParser.ParsePermissionsFromArray(jsonEntries);
savedMinimumApiLevelChoice = $"API Level {MagicLeapProjectSettings.Instance.PermissionsMinApiLevel}";
var groups = new Dictionary<string, List<Permission>>();
foreach (var permission in permissions)
{
var name = permission.Level.ToString();
if (!groups.ContainsKey(name))
{
groups[name] = new List<Permission>();
}
groups[name].Add(permission);
}
var groupList = new List<PermissionGroup>();
foreach (var group in groups)
{
var permissionGroup = new PermissionGroup();
permissionGroup.Name = group.Key;
permissionGroup.Permissions = group.Value.ToArray();
groupList.Add(permissionGroup);
}
permissionGroups = groupList.ToArray();
RenderView();
}
#region Render functions
private void SynchronizeView()
{
foldoutGroups.Clear();
renderedSettingProperties.Clear();
RenderView();
}
private void RenderView()
{
renderedSettingProperties.Clear();
root.Clear();
scrollView = new ScrollView() { mode = ScrollViewMode.Vertical };
root.Add(scrollView);
RenderLabel();
RenderInstructions();
RenderFields();
}
private void RenderLabel()
{
Label label = new(settingName);
label.AddToClassList("settings-label");
scrollView.Add(label);
}
private void RenderInstructions()
{
HelpBox instructions = new HelpBox();
if (androidManifest != null)
{
instructions.text = instructionsText;
instructions.messageType = HelpBoxMessageType.Info;
}
else
{
instructions.text = missingManifestText;
instructions.messageType = HelpBoxMessageType.Warning;
}
instructions.AddToClassList("settings-help-box");
root.Add(instructions);
}
private void RenderFields()
{
renderedSettingProperties.Clear();
foldoutGroups.Clear();
RenderApiLevel();
foreach (var group in permissionGroups)
{
RenderGroup(group);
}
}
private void RenderApiLevel()
{
ApiLevelDropdown = new DropdownField("Minimum API Level");
int minimumApiLevel = permissions.Min(p => p.MinimumApiLevel);
int maximumApiLevel = permissions.Max(p => p.MinimumApiLevel);
for (int i = minimumApiLevel; i <= maximumApiLevel; ++i)
{
apiLevelDropdownChoices.Add(i);
ApiLevelDropdown.choices.Add($"API Level {i}");
}
var currentChoice = savedMinimumApiLevelChoice;
int selectedIndex = ApiLevelDropdown.choices.IndexOf(currentChoice);
minimumApiLevelChosen = (selectedIndex >= 0) ? apiLevelDropdownChoices[selectedIndex] : minimumApiLevel;
ApiLevelDropdown.SetValueWithoutNotify(currentChoice);
ApiLevelDropdown.RegisterValueChangedCallback(OnApiLevelChange);
scrollView.Add(ApiLevelDropdown);
}
private void OnApiLevelChange(ChangeEvent<string> evt)
{
savedMinimumApiLevelChoice = evt.newValue;
minimumApiLevelChosen = apiLevelDropdownChoices[ApiLevelDropdown.choices.IndexOf(savedMinimumApiLevelChoice)];
if (int.TryParse(savedMinimumApiLevelChoice.Split(' ')[2], out int apiLevel))
{
MagicLeapProjectSettings.Instance.PermissionsMinApiLevel = apiLevel;
}
RenderView();
}
private void RenderGroup(PermissionGroup permissionGroup)
{
foreach (var permission in permissionGroup.permissions)
{
if (minimumApiLevelChosen >= permission.MinimumApiLevel)
{
RenderPermission(permission);
}
}
}
private void RenderPermission(Permission permission)
{
var level = permission.Level;
var isMandatory = mandatoryPermissions.Contains(permission.Name);
var property = new PermissionSettingsProperty(permission, isMandatory);
if (!foldoutGroups.TryGetValue(level, out _))
{
CreateFoldout(level);
}
foldoutGroups.TryGetValue(level, out Foldout foldout);
property.RenderProperty(foldout);
if (androidManifest != null)
{
List<string> manifestPermissions = new List<string>(androidManifest.GetIncludedPermissions());
property.Toggle.SetValueWithoutNotify(manifestPermissions.Contains(permission.Name));
property.Toggle.RegisterValueChangedCallback(OnPermissionToggled);
}
if (!renderedSettingProperties.TryGetValue(permission.Name, out _))
{
renderedSettingProperties.Add(permission.Name, property);
}
}
private void OnPermissionToggled(ChangeEvent<bool> evt)
{
AssetDatabase.Refresh();
if (File.Exists(AndroidManifestXml.AssetPath))
{
androidManifest = new AndroidManifestXml(AndroidManifestXml.AssetPath);
}
else
{
androidManifest = null;
}
RenderView();
}
private void CreateFoldout(ProtectionLevel category)
{
var foldout = new Foldout();
foldout.AddToClassList("settings-foldout");
foldout.text = category.ToString();
foldout.tooltip = LevelDescriptions[category];
scrollView.Add(foldout);
foldoutGroups.Add(category, foldout);
}
#endregion
}
}