using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; namespace UnityEditor.PackageManager.UI { // History of a single package internal class Package : IEquatable { static public bool ShouldProposeLatestVersions { get { // Until we figure out a way to test this properly, alway show standard behavior // return InternalEditorUtility.IsUnityBeta() && !Unsupported.IsDeveloperMode(); return false; } } // There can only be one package add/remove operation. private static IBaseOperation addRemoveOperationInstance; public static bool AddRemoveOperationInProgress { get { return addRemoveOperationInstance != null && !addRemoveOperationInstance.IsCompleted; } } internal const string packageManagerUIName = "com.unity.package-manager-ui"; private readonly string packageName; private IEnumerable source; internal Package(string packageName, IEnumerable infos) { if (string.IsNullOrEmpty(packageName)) throw new ArgumentException("Cannot be empty or null", "packageName"); if (!infos.Any()) throw new ArgumentException("Cannot be empty", "infos"); this.packageName = packageName; UpdateSource(infos); } internal void UpdateSource(IEnumerable source) { this.source = source; #if UNITY_2018_3_OR_NEWER if (IsPackageManagerUI) this.source = this.source.Where(p => p != null && p.Version.Major >= 2); #endif } public PackageInfo Current { get { return Versions.FirstOrDefault(package => package.IsCurrent); } } // This is the latest verified or official release (eg: 1.3.2). Not necessarily the latest verified release (eg: 1.2.4) or that latest candidate (eg: 1.4.0-beta) public PackageInfo LatestUpdate { get { // We want to show the absolute latest when in beta mode if (ShouldProposeLatestVersions) return Latest; // Override with current when it's version locked var current = Current; if (current != null && current.IsVersionLocked) return current; // Get all the candidates versions (verified, release, preview) that are newer than current var verified = Verified; var latestRelease = LatestRelease; var latestPreview = Versions.LastOrDefault(package => package.IsPreview); var candidates = new List { verified, latestRelease, latestPreview, }.Where(package => package != null && (current == null || current == package || current.Version < package.Version)).ToList(); if (candidates.Contains(verified)) return verified; if ((current == null || !current.IsVerified) && candidates.Contains(latestRelease)) return latestRelease; if ((current == null || current.IsPreview) && candidates.Contains(latestPreview)) return latestPreview; // Show current if it exists, otherwise latest user visible, and then otherwise show the absolute latest return current ?? Latest; } } public PackageInfo LatestPatch { get { if (Current == null) return null; // Get all version that have the same Major/Minor var versions = Versions.Where(package => package.Version.Major == Current.Version.Major && package.Version.Minor == Current.Version.Minor); return versions.LastOrDefault(); } } // This is the very latest version, including pre-releases (eg: 1.4.0-beta). internal PackageInfo Latest { get { return Versions.FirstOrDefault(package => package.IsLatest) ?? Versions.LastOrDefault(); } } // Returns the current version if it exist, otherwise returns the latest user visible version. internal PackageInfo VersionToDisplay { get { return Current ?? LatestUpdate; } } // Every version available for this package internal IEnumerable Versions { get { return source.OrderBy(package => package.Version); } } // Every version that's not a pre-release (eg: not beta/alpha/preview). internal IEnumerable ReleaseVersions { get { return Versions.Where(package => !package.IsPreRelease); } } internal PackageInfo LatestRelease { get {return ReleaseVersions.LastOrDefault();}} internal PackageInfo Verified { get {return Versions.FirstOrDefault(package => package.IsVerified);}} internal bool IsAfterCurrentVersion(PackageInfo packageInfo) { return Current == null || (packageInfo != null && packageInfo.Version > Current.Version); } internal bool IsBuiltIn {get { return Versions.Any() && Versions.First().IsBuiltIn; }} internal bool IsUnityPackage { get { return VersionToDisplay.IsUnityPackage; } } public string Name { get { return packageName; } } public bool IsPackageManagerUI { get { return Name == packageManagerUIName; } } public bool Equals(Package other) { if (other == null) return false; return packageName == other.packageName; } public override int GetHashCode() { return packageName.GetHashCode(); } [SerializeField] internal readonly OperationSignal AddSignal = new OperationSignal(); private Action OnAddOperationFinalizedEvent; internal void Add(PackageInfo packageInfo) { if (packageInfo == Current || AddRemoveOperationInProgress) return; var operation = OperationFactory.Instance.CreateAddOperation(); addRemoveOperationInstance = operation; OnAddOperationFinalizedEvent = () => { AddSignal.Operation = null; operation.OnOperationFinalized -= OnAddOperationFinalizedEvent; PackageCollection.Instance.FetchListOfflineCache(true); }; operation.OnOperationFinalized += OnAddOperationFinalizedEvent; AddSignal.SetOperation(operation); operation.AddPackageAsync(packageInfo); } internal void Update() { Add(Latest); } internal static void AddFromLocalDisk(string path) { if (AddRemoveOperationInProgress) return; var packageJson = PackageJsonHelper.Load(path); if (null == packageJson) { Debug.LogError(string.Format("Invalid package path: cannot find \"{0}\".", path)); return; } var operation = OperationFactory.Instance.CreateAddOperation(); addRemoveOperationInstance = operation; operation.AddPackageAsync(packageJson.PackageInfo); } [SerializeField] internal readonly OperationSignal RemoveSignal = new OperationSignal(); private Action OnRemoveOperationFinalizedEvent; public void Remove() { if (Current == null || AddRemoveOperationInProgress) return; var operation = OperationFactory.Instance.CreateRemoveOperation(); addRemoveOperationInstance = operation; OnRemoveOperationFinalizedEvent = () => { RemoveSignal.Operation = null; operation.OnOperationFinalized -= OnRemoveOperationFinalizedEvent; PackageCollection.Instance.FetchListOfflineCache(true); }; operation.OnOperationFinalized += OnRemoveOperationFinalizedEvent; RemoveSignal.SetOperation(operation); operation.RemovePackageAsync(Current); } } }