// Copyright 2020 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; using System.Collections.Generic; using System.IO; using System.Linq; namespace Google.Android.AppBundle.Editor { /// /// Configuration for all asset packs that will be packaged in an Android App Bundle (AAB). /// public class AssetPackConfig { /// /// Indicates that the assets in an AAB's base module should be split into a separate install-time asset /// pack. Usually when building a game, some assets are stored in the base module's "assets" directory. /// Games that run into APK size limits may have previously used the "Split Application Binary" /// option to create APK Expansion (*.obb) files, however this is unsupported with AABs. /// If Play Console indicates that the base module exceeds a download size limit, for example 150 MB, /// enabling this option may resolve the issue. See https://developer.android.com/guide/app-bundle. /// public bool SplitBaseModuleAssets; /// /// Dictionary from asset pack name to object containing info such as delivery mode. /// Note: asset pack names must start with a letter and can contain only letters, numbers, and underscores. /// Note: convenience methods such as are recommended to more safely add new /// Asset packs, but this dictionary is available for direct modification for advanced use cases. /// public readonly Dictionary AssetPacks = new Dictionary(); /// /// A dictionary containing the subset of that are marked for delivery. /// Note: the returned Dictionary doesn't indicate whether is enabled. /// public Dictionary DeliveredAssetPacks { get { return AssetPacks .Where(pair => pair.Value.DeliveryMode != AssetPackDeliveryMode.DoNotPackage) .ToDictionary(pair => pair.Key, pair => pair.Value); } } /// /// When asset packs for multiple texture compression formats are present, this specifies the format used /// when building standalone APKs for Android pre-Lollipop devices. /// public TextureCompressionFormat DefaultTextureCompressionFormat = TextureCompressionFormat.Default; /// /// When asset packs for multiple device tiers are present, this specifies the tier used /// when building standalone APKs for Android pre-Lollipop devices. /// public DeviceTier DefaultDeviceTier = 0; /// /// Returns true if this configuration includes at least 1 asset pack that may be packaged in an AAB. /// Return true if is enabled, even if there are no other asset packs. /// public bool HasDeliveredAssetPacks() { return SplitBaseModuleAssets || DeliveredAssetPacks.Any(); } /// /// Package the specified AssetBundle file in its own with the specified delivery mode. /// The name of the created asset pack will match that of the specified AssetBundle file. /// /// The path to a single AssetBundle file. /// The for the asset pack. /// If the asset pack name is invalid. /// If the AssetBundle file doesn't exist. public void AddAssetBundle(string assetBundleFilePath, AssetPackDeliveryMode deliveryMode) { var assetPackName = GetAssetPackName(assetBundleFilePath); AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, AssetBundleFilePath = assetBundleFilePath }; } /// /// Package all raw assets in the specified folder in an with the specified name and /// using the specified delivery mode. /// /// The name of the asset pack. /// /// The path to a directory whose files will be directly copied into the asset pack during app bundle creation. /// /// The for the asset pack. /// If the is invalid. /// If the doesn't exist. public void AddAssetsFolder(string assetPackName, string assetsFolderPath, AssetPackDeliveryMode deliveryMode) { var directoryInfo = new DirectoryInfo(assetsFolderPath); if (!directoryInfo.Exists) { throw new FileNotFoundException("Asset pack directory doesn't exist", assetsFolderPath); } CheckAssetPackName(assetPackName); AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, AssetPackDirectoryPath = assetsFolderPath }; } /// /// Package the specified raw assets in the specified folders, keyed by , /// in an with the specified delivery mode. /// When using Play Asset Delivery APIs, only the folder for the device's preferred texture compression format /// will be delivered. /// /// The name of the asset pack. /// /// A dictionary from to the path of directories of files that will be /// directly copied into the asset pack during app bundle creation. /// The for the asset pack. /// If the dictionary or asset pack name is invalid. public void AddAssetsFolders( string assetPackName, IDictionary compressionFormatToAssetPackDirectoryPath, AssetPackDeliveryMode deliveryMode) { if (compressionFormatToAssetPackDirectoryPath.Count == 0) { throw new ArgumentException("Dictionary should contain at least one path"); } if (compressionFormatToAssetPackDirectoryPath.All(kvp => kvp.Key != TextureCompressionFormat.Default)) { throw new ArgumentException("Dictionary should contain at least one Default compression path"); } CheckAssetPackName(assetPackName); AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, CompressionFormatToAssetPackDirectoryPath = new Dictionary(compressionFormatToAssetPackDirectoryPath) }; } /// /// Package the specified AssetBundle files, which vary only by , in an /// with the specified delivery mode. /// When using Play Asset Delivery APIs, only the AssetBundle for the device's preferred texture compression /// format will be delivered. /// /// /// A dictionary from to AssetBundle files. /// The for the asset pack. /// If the dictionary or asset pack name is invalid. /// If any AssetBundle file doesn't exist. public void AddAssetBundles( IDictionary compressionFormatToAssetBundleFilePath, AssetPackDeliveryMode deliveryMode) { if (compressionFormatToAssetBundleFilePath.Count == 0) { throw new ArgumentException("Dictionary should contain at least one AssetBundle"); } if (compressionFormatToAssetBundleFilePath.All(kvp => kvp.Key != TextureCompressionFormat.Default)) { throw new ArgumentException("Dictionary should contain at least one Default compression AssetBundle"); } var assetPackName = GetAssetPackName(compressionFormatToAssetBundleFilePath.Values.First()); if (compressionFormatToAssetBundleFilePath.Any(kvp => assetPackName != GetAssetPackName(kvp.Value))) { throw new ArgumentException("All AssetBundles in the Dictionary must have the same name"); } AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, CompressionFormatToAssetBundleFilePath = new Dictionary(compressionFormatToAssetBundleFilePath) }; } /// /// Package the specified raw assets in the specified folders, keyed by , /// in an with the specified delivery mode. /// When using Play Asset Delivery APIs, only the AssetBundle for the device's tier will be delivered. /// public void AddAssetsFolders( string assetPackName, IDictionary deviceTierToAssetPackDirectoryPath, AssetPackDeliveryMode deliveryMode) { if (deviceTierToAssetPackDirectoryPath.Count == 0) { throw new ArgumentException("Dictionary should contain at least one path"); } CheckAssetPackName(assetPackName); AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, DeviceTierToAssetPackDirectoryPath = new Dictionary(deviceTierToAssetPackDirectoryPath) }; } /// /// Package the specified AssetBundle files, which vary only by , in an /// with the specified delivery mode. /// When using Play Asset Delivery APIs, only the AssetBundle for the device's tier will be delivered. /// public void AddAssetBundles( IDictionary deviceTierToAssetBundleFilePath, AssetPackDeliveryMode deliveryMode) { if (deviceTierToAssetBundleFilePath.Count == 0) { throw new ArgumentException("Dictionary should contain at least one AssetBundle"); ; } var assetPackName = GetAssetPackName(deviceTierToAssetBundleFilePath.Values.First()); if (deviceTierToAssetBundleFilePath.Any(kvp => assetPackName != GetAssetPackName(kvp.Value))) { throw new ArgumentException("All AssetBundles in the Dictionary must have the same name"); } AssetPacks[assetPackName] = new AssetPack { DeliveryMode = deliveryMode, DeviceTierToAssetBundleFilePath = new Dictionary(deviceTierToAssetBundleFilePath) }; } private static string GetAssetPackName(string assetBundleFilePath) { var fileInfo = new FileInfo(assetBundleFilePath); if (!fileInfo.Exists) { throw new FileNotFoundException("AssetBundle file doesn't exist", assetBundleFilePath); } var assetPackName = fileInfo.Name; CheckAssetPackName(assetPackName); return assetPackName; } private static void CheckAssetPackName(string assetPackName) { if (!AndroidAppBundle.IsValidModuleName(assetPackName)) { throw new ArgumentException("Invalid asset pack name: " + assetPackName); } } } }