Game/Library/PackageCache/com.unity.package-manager-ui@2.0.13/Editor/Sources/Services/Packages/PackageCollection.cs
2021-01-16 18:29:12 +01:00

375 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.PackageManager.UI
{
[Serializable]
internal class PackageCollection
{
private const string k_UnityPackage = "Unity";
private static PackageCollection instance = new PackageCollection();
public static PackageCollection Instance { get { return instance; } }
public event Action<IEnumerable<Package>> OnPackagesChanged = delegate {};
public event Action<PackageFilter> OnFilterChanged = delegate {};
private readonly Dictionary<string, Package> packages;
private PackageFilter filter;
private string selectedListPackage;
private string selectedSearchUnityPackage;
private string selectedSearchOtherPackage;
private List<string> collapsedListGroups;
private List<string> collapsedSearchUnityGroups;
private List<string> collapsedSearchOtherGroups;
internal string lastUpdateTime;
private List<PackageInfo> listPackagesOffline;
private List<PackageInfo> listPackages;
private List<PackageInfo> searchUnityPackages;
private List<PackageInfo> searchOtherPackages;
private List<PackageError> packageErrors;
private int listPackagesVersion;
private int listPackagesOfflineVersion;
private bool searchOperationOngoing;
private bool listOperationOngoing;
private bool listOperationOfflineOngoing;
private IListOperation listOperationOffline;
private IListOperation listOperation;
private ISearchOperation searchOperation;
public readonly OperationSignal<ISearchOperation> SearchSignal = new OperationSignal<ISearchOperation>();
public readonly OperationSignal<IListOperation> ListSignal = new OperationSignal<IListOperation>();
public static void InitInstance(ref PackageCollection value)
{
if (value == null) // UI window opened
{
value = instance;
Instance.OnPackagesChanged = delegate {};
Instance.OnFilterChanged = delegate {};
Instance.SearchSignal.ResetEvents();
Instance.ListSignal.ResetEvents();
Instance.FetchListOfflineCache(true);
Instance.FetchListCache(true);
Instance.FetchSearchCache(true);
}
else // Domain reload
{
instance = value;
Instance.RebuildPackageDictionary();
// Resume operations interrupted by domain reload
Instance.FetchListOfflineCache(Instance.listOperationOfflineOngoing);
Instance.FetchListCache(Instance.listOperationOngoing);
Instance.FetchSearchCache(Instance.searchOperationOngoing);
}
}
public PackageFilter Filter
{
get { return filter; }
// For public usage, use SetFilter() instead
private set
{
var changed = value != filter;
if (changed)
{
var selectedPackageName = SelectedPackage;
Package package;
if (!string.IsNullOrEmpty(selectedPackageName) && packages.TryGetValue(selectedPackageName, out package))
{
var groupName = GetGroupName(package);
if (CollapsedGroups.Contains(groupName))
CollapsedGroups.Remove(groupName);
}
}
filter = value;
if (changed)
OnFilterChanged(filter);
}
}
public List<PackageInfo> LatestListPackages
{
get { return listPackagesVersion > listPackagesOfflineVersion ? listPackages : listPackagesOffline; }
}
public List<PackageInfo> LatestSearchUnityPackages { get { return searchUnityPackages; } }
public List<PackageInfo> LatestSearchOtherPackages { get { return searchOtherPackages; } }
public string SelectedPackage
{
get
{
switch (Filter)
{
case PackageFilter.Unity:
return selectedSearchUnityPackage;
case PackageFilter.Other:
return selectedSearchOtherPackage;
default:
return selectedListPackage;
}
}
set
{
switch (Filter)
{
case PackageFilter.Unity:
selectedSearchUnityPackage = value;
break;
case PackageFilter.Other:
selectedSearchOtherPackage = value;
break;
default:
selectedListPackage = value;
break;
}
}
}
public List<string> CollapsedGroups
{
get
{
switch (Filter)
{
case PackageFilter.Unity:
return collapsedSearchUnityGroups;
case PackageFilter.Other:
return collapsedSearchOtherGroups;
case PackageFilter.Local:
return collapsedListGroups;
default:
return new List<string>();
}
}
}
public static string GetGroupName(Package package)
{
if (package.IsBuiltIn)
return PackageGroupOrigins.BuiltInPackages.ToString();
else if (package.IsUnityPackage)
return k_UnityPackage;
else
return package.Latest != null ? package.Latest.Author : package.Current.Author;
}
private PackageCollection()
{
packages = new Dictionary<string, Package>();
listPackagesOffline = new List<PackageInfo>();
listPackages = new List<PackageInfo>();
searchUnityPackages = new List<PackageInfo>();
searchOtherPackages = new List<PackageInfo>();
collapsedListGroups = new List<string>();
collapsedSearchUnityGroups = new List<string>();
collapsedSearchOtherGroups = new List<string>();
packageErrors = new List<PackageError>();
listPackagesVersion = 0;
listPackagesOfflineVersion = 0;
searchOperationOngoing = false;
listOperationOngoing = false;
listOperationOfflineOngoing = false;
Filter = PackageFilter.Unity;
}
public bool SetFilter(PackageFilter value, bool refresh = true)
{
if (value == Filter)
return false;
Filter = value;
if (refresh)
{
UpdatePackageCollection();
}
return true;
}
public void UpdatePackageCollection(bool rebuildDictionary = false)
{
if (rebuildDictionary)
{
lastUpdateTime = DateTime.Now.ToString("HH:mm");
RebuildPackageDictionary();
}
if (packages.Any())
OnPackagesChanged(OrderedPackages());
}
internal void FetchListOfflineCache(bool forceRefetch = false)
{
if (!forceRefetch && (listOperationOfflineOngoing || listPackagesOffline.Any())) return;
if (listOperationOffline != null)
listOperationOffline.Cancel();
listOperationOfflineOngoing = true;
listOperationOffline = OperationFactory.Instance.CreateListOperation(true);
listOperationOffline.OnOperationFinalized += () =>
{
listOperationOfflineOngoing = false;
UpdatePackageCollection(true);
};
listOperationOffline.GetPackageListAsync(
infos =>
{
var version = listPackagesVersion;
UpdateListPackageInfosOffline(infos, version);
},
error => { Debug.LogError("Error fetching package list (offline mode)."); });
}
internal void FetchListCache(bool forceRefetch = false)
{
if (!forceRefetch && (listOperationOngoing || listPackages.Any())) return;
if (listOperation != null)
listOperation.Cancel();
listOperationOngoing = true;
listOperation = OperationFactory.Instance.CreateListOperation();
listOperation.OnOperationFinalized += () =>
{
listOperationOngoing = false;
UpdatePackageCollection(true);
};
listOperation.GetPackageListAsync(UpdateListPackageInfos,
error => { Debug.LogError("Error fetching package list."); });
ListSignal.SetOperation(listOperation);
}
internal void FetchSearchCache(bool forceRefetch = false)
{
if (!forceRefetch && (searchOperationOngoing || searchUnityPackages.Any() || searchOtherPackages.Any())) return;
if (searchOperation != null)
searchOperation.Cancel();
searchOperationOngoing = true;
searchOperation = OperationFactory.Instance.CreateSearchOperation();
searchOperation.OnOperationFinalized += () =>
{
searchOperationOngoing = false;
UpdatePackageCollection(true);
};
searchOperation.GetAllPackageAsync(UpdateSearchPackageInfos,
error => { Debug.LogError("Error searching packages online."); });
SearchSignal.SetOperation(searchOperation);
}
private void UpdateListPackageInfosOffline(IEnumerable<PackageInfo> newInfos, int version)
{
listPackagesOfflineVersion = version;
listPackagesOffline = newInfos.Where(p => p.IsUserVisible).ToList();
}
private void UpdateListPackageInfos(IEnumerable<PackageInfo> newInfos)
{
// Each time we fetch list packages, the cache for offline mode will be updated
// We keep track of the list packages version so that we know which version of cache
// we are getting with the offline fetch operation.
listPackagesVersion++;
listPackages = newInfos.Where(p => p.IsUserVisible).ToList();
listPackagesOffline = listPackages;
}
private void UpdateSearchPackageInfos(IEnumerable<PackageInfo> newInfos)
{
searchUnityPackages = newInfos.Where(p => p.IsUserVisible && p.IsUnityPackage).ToList();
searchOtherPackages = newInfos.Where(p => p.IsUserVisible && !p.IsUnityPackage).ToList();
if (!searchOtherPackages.Any() && Filter == PackageFilter.Other)
{
SetFilter(PackageFilter.Unity);
}
}
private IEnumerable<Package> OrderedPackages()
{
return packages.Values.OrderByDescending(pkg => pkg.IsUnityPackage).
ThenBy(pkg => pkg.VersionToDisplay.Author == "Other").
ThenBy(pkg => pkg.VersionToDisplay.Author).
ThenBy(pkg => pkg.Versions.LastOrDefault() == null ? pkg.Name : pkg.Versions.Last().DisplayName);
}
public Package GetPackageByName(string name)
{
Package package;
packages.TryGetValue(name, out package);
return package;
}
public Error GetPackageError(Package package)
{
if (null == package) return null;
var firstMatchingError = packageErrors.FirstOrDefault(p => p.PackageName == package.Name);
return firstMatchingError != null ? firstMatchingError.Error : null;
}
public void AddPackageError(Package package, Error error)
{
if (null == package || null == error) return;
packageErrors.Add(new PackageError(package.Name, error));
}
public void RemovePackageErrors(Package package)
{
if (null == package) return;
packageErrors.RemoveAll(p => p.PackageName == package.Name);
}
public void ResetExpandedGroups()
{
collapsedListGroups = new List<string>();
collapsedSearchUnityGroups = new List<string>();
collapsedSearchOtherGroups = new List<string>();
}
private void RebuildPackageDictionary()
{
// Merge list & search packages
var allPackageInfos = new List<PackageInfo>(LatestListPackages);
var installedPackageIds = new HashSet<string>(allPackageInfos.Select(p => p.PackageId));
allPackageInfos.AddRange(searchUnityPackages.Where(p => !installedPackageIds.Contains(p.PackageId)));
allPackageInfos.AddRange(searchOtherPackages.Where(p => !installedPackageIds.Contains(p.PackageId)));
if (!PackageManagerPrefs.ShowPreviewPackages)
{
allPackageInfos = allPackageInfos.Where(p => !p.IsPreRelease || installedPackageIds.Contains(p.PackageId)).ToList();
}
// Rebuild packages dictionary
packages.Clear();
foreach (var p in allPackageInfos)
{
var packageName = p.Name;
if (packages.ContainsKey(packageName))
continue;
var packageQuery = from pkg in allPackageInfos where pkg.Name == packageName select pkg;
var package = new Package(packageName, packageQuery);
packages[packageName] = package;
}
}
}
}