// 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);
}
}
}
}