// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // 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. using System.Collections.Generic; using System.Linq; using Google.Android.AppBundle.Editor.Internal.Config; using UnityEditor; using UnityEngine; namespace Google.Android.AppBundle.Editor.Internal.AssetPacks { /// /// Internal-only representation of asset packs being considered for packaging in an Android App Bundle. /// public class AssetDeliveryConfig { /// /// Default texture compression format for building standalone APKs for Android pre-Lollipop devices. /// public TextureCompressionFormat DefaultTextureCompressionFormat = TextureCompressionFormat.Default; /// /// Whether to split the assets in an AAB's base module into a separate install-time asset pack. /// public bool SplitBaseModuleAssets; /// /// Dictionary from a folder path to a AssetBundleFolder object describing the folder's contents. /// public readonly IDictionary Folders = new SortedDictionary(); /// /// Dictionary from AssetBundle name to AssetBundlePacks. /// public IDictionary AssetBundlePacks { get; private set; } // TODO(b/150701341): add support for managing this type of asset pack in the UI. public List rawAssetsPacks; private readonly IEnumerable _packValidators; public AssetDeliveryConfig() { AssetBundlePacks = new Dictionary(); _packValidators = AssetPackValidatorRegistry.Registry.ConstructInstances(); } /// /// Updates the in-memory state based on the AssetBundles on disk. /// public void Refresh() { // Refresh the AssetBundlePacks according to AssetBundles contained in folders. RefreshAssetBundlePacks(); // Check for errors. foreach (var assetBundlePack in AssetBundlePacks.Values) { CheckBuildTypeErrors(assetBundlePack); assetBundlePack.CheckDependencyErrors( (string name, out AssetBundlePack pack) => AssetBundlePacks.TryGetValue(name, out pack)); } } /// /// Potentially refresh all folders and/or add and remove the specified folders. /// /// true if changes were made. public bool UpdateAndRefreshFolders(bool refreshFolders, List foldersToAdd, List foldersToRemove) { if (!refreshFolders && foldersToRemove.Count == 0 && foldersToAdd.Count == 0) { return false; } foreach (var folder in foldersToAdd) { if (Folders.ContainsKey(folder)) { Debug.LogWarningFormat("Skipping folder \"{0}\" because it is already in the list.", folder); } else { Folders.Add(folder, new AssetBundleFolder(folder)); } } foreach (var folder in foldersToRemove) { Folders.Remove(folder); } Refresh(); return true; } /// /// Return a list of error messages that are applicable to AssetBundles marked for packaging/delivery /// along with the number of AssetBundles that are marked for packaging/delivery. /// public List GetPackagingErrorMessages(out int numAssetPacksToDeliver) { // Check for errors in asset packs numAssetPacksToDeliver = 0; var errors = new List(); foreach (var assetBundlePack in AssetBundlePacks.Values) { if (assetBundlePack.DeliveryMode == AssetPackDeliveryMode.DoNotPackage) { continue; } var errorSummary = assetBundlePack.ErrorSummary; if (errorSummary != null) { errors.Add(string.Format( "Unable to package AssetBundle pack \"{0}\" because {1}.", assetBundlePack.Name, errorSummary.ToLower())); } numAssetPacksToDeliver++; foreach (var variant in assetBundlePack.Variants.Values) { var variantErrorSummary = variant.ErrorSummary; if (variantErrorSummary != null) { errors.Add(string.Format( "Unable to package AssetBundle \"{0}\" because {1}.", variant.Path, variantErrorSummary.ToLower())); } } } // Check for errors in the rest of the configuration. if (HasTextureCompressionFormatTargeting() && !GetAllTextureCompressionFormats().Contains(DefaultTextureCompressionFormat)) { errors.Add(string.Format( "The default texture compression format ({0}) is not used by any AssetBundles.", DefaultTextureCompressionFormat.ToString())); } // TODO: think about moving to a build check. if (HasTextureCompressionFormatTargeting() && EditorUserBuildSettings.androidBuildSubtarget != MobileTextureSubtarget.Generic) { errors.Add(string.Format( "Texture Compression in the Build Settings for Android is set to \"{0}\". This is not compatible with asset packs having targeted textures. Set the texture compression in the Build Settings for Android to \"Don't override'\" before continuing.", EditorUserBuildSettings.androidBuildSubtarget)); } return errors; } /// /// Refresh the list of all the represented by the folders. /// public void RefreshAssetBundlePacks() { var assetBundlePacks = new Dictionary(); foreach (var folder in Folders.Values) { var assetPacks = folder.ExtractAssetBundleVariant(); foreach (var targetedAssetPackPair in assetPacks) { var targetedAssetPackName = targetedAssetPackPair.Key; var targetedAssetPack = targetedAssetPackPair.Value; AssetBundlePack assetBundlePack; if (!assetBundlePacks.TryGetValue(targetedAssetPackName, out assetBundlePack)) { var deliveryMode = GetMultiTargetingAssetPackDeliveryMode(targetedAssetPackName); assetBundlePack = new AssetBundlePack(targetedAssetPackName, deliveryMode); assetBundlePacks.Add(targetedAssetPackName, assetBundlePack); } if (!assetBundlePack.Add(folder.TextureCompressionFormat, targetedAssetPack)) { assetBundlePack.Errors.Add(AssetPackError.DuplicateName); } } } AssetBundlePacks = assetBundlePacks; } /// /// Returns the delivery mode for the AssetBundlePack with the given name, or DoNotPackage if not found. /// private AssetPackDeliveryMode GetMultiTargetingAssetPackDeliveryMode(string multiTargetingAssetPackName) { AssetBundlePack assetBundlePack; if (AssetBundlePacks.TryGetValue(multiTargetingAssetPackName, out assetBundlePack)) { return assetBundlePack.DeliveryMode; } return AssetPackDeliveryMode.DoNotPackage; } /// /// Returns all texture compression formats used by asset packs. /// public IEnumerable GetAllTextureCompressionFormats() { return AssetBundlePacks.Values.SelectMany( multiTargetingAssetPack => multiTargetingAssetPack.Variants.Keys).Distinct(); } /// /// Check if some asset packs are using targeted texture compression format. /// public bool HasTextureCompressionFormatTargeting() { return GetAllTextureCompressionFormats().Any(textureCompressionFormat => textureCompressionFormat != TextureCompressionFormat.Default); } /// /// Checks the specified bundlePack for build errors using validators registered in AssetPackValidatorRegistry. /// private void CheckBuildTypeErrors(AssetBundlePack bundlePack) { var buildErrors = new HashSet(); foreach (var buildValidator in _packValidators) { buildErrors.UnionWith(buildValidator.CheckBuildErrors(bundlePack)); } foreach (var error in buildErrors) { bundlePack.AddErrorToVariants(error); } } } }