chunk 2: remaining non-audio non-NewImport assets
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal-only representation of a folder containing AssetBundles.
|
||||
/// </summary>
|
||||
public class AssetBundleFolder
|
||||
{
|
||||
/// <summary>
|
||||
/// The error state of this folder or <see cref="AssetPackFolderState.Ok"/>.
|
||||
/// </summary>
|
||||
public AssetPackFolderState State { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path represented by the folder.
|
||||
/// </summary>
|
||||
public string FolderPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The texture compression format of AssetBundles inside this folder, based on the folder name.
|
||||
/// </summary>
|
||||
public TextureCompressionFormat TextureCompressionFormat { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of AssetBundles found in this folder.
|
||||
/// Note: this value is cached during calls to <see cref="ExtractAssetBundleVariant"/>.
|
||||
/// </summary>
|
||||
public int AssetBundleCount { get; private set; }
|
||||
|
||||
private string _searchedManifestFileName;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new AssetBundleFolder for a given path (which is then immutable).
|
||||
/// Call <see cref="Refresh"/> to update the folder state and texture compression format targeting.
|
||||
/// </summary>
|
||||
public AssetBundleFolder(string folderPath)
|
||||
{
|
||||
AssetBundleCount = 0;
|
||||
FolderPath = folderPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the folder state and texture compression format targeting, if any.
|
||||
/// </summary>
|
||||
/// <returns>true if the refresh was properly done (no errors)</returns>
|
||||
public bool Refresh()
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(FolderPath);
|
||||
if (!directoryInfo.Exists)
|
||||
{
|
||||
AssetBundleCount = 0;
|
||||
State = AssetPackFolderState.FolderMissing;
|
||||
return false;
|
||||
}
|
||||
|
||||
var files = directoryInfo.GetFiles();
|
||||
if (files.Length == 0)
|
||||
{
|
||||
AssetBundleCount = 0;
|
||||
State = AssetPackFolderState.FolderEmpty;
|
||||
return false;
|
||||
}
|
||||
|
||||
TextureCompressionFormat textureCompressionFormat;
|
||||
TextureTargetingTools.GetTextureCompressionFormatAndStripSuffix(
|
||||
directoryInfo.Name,
|
||||
out textureCompressionFormat,
|
||||
out _searchedManifestFileName);
|
||||
TextureCompressionFormat = textureCompressionFormat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans and returns every <see cref="AssetBundleVariant"/> found in this folder.
|
||||
/// </summary>
|
||||
public IDictionary<string, AssetBundleVariant> ExtractAssetBundleVariant()
|
||||
{
|
||||
AssetBundleCount = 0;
|
||||
var assetBundleVariants = new Dictionary<string, AssetBundleVariant>();
|
||||
if (!Refresh())
|
||||
{
|
||||
return assetBundleVariants;
|
||||
}
|
||||
|
||||
var directoryInfo = new DirectoryInfo(FolderPath);
|
||||
var files = directoryInfo.GetFiles();
|
||||
|
||||
var fileDictionary = files.ToDictionary(file => file.Name, file => file);
|
||||
|
||||
// Look for an AssetBundle file with the same name as the directory that contains it.
|
||||
// This AssetBundle file contains a single asset, which is of type AssetBundleManifest.
|
||||
// See https://unity3d.com/learn/tutorials/topics/best-practices/assetbundle-fundamentals
|
||||
FileInfo manifestFileInfo;
|
||||
if (!fileDictionary.TryGetValue(_searchedManifestFileName, out manifestFileInfo))
|
||||
{
|
||||
State = AssetPackFolderState.ManifestFileMissing;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
|
||||
var manifestFilePath = manifestFileInfo.FullName;
|
||||
AssetBundle manifestAssetBundle;
|
||||
try
|
||||
{
|
||||
manifestAssetBundle = AssetBundle.LoadFromFile(manifestFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogErrorFormat("Exception loading AssetBundle file containing manifest ({0}): {1}",
|
||||
manifestFilePath, ex);
|
||||
State = AssetPackFolderState.ManifestFileLoadError;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var manifest = manifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
|
||||
if (manifest == null)
|
||||
{
|
||||
State = AssetPackFolderState.ManifestAssetMissing;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
|
||||
var allAssetBundles = manifest.GetAllAssetBundles();
|
||||
if (allAssetBundles.Length == 0)
|
||||
{
|
||||
State = AssetPackFolderState.AssetBundlesMissing;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
|
||||
foreach (var assetBundleName in allAssetBundles)
|
||||
{
|
||||
FileInfo assetBundleFileInfo;
|
||||
var fileSizeBytes = fileDictionary.TryGetValue(assetBundleName, out assetBundleFileInfo)
|
||||
? assetBundleFileInfo.Length
|
||||
: AssetBundleVariant.FileSizeIfMissing;
|
||||
|
||||
assetBundleVariants.Add(
|
||||
assetBundleName,
|
||||
AssetBundleVariant.CreateVariant(
|
||||
assetBundleName,
|
||||
fileSizeBytes,
|
||||
manifest.GetDirectDependencies(assetBundleName),
|
||||
manifest.GetAllDependencies(assetBundleName),
|
||||
Path.Combine(FolderPath, assetBundleName)));
|
||||
}
|
||||
|
||||
AssetBundleCount = assetBundleVariants.Count;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogErrorFormat(
|
||||
"Exception loading AssetBundleManifest from AssetBundle ({0}): {1}", manifestFilePath, ex);
|
||||
State = AssetPackFolderState.ManifestAssetLoadError;
|
||||
return assetBundleVariants;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// If an AssetBundle isn't unloaded, the Editor will have to be restarted to load it again.
|
||||
manifestAssetBundle.Unload(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c3bd268718fc41b48090fadb8eee0bd
|
||||
timeCreated: 1548906103
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,170 @@
|
||||
// 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.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal-only wrapper around <see cref="AssetBundleVariant"/>s that are identical except for their
|
||||
/// texture compression formats.
|
||||
/// </summary>
|
||||
public class AssetBundlePack
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the asset pack, which is the same as the name of every included AssetBundle.
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this pack will be included in an Android App Bundle, and if so how it is delivered.
|
||||
/// </summary>
|
||||
public AssetPackDeliveryMode DeliveryMode;
|
||||
|
||||
/// <summary>
|
||||
/// The AssetBundles, all with the same name but generated for different texture compression formats.
|
||||
/// </summary>
|
||||
public IDictionary<TextureCompressionFormat, AssetBundleVariant> Variants { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If non-empty, these errors will prevent this AssetBundlePack from being packaged in an App Bundle.
|
||||
/// </summary>
|
||||
public readonly HashSet<AssetPackError> Errors = new HashSet<AssetPackError>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public AssetBundlePack(string name, AssetPackDeliveryMode deliveryMode)
|
||||
{
|
||||
Name = name;
|
||||
DeliveryMode = deliveryMode;
|
||||
Variants = new Dictionary<TextureCompressionFormat, AssetBundleVariant>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="AssetBundleVariant"/> to this asset pack.
|
||||
/// <returns>true if the AssetBundle was added, false if another AssetBundle with the same compression
|
||||
/// is already present.</returns>
|
||||
/// </summary>
|
||||
public bool Add(TextureCompressionFormat textureCompressionFormat, AssetBundleVariant variant)
|
||||
{
|
||||
AssetBundleVariant existingVariant;
|
||||
if (Variants.TryGetValue(textureCompressionFormat, out existingVariant))
|
||||
{
|
||||
Debug.LogErrorFormat("Multiple AssetBundles for texture format {0} are used for {1}.",
|
||||
textureCompressionFormat.ToString(), Name);
|
||||
existingVariant.Errors.Add(AssetPackError.DuplicateName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Variants.Add(textureCompressionFormat, variant);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified error to all children.
|
||||
/// </summary>
|
||||
public void AddErrorToVariants(AssetPackError error)
|
||||
{
|
||||
foreach (var assetPack in Variants.Values)
|
||||
{
|
||||
assetPack.Errors.Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The names of any AssetBundles that the AssetBundles inside this AssetBundlePack directly depend on.
|
||||
/// </summary>
|
||||
public IList<string> DirectDependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
var directDependencies =
|
||||
Variants.Values.SelectMany(pack => pack.DirectDependencies).Distinct().ToList();
|
||||
directDependencies.Sort();
|
||||
return directDependencies;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A UI friendly way of displaying the error with this AssetBundlePack (if one) or the number of errors
|
||||
/// (if more than one). If there are no errors, returns null.
|
||||
/// </summary>
|
||||
public string ErrorSummary
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Errors.Count)
|
||||
{
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return NameAndDescriptionAttribute.GetAttribute(Errors.First()).Name;
|
||||
default:
|
||||
return Errors.Count + " Errors";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to find an AssetBundlePack when doing the dependencies check.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the other AssetBundlePack.</param>
|
||||
/// <param name="assetBundlePack">If found, will contain the AssetBundlePack.</param>
|
||||
public delegate bool TryGetAssetBundlePack(string name, out AssetBundlePack assetBundlePack);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates whether to set any <see cref="Errors"/> based on problems with the dependencies.
|
||||
/// </summary>
|
||||
/// <param name="tryGetAssetBundlePack">Function to get an <see cref="AssetBundlePack"/> given its name.</param>
|
||||
public void CheckDependencyErrors(TryGetAssetBundlePack tryGetAssetBundlePack)
|
||||
{
|
||||
if (DeliveryMode == AssetPackDeliveryMode.DoNotPackage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var variant in Variants)
|
||||
{
|
||||
var textureCompressionFormat = variant.Key;
|
||||
var assetPack = variant.Value;
|
||||
assetPack.CheckDependencyErrors(
|
||||
DeliveryMode,
|
||||
TryGetDependency(tryGetAssetBundlePack, textureCompressionFormat));
|
||||
}
|
||||
}
|
||||
|
||||
private static AssetBundleVariant.TryGetDependency TryGetDependency(
|
||||
TryGetAssetBundlePack tryGetAssetBundlePack, TextureCompressionFormat textureCompressionFormat)
|
||||
{
|
||||
return (string name, out AssetBundleVariant dependency, out AssetPackDeliveryMode deliveryMode) =>
|
||||
{
|
||||
deliveryMode = AssetPackDeliveryMode.DoNotPackage;
|
||||
dependency = null;
|
||||
AssetBundlePack assetBundlePack;
|
||||
if (tryGetAssetBundlePack(name, out assetBundlePack) &&
|
||||
assetBundlePack.Variants.TryGetValue(textureCompressionFormat, out dependency))
|
||||
{
|
||||
deliveryMode = assetBundlePack.DeliveryMode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20e43cae4a4245538a68b920730857b6
|
||||
timeCreated: 1571324952
|
||||
@@ -0,0 +1,206 @@
|
||||
// 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.Utils;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal-only wrapper around an AssetBundle that by virtue of its texture compression format is a
|
||||
/// variant of the other AssetBundles contained in an <see cref="AssetBundlePack"/>.
|
||||
/// </summary>
|
||||
public class AssetBundleVariant
|
||||
{
|
||||
/// <summary>
|
||||
/// Special value for <see cref="FileSizeBytes"/> indicating the AssetBundle file does not exist.
|
||||
/// </summary>
|
||||
public const long FileSizeIfMissing = -1L;
|
||||
|
||||
// Visible for testing.
|
||||
public const string FileMissingText = "file missing";
|
||||
|
||||
// Visible for testing.
|
||||
public const string NoneText = "none";
|
||||
|
||||
private static readonly string[] EmptyStringArray = new string[0];
|
||||
|
||||
/// <summary>
|
||||
/// Constructor. Prefer <see cref="CreateVariant"/> for all cases except deserialization and testing.
|
||||
/// </summary>
|
||||
public AssetBundleVariant()
|
||||
{
|
||||
DirectDependencies = EmptyStringArray;
|
||||
AllDependencies = EmptyStringArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If non-empty, these errors will prevent this AssetBundle from being packaged in an app bundle.
|
||||
/// </summary>
|
||||
public readonly HashSet<AssetPackError> Errors = new HashSet<AssetPackError>();
|
||||
|
||||
/// <summary>
|
||||
/// The names of any AssetBundles that this one directly depends on.
|
||||
/// </summary>
|
||||
public string[] DirectDependencies { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The names of any AssetBundles that this one directly or transitively depends on.
|
||||
/// </summary>
|
||||
public string[] AllDependencies { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The size of the file in bytes, or -1L if the file doesn't exist.
|
||||
/// </summary>
|
||||
public long FileSizeBytes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the AssetBundle represented by this wrapper.
|
||||
/// </summary>
|
||||
public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A UI friendly way of displaying dependency info.
|
||||
/// </summary>
|
||||
public string DependenciesText
|
||||
{
|
||||
// TODO: consider how to handle an extra long string in the UI.
|
||||
get { return DirectDependencies.Length == 0 ? NoneText : string.Join(", ", DirectDependencies); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A UI friendly way of displaying file size or that the file doesn't exist on disk.
|
||||
/// </summary>
|
||||
public string FileSizeText
|
||||
{
|
||||
get
|
||||
{
|
||||
// Display file size in Kibibytes using "JEDEC" standard. See https://en.wikipedia.org/wiki/Kilobyte
|
||||
return FileSizeBytes == FileSizeIfMissing ? FileMissingText : (FileSizeBytes / 1024) + " KB";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A UI friendly way of displaying the error with this AssetBundle (if there is one) or the number of errors
|
||||
/// (if there is more than one). If there are no errors, this is null.
|
||||
/// </summary>
|
||||
public string ErrorSummary
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Errors.Count)
|
||||
{
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return NameAndDescriptionAttribute.GetAttribute(Errors.First()).Name;
|
||||
default:
|
||||
return Errors.Count + " Errors";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to find a variant when doing the dependencies check.
|
||||
/// See <see cref="AssetBundleVariant.CheckDependencyErrors"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The asset pack to search.</param>
|
||||
/// <param name="dependencyVariant">If found, will contain the searched asset pack.</param>
|
||||
/// <param name="dependencyDeliveryMode">If found, will contain the delivery mode of the searched asset pack.</param>
|
||||
/// <returns>true if the asset pack was found, false otherwise.</returns>
|
||||
public delegate bool TryGetDependency(string name, out AssetBundleVariant dependencyVariant,
|
||||
out AssetPackDeliveryMode dependencyDeliveryMode);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates whether to set any <see cref="Errors"/> based on problems with this variant's dependencies.
|
||||
/// </summary>
|
||||
/// <param name="deliveryMode">The delivery mode of this asset pack.</param>
|
||||
/// <param name="tryGetDependency">A function returning the asset pack for the given dependency name,
|
||||
/// along with the delivery mode of the asset pack.</param>
|
||||
public void CheckDependencyErrors(AssetPackDeliveryMode deliveryMode, TryGetDependency tryGetDependency)
|
||||
{
|
||||
if (deliveryMode == AssetPackDeliveryMode.DoNotPackage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var dependencyName in AllDependencies)
|
||||
{
|
||||
AssetBundleVariant dependencyVariant;
|
||||
AssetPackDeliveryMode dependencyDeliveryMode;
|
||||
if (!tryGetDependency(dependencyName, out dependencyVariant, out dependencyDeliveryMode))
|
||||
{
|
||||
Errors.Add(AssetPackError.DependencyMissing);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dependencyVariant.Errors.Count > 0)
|
||||
{
|
||||
Errors.Add(AssetPackError.DependencyError);
|
||||
}
|
||||
|
||||
// If this asset pack is marked for delivery, all of its dependencies must be packaged for delivery.
|
||||
if (dependencyDeliveryMode == AssetPackDeliveryMode.DoNotPackage)
|
||||
{
|
||||
Errors.Add(AssetPackError.DependencyNotPackaged);
|
||||
continue;
|
||||
}
|
||||
|
||||
// An asset pack cannot have dependencies on asset packs with later delivery modes,
|
||||
// e.g. A FastFollow asset pack cannot depend on an OnDemand asset pack.
|
||||
if (deliveryMode < dependencyDeliveryMode)
|
||||
{
|
||||
Errors.Add(AssetPackError.DependencyIncompatibleDelivery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="AssetBundleVariant"/>. Evaluates some conditions and may set <see cref="Errors"/>.
|
||||
/// </summary>
|
||||
/// <param name="assetBundleName">Name of the AssetBundle represented by this variant.</param>
|
||||
/// <param name="fileSizeBytes">Size of the file in bytes, or <see cref="FileSizeIfMissing"/>.</param>
|
||||
/// <param name="directDependencies">AssetBundles that are direct dependencies of this one.</param>
|
||||
/// <param name="allDependencies">AssetBundles that are direct or transitive dependencies of this one.</param>
|
||||
/// <param name="path">The path to the AssetBundle represented by this variant.</param>
|
||||
public static AssetBundleVariant CreateVariant(
|
||||
string assetBundleName,
|
||||
long fileSizeBytes,
|
||||
string[] directDependencies,
|
||||
string[] allDependencies,
|
||||
string path)
|
||||
{
|
||||
var variant = new AssetBundleVariant
|
||||
{
|
||||
DirectDependencies = directDependencies ?? EmptyStringArray,
|
||||
AllDependencies = allDependencies ?? EmptyStringArray,
|
||||
FileSizeBytes = fileSizeBytes,
|
||||
Path = path,
|
||||
};
|
||||
|
||||
if (fileSizeBytes == FileSizeIfMissing)
|
||||
{
|
||||
variant.Errors.Add(AssetPackError.FileMissing);
|
||||
}
|
||||
|
||||
if (!AndroidAppBundle.IsValidModuleName(assetBundleName))
|
||||
{
|
||||
variant.Errors.Add(AssetPackError.InvalidName);
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c348752fa8c94d3a8f6f085846aa228
|
||||
timeCreated: 1548906103
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,251 @@
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal-only representation of asset packs being considered for packaging in an Android App Bundle.
|
||||
/// </summary>
|
||||
public class AssetDeliveryConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Default texture compression format for building standalone APKs for Android pre-Lollipop devices.
|
||||
/// </summary>
|
||||
public TextureCompressionFormat DefaultTextureCompressionFormat = TextureCompressionFormat.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to split the assets in an AAB's base module into a separate install-time asset pack.
|
||||
/// </summary>
|
||||
public bool SplitBaseModuleAssets;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary from a folder path to a AssetBundleFolder object describing the folder's contents.
|
||||
/// </summary>
|
||||
public readonly IDictionary<string, AssetBundleFolder> Folders =
|
||||
new SortedDictionary<string, AssetBundleFolder>();
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary from AssetBundle name to AssetBundlePacks.
|
||||
/// </summary>
|
||||
public IDictionary<string, AssetBundlePack> AssetBundlePacks { get; private set; }
|
||||
|
||||
// TODO(b/150701341): add support for managing this type of asset pack in the UI.
|
||||
public List<SerializableAssetPack> rawAssetsPacks;
|
||||
|
||||
private readonly IEnumerable<IAssetPackValidator> _packValidators;
|
||||
|
||||
public AssetDeliveryConfig()
|
||||
{
|
||||
AssetBundlePacks = new Dictionary<string, AssetBundlePack>();
|
||||
_packValidators = AssetPackValidatorRegistry.Registry.ConstructInstances();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the in-memory state based on the AssetBundles on disk.
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Potentially refresh all folders and/or add and remove the specified folders.
|
||||
/// </summary>
|
||||
/// <returns>true if changes were made.</returns>
|
||||
public bool UpdateAndRefreshFolders(bool refreshFolders, List<string> foldersToAdd,
|
||||
List<string> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public List<string> GetPackagingErrorMessages(out int numAssetPacksToDeliver)
|
||||
{
|
||||
// Check for errors in asset packs
|
||||
numAssetPacksToDeliver = 0;
|
||||
var errors = new List<string>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the list of all the <see cref="AssetBundlePacks"/> represented by the folders.
|
||||
/// </summary>
|
||||
public void RefreshAssetBundlePacks()
|
||||
{
|
||||
var assetBundlePacks = new Dictionary<string, AssetBundlePack>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the delivery mode for the AssetBundlePack with the given name, or DoNotPackage if not found.
|
||||
/// </summary>
|
||||
private AssetPackDeliveryMode GetMultiTargetingAssetPackDeliveryMode(string multiTargetingAssetPackName)
|
||||
{
|
||||
AssetBundlePack assetBundlePack;
|
||||
if (AssetBundlePacks.TryGetValue(multiTargetingAssetPackName, out assetBundlePack))
|
||||
{
|
||||
return assetBundlePack.DeliveryMode;
|
||||
}
|
||||
|
||||
return AssetPackDeliveryMode.DoNotPackage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all texture compression formats used by asset packs.
|
||||
/// </summary>
|
||||
public IEnumerable<TextureCompressionFormat> GetAllTextureCompressionFormats()
|
||||
{
|
||||
return AssetBundlePacks.Values.SelectMany(
|
||||
multiTargetingAssetPack => multiTargetingAssetPack.Variants.Keys).Distinct();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if some asset packs are using targeted texture compression format.
|
||||
/// </summary>
|
||||
public bool HasTextureCompressionFormatTargeting()
|
||||
{
|
||||
return GetAllTextureCompressionFormats().Any(textureCompressionFormat =>
|
||||
textureCompressionFormat != TextureCompressionFormat.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the specified bundlePack for build errors using validators registered in AssetPackValidatorRegistry.
|
||||
/// </summary>
|
||||
private void CheckBuildTypeErrors(AssetBundlePack bundlePack)
|
||||
{
|
||||
var buildErrors = new HashSet<AssetPackError>();
|
||||
foreach (var buildValidator in _packValidators)
|
||||
{
|
||||
buildErrors.UnionWith(buildValidator.CheckBuildErrors(bundlePack));
|
||||
}
|
||||
|
||||
foreach (var error in buildErrors)
|
||||
{
|
||||
bundlePack.AddErrorToVariants(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdc061489f3c64fd78ed0cefe895b007
|
||||
timeCreated: 1549914493
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using Google.Android.AppBundle.Editor.Internal.Config;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for serializing <see cref="AssetDeliveryConfig"/> to a JSON file and for deserializing it.
|
||||
/// </summary>
|
||||
public static class AssetDeliveryConfigSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Save the specified AssetDeliveryConfig config to disk.
|
||||
/// </summary>
|
||||
public static void SaveConfig(AssetDeliveryConfig assetDeliveryConfig)
|
||||
{
|
||||
Debug.LogFormat("Saving {0}", SerializationHelper.ConfigurationFilePath);
|
||||
var config = Serialize(assetDeliveryConfig);
|
||||
var jsonText = JsonUtility.ToJson(config);
|
||||
File.WriteAllText(SerializationHelper.ConfigurationFilePath, jsonText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an AssetDeliveryConfig loaded from disk.
|
||||
/// </summary>
|
||||
public static AssetDeliveryConfig LoadConfig()
|
||||
{
|
||||
Debug.LogFormat("Loading {0}", SerializationHelper.ConfigurationFilePath);
|
||||
SerializableAssetPackConfig config;
|
||||
if (File.Exists(SerializationHelper.ConfigurationFilePath))
|
||||
{
|
||||
var jsonText = File.ReadAllText(SerializationHelper.ConfigurationFilePath);
|
||||
config = JsonUtility.FromJson<SerializableAssetPackConfig>(jsonText);
|
||||
}
|
||||
else
|
||||
{
|
||||
config = new SerializableAssetPackConfig();
|
||||
}
|
||||
|
||||
return Deserialize(config);
|
||||
}
|
||||
|
||||
private static SerializableAssetPackConfig Serialize(AssetDeliveryConfig assetDeliveryConfig)
|
||||
{
|
||||
var config = new SerializableAssetPackConfig
|
||||
{
|
||||
DefaultTextureCompressionFormat = assetDeliveryConfig.DefaultTextureCompressionFormat,
|
||||
splitBaseModuleAssets = assetDeliveryConfig.SplitBaseModuleAssets
|
||||
};
|
||||
|
||||
foreach (var assetPack in assetDeliveryConfig.AssetBundlePacks.Values)
|
||||
{
|
||||
config.assetBundles.Add(new SerializableMultiTargetingAssetBundle
|
||||
{
|
||||
name = assetPack.Name,
|
||||
DeliveryMode = assetPack.DeliveryMode,
|
||||
assetBundles = assetPack.Variants.Select(pack => new SerializableAssetBundle
|
||||
{
|
||||
path = pack.Value.Path,
|
||||
TextureCompressionFormat = pack.Key
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
config.assetPacks = assetDeliveryConfig.rawAssetsPacks;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static AssetDeliveryConfig Deserialize(SerializableAssetPackConfig config)
|
||||
{
|
||||
var assetDeliveryConfig = new AssetDeliveryConfig
|
||||
{
|
||||
DefaultTextureCompressionFormat = config.DefaultTextureCompressionFormat,
|
||||
SplitBaseModuleAssets = config.splitBaseModuleAssets
|
||||
};
|
||||
|
||||
var paths = new HashSet<string>();
|
||||
foreach (var multiTargetingAssetBundle in config.assetBundles)
|
||||
{
|
||||
paths.UnionWith(
|
||||
multiTargetingAssetBundle.assetBundles.Select(item => Path.GetDirectoryName(item.path)));
|
||||
assetDeliveryConfig.AssetBundlePacks.Add(multiTargetingAssetBundle.name,
|
||||
new AssetBundlePack(multiTargetingAssetBundle.name,
|
||||
multiTargetingAssetBundle.DeliveryMode));
|
||||
}
|
||||
|
||||
paths.ToList().ForEach(path => assetDeliveryConfig.Folders.Add(path, new AssetBundleFolder(path)));
|
||||
|
||||
assetDeliveryConfig.rawAssetsPacks = config.assetPacks;
|
||||
|
||||
return assetDeliveryConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f127ef3c767384ec89ad3e3aef7ae1bb
|
||||
timeCreated: 1548906103
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,399 @@
|
||||
// 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.Utils;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
public class AssetDeliveryWindow : EditorWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Label for the split base APK checkbox. Visible for referencing from error messages.
|
||||
/// </summary>
|
||||
public const string SeparateAssetsLabel = "Separate Base APK Assets";
|
||||
|
||||
private const string RefreshButtonText = "Refresh";
|
||||
private const int WindowMinWidth = 610;
|
||||
private const int WindowMinHeight = 300;
|
||||
private const int ButtonWidth = 150;
|
||||
private const int FieldWidth = 100;
|
||||
|
||||
private AssetDeliveryConfig _assetDeliveryConfig;
|
||||
private Vector2 _scrollPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The names of asset packs that are shown collapsed.
|
||||
/// </summary>
|
||||
private HashSet<string> _collapsedAssetPacks;
|
||||
|
||||
/// <summary>
|
||||
/// Displays this window, creating it if necessary.
|
||||
/// </summary>
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow(typeof(AssetDeliveryWindow), false, "Asset Delivery");
|
||||
window.minSize = new Vector2(WindowMinWidth, WindowMinHeight);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (_assetDeliveryConfig == null)
|
||||
{
|
||||
_assetDeliveryConfig = AssetDeliveryConfigSerializer.LoadConfig();
|
||||
_assetDeliveryConfig.Refresh();
|
||||
}
|
||||
|
||||
if (_collapsedAssetPacks == null)
|
||||
{
|
||||
_collapsedAssetPacks = new HashSet<string>();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Base APK Asset Delivery", EditorStyles.boldLabel);
|
||||
RenderDescription(
|
||||
"When building an Android App Bundle (AAB), automatically move assets that would normally be " +
|
||||
"delivered in the base APK into an install-time asset pack. This option reduces base APK size " +
|
||||
"similarly to the \"Split Application Binary\" publishing setting, but it uses Play Asset Delivery " +
|
||||
"instead of APK Expansion (OBB) files, so it's compatible with AABs. This option is recommended for " +
|
||||
"apps with a large base APK, e.g. over 150 MB.");
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(SeparateAssetsLabel, GUILayout.Width(145));
|
||||
var pendingSplitBaseModuleAssets = EditorGUILayout.Toggle(_assetDeliveryConfig.SplitBaseModuleAssets);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField("Asset Pack Configuration", EditorStyles.boldLabel);
|
||||
RenderDescription(
|
||||
"Add folders that directly contain AssetBundle files, for example the output folder from Unity's " +
|
||||
"AssetBundle Browser. All AssetBundles directly contained in the specified folders will be displayed " +
|
||||
"below. Update the \"Delivery Mode\" to include an AssetBundle in AAB builds.");
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// TODO(b/144677274): Remove this #if once TCF support is available in Play, to make TCF targeting discoverable.
|
||||
#if SHOW_TCF_IN_UI
|
||||
if (_assetDeliveryConfig.Folders.Count != 0 && !_assetDeliveryConfig.HasTextureCompressionFormatTargeting())
|
||||
{
|
||||
// TODO(b/144677274): Add link to public documentation when available.
|
||||
EditorGUILayout.HelpBox(
|
||||
"These AssetBundles don't specify texture compression format targeting. Generate additional " +
|
||||
"AssetBundles into folders ending with #tcf_xxx (e.g. AssetBundles#tcf_astc), and the " +
|
||||
"AssetBundles will be delivered to devices supporting that format.", MessageType.Info);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
#endif
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// Store the changes and defer the actual update at the end to avoid modifications during rendering.
|
||||
var refreshAssetDeliveryConfig = false;
|
||||
var foldersToRemove = new List<string>();
|
||||
var foldersToAdd = new List<string>();
|
||||
|
||||
if (GUILayout.Button("Add Folder...", GUILayout.Width(ButtonWidth)))
|
||||
{
|
||||
var folderPath = EditorUtility.OpenFolderPanel("Add Folder Containing AssetBundles", null, null);
|
||||
if (string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
// Assume cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_assetDeliveryConfig.Folders.ContainsKey(folderPath))
|
||||
{
|
||||
var errorMessage =
|
||||
string.Format(
|
||||
"Cannot add a folder that has already been added. Use \"{0}\" to analyze existing folders.",
|
||||
RefreshButtonText);
|
||||
EditorUtility.DisplayDialog("Add Folder Error", errorMessage, WindowUtils.OkButtonText);
|
||||
}
|
||||
else
|
||||
{
|
||||
foldersToAdd.Add(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button(RefreshButtonText, GUILayout.Width(ButtonWidth)))
|
||||
{
|
||||
refreshAssetDeliveryConfig = true;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("App Bundle Summary...", GUILayout.Width(ButtonWidth)))
|
||||
{
|
||||
_assetDeliveryConfig.Refresh();
|
||||
EditorUtility.DisplayDialog("App Bundle Summary", GetPackagingSummary(), WindowUtils.OkButtonText);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Hide this setting for projects targeting SDK 21+ since it only affects install-time asset pack
|
||||
// installation on older devices.
|
||||
if (_assetDeliveryConfig.HasTextureCompressionFormatTargeting() &&
|
||||
TextureTargetingTools.IsSdkVersionPreLollipop(PlayerSettings.Android.minSdkVersion))
|
||||
{
|
||||
EditorGUILayout.LabelField("Texture Compression Configuration", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
bool targetingUpdated;
|
||||
RenderTextureCompressionFormatTargetingConfiguration(_assetDeliveryConfig, out targetingUpdated);
|
||||
refreshAssetDeliveryConfig |= targetingUpdated;
|
||||
}
|
||||
|
||||
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||
|
||||
EditorGUILayout.LabelField("AssetBundle Folders", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
foreach (var folderPair in _assetDeliveryConfig.Folders)
|
||||
{
|
||||
var folderPath = folderPair.Key;
|
||||
bool removeFolder;
|
||||
RenderFolder(folderPath, folderPair.Value, out removeFolder);
|
||||
if (removeFolder)
|
||||
{
|
||||
foldersToRemove.Add(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (_assetDeliveryConfig.Folders.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Add folders that directly contain AssetBundles.", MessageType.None);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("AssetBundle Configuration", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
foreach (var assetBundlePack in _assetDeliveryConfig.AssetBundlePacks.Values)
|
||||
{
|
||||
bool refreshFolder;
|
||||
RenderAssetBundlePack(assetBundlePack, out refreshFolder);
|
||||
refreshAssetDeliveryConfig |= refreshFolder;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
// Defer folder refresh, addition and removal to avoid modification of the collection while iterating.
|
||||
var pendingConfigChanges =
|
||||
_assetDeliveryConfig.UpdateAndRefreshFolders(refreshAssetDeliveryConfig, foldersToAdd, foldersToRemove);
|
||||
pendingConfigChanges |= pendingSplitBaseModuleAssets != _assetDeliveryConfig.SplitBaseModuleAssets;
|
||||
if (pendingConfigChanges)
|
||||
{
|
||||
_assetDeliveryConfig.SplitBaseModuleAssets = pendingSplitBaseModuleAssets;
|
||||
AssetDeliveryConfigSerializer.SaveConfig(_assetDeliveryConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderAssetBundlePack(AssetBundlePack assetBundlePack, out bool refreshFolder)
|
||||
{
|
||||
refreshFolder = false;
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
var showGroupExpanded = !_collapsedAssetPacks.Contains(assetBundlePack.Name);
|
||||
var groupExpanded =
|
||||
EditorGUILayout.BeginFoldoutHeaderGroup(showGroupExpanded, assetBundlePack.Name);
|
||||
if (groupExpanded)
|
||||
{
|
||||
_collapsedAssetPacks.Remove(assetBundlePack.Name);
|
||||
RenderAssetBundlePackContent(assetBundlePack, out refreshFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
_collapsedAssetPacks.Add(assetBundlePack.Name);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
#else
|
||||
// Old Unity versions don't support foldable headers. Use a simple label instead.
|
||||
EditorGUILayout.LabelField(assetBundlePack.Name, EditorStyles.boldLabel);
|
||||
RenderAssetBundlePackContent(assetBundlePack, out refreshFolder);
|
||||
EditorGUILayout.Separator();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void RenderAssetBundlePackContent(AssetBundlePack assetBundlePack, out bool refreshFolder)
|
||||
{
|
||||
refreshFolder = false;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newDeliveryMode =
|
||||
(AssetPackDeliveryMode) EditorGUILayout.EnumPopup("Delivery Mode", assetBundlePack.DeliveryMode,
|
||||
GUILayout.Width(300));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
assetBundlePack.DeliveryMode = newDeliveryMode;
|
||||
|
||||
// Don't use a short-circuit evaluation one-liner to ensure EndChangeCheck() is called above.
|
||||
refreshFolder = true;
|
||||
}
|
||||
|
||||
// Display the direct dependencies of all the contained asset packs.
|
||||
var directDependencies = assetBundlePack.DirectDependencies;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel("Dependencies");
|
||||
GUILayout.TextArea(
|
||||
directDependencies.Count > 0 ? string.Join(", ", directDependencies.ToArray()) : "None");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.LabelField("AssetBundles");
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (var variant in assetBundlePack.Variants)
|
||||
{
|
||||
RenderVariant(variant.Value, variant.Key);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private static void RenderVariant(AssetBundleVariant variant, TextureCompressionFormat targeting)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// TODO: revisit TextureCompressionFormat enum extension methods.
|
||||
var targetingDescription = targeting == TextureCompressionFormat.Default
|
||||
? targeting.ToString()
|
||||
: targeting.ToString().ToUpper();
|
||||
EditorGUILayout.PrefixLabel(targetingDescription);
|
||||
|
||||
EditorGUILayout.LabelField(variant.FileSizeText, GUILayout.ExpandWidth(false));
|
||||
|
||||
var errors = variant.Errors;
|
||||
if (errors.Count == 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("No errors", GUILayout.ExpandWidth(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button(variant.ErrorSummary + "...", GUILayout.ExpandWidth(true)))
|
||||
{
|
||||
var errorDialogTitle = "AssetBundle Error" + (errors.Count == 1 ? string.Empty : "s");
|
||||
var errorDialogMessage = string.Join("\n\n",
|
||||
errors.Select(e => NameAndDescriptionAttribute.GetAttribute(e).Description).ToArray());
|
||||
EditorUtility.DisplayDialog(errorDialogTitle, errorDialogMessage, WindowUtils.OkButtonText);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private static void RenderFolder(string folderPath, AssetBundleFolder assetBundleFolder, out bool removeFolder)
|
||||
{
|
||||
// Folder path and associated management buttons.
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(folderPath, EditorStyles.wordWrappedLabel);
|
||||
removeFolder = GUILayout.Button("Remove", GUILayout.Width(FieldWidth));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Display the folder state.
|
||||
if (assetBundleFolder.State == AssetPackFolderState.Ok)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
string.Format("Found {0} AssetBundle(s) in this folder", assetBundleFolder.AssetBundleCount),
|
||||
MessageType.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorMessage = NameAndDescriptionAttribute.GetAttribute(assetBundleFolder.State).Description;
|
||||
EditorGUILayout.HelpBox(errorMessage, MessageType.Warning);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private static void RenderTextureCompressionFormatTargetingConfiguration(
|
||||
AssetDeliveryConfig assetDeliveryConfig, out bool targetingUpdated)
|
||||
{
|
||||
targetingUpdated = false;
|
||||
const string label = "Format for pre-L devices";
|
||||
var width = GUILayout.Width(300);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
var validTextureCompressionFormats = assetDeliveryConfig.GetAllTextureCompressionFormats();
|
||||
var newDefaultTextureCompressionFormat =
|
||||
(TextureCompressionFormat) EditorGUILayout.EnumPopup(new GUIContent(label),
|
||||
assetDeliveryConfig.DefaultTextureCompressionFormat,
|
||||
textureCompressionFormat =>
|
||||
validTextureCompressionFormats.Contains((TextureCompressionFormat) textureCompressionFormat),
|
||||
false, width);
|
||||
#else
|
||||
var newDefaultTextureCompressionFormat =
|
||||
(TextureCompressionFormat) EditorGUILayout.EnumPopup(label,
|
||||
assetDeliveryConfig.DefaultTextureCompressionFormat, width);
|
||||
#endif
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
assetDeliveryConfig.DefaultTextureCompressionFormat = newDefaultTextureCompressionFormat;
|
||||
targetingUpdated = true;
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"This compression format will be used for devices running Android 4.4.4 (SDK 20) or older. " +
|
||||
"AssetBundles marked as install-time will be included in the APK generated for these devices. " +
|
||||
"Android 5.0 (SDK 21) and newer devices aren't affected by this setting.",
|
||||
MessageType.None);
|
||||
}
|
||||
|
||||
private static void RenderDescription(string label)
|
||||
{
|
||||
var descriptionTextStyle = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
fontStyle = FontStyle.Italic,
|
||||
wordWrap = true
|
||||
};
|
||||
|
||||
EditorGUILayout.BeginVertical("textfield"); // Adds a light grey background.
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField(label, descriptionTextStyle);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private string GetPackagingSummary()
|
||||
{
|
||||
int numAssetPacksToDeliver;
|
||||
var errorMessages = _assetDeliveryConfig.GetPackagingErrorMessages(out numAssetPacksToDeliver);
|
||||
|
||||
if (numAssetPacksToDeliver == 0)
|
||||
{
|
||||
return _assetDeliveryConfig.SplitBaseModuleAssets
|
||||
? "The Base APK's assets will be packaged in an asset pack."
|
||||
: "There are no AssetBundles marked for packaging.";
|
||||
}
|
||||
|
||||
if (errorMessages.Count > 0)
|
||||
{
|
||||
const string separator = "\n\n- ";
|
||||
return "The following error(s) will occur when building an Android App Bundle:" + separator +
|
||||
string.Join(separator, errorMessages.ToArray());
|
||||
}
|
||||
|
||||
var description = numAssetPacksToDeliver == 1
|
||||
? "There is 1 AssetBundle marked for packaging."
|
||||
: string.Format("There are {0} AssetBundles marked for packaging.", numAssetPacksToDeliver);
|
||||
return _assetDeliveryConfig.SplitBaseModuleAssets
|
||||
? description + " Also, the Base APK's assets will be packaged in an asset pack."
|
||||
: description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fe34ebc6742d4f818529bc14c64c974
|
||||
timeCreated: 1548906103
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 Google.Android.AppBundle.Editor.Internal.Utils;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes various possible error states of an AssetPack.
|
||||
/// </summary>
|
||||
public enum AssetPackError
|
||||
{
|
||||
[NameAndDescription("Duplicate Name",
|
||||
"AssetBundles with the same name and texture format exist in different folders. An " +
|
||||
"AssetBundle's name and parent folder texture format must be unique.")]
|
||||
DuplicateName,
|
||||
|
||||
[NameAndDescription("Invalid Name",
|
||||
"A Play-delivered AssetBundle's name must start with an English letter and can only contain letters, " +
|
||||
"numbers, and underscores. Please regenerate the AssetBundle with a new name.")]
|
||||
InvalidName,
|
||||
|
||||
[NameAndDescription("Missing File", "An AssetBundle file with this name is missing from the folder.")]
|
||||
FileMissing,
|
||||
|
||||
[NameAndDescription("Dependency Error", "One or more of this AssetBundle's dependencies has an error.")]
|
||||
DependencyError,
|
||||
|
||||
[NameAndDescription("Missing Dependency", "One or more of this AssetBundle's dependencies is missing.")]
|
||||
DependencyMissing,
|
||||
|
||||
[NameAndDescription("Dependency Not Packaged",
|
||||
"This AssetBundle is marked for delivery, but one or more of its dependencies is not.")]
|
||||
DependencyNotPackaged,
|
||||
|
||||
[NameAndDescription("Incompatible Dependency",
|
||||
"This AssetBundle is marked to be delivered earlier than one or more of its dependencies.")]
|
||||
DependencyIncompatibleDelivery,
|
||||
|
||||
[NameAndDescription("Instant Incompatible",
|
||||
"Install-time asset packs aren't supported for instant apps.")]
|
||||
InstallTimeAndInstant,
|
||||
|
||||
[NameAndDescription("Instant Incompatible",
|
||||
"Fast-follow asset packs aren't supported for instant apps.")]
|
||||
FastFollowAndInstant,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cad3b640bc35422c88ba5dd808e4170
|
||||
timeCreated: 1549483271
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 Google.Android.AppBundle.Editor.Internal.Utils;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes various possible states of a folder containing AssetBundle files.
|
||||
/// </summary>
|
||||
public enum AssetPackFolderState
|
||||
{
|
||||
// Does not need NameAndDescription attribute.
|
||||
Ok,
|
||||
|
||||
[NameAndDescription("Folder Missing", "This folder does not exist.")]
|
||||
FolderMissing,
|
||||
|
||||
[NameAndDescription("Folder Empty", "This folder contains no files.")]
|
||||
FolderEmpty,
|
||||
|
||||
[NameAndDescription("Manifest File Missing",
|
||||
"This folder is missing an AssetBundle file that matches the folder name.")]
|
||||
ManifestFileMissing,
|
||||
|
||||
[NameAndDescription("Manifest File Load Error",
|
||||
"There was an error loading the AssetBundle containing the AssetBundleManifest asset.")]
|
||||
ManifestFileLoadError,
|
||||
|
||||
[NameAndDescription("AssetBundleManifest Missing",
|
||||
"This folder's AssetBundle manifest file is missing the AssetBundleManifest asset.")]
|
||||
ManifestAssetMissing,
|
||||
|
||||
[NameAndDescription("AssetBundleManifest Load Error",
|
||||
"There was an error loading the AssetBundleManifest asset.")]
|
||||
ManifestAssetLoadError,
|
||||
|
||||
[NameAndDescription("AssetBundle Files Missing", "This folder contains no AssetBundle files.")]
|
||||
AssetBundlesMissing,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0110294bad4d544c486274699623a0c2
|
||||
timeCreated: 1548906103
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a <see cref="Registry"/> of <see cref="IAssetPackValidator"/>s.
|
||||
/// </summary>
|
||||
public class AssetPackValidatorRegistry : Registry<IAssetPackValidator>
|
||||
{
|
||||
private static AssetPackValidatorRegistry _instance;
|
||||
|
||||
public static AssetPackValidatorRegistry Registry
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new AssetPackValidatorRegistry();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ad448820c8d482996636e3862c1a420
|
||||
timeCreated: 1574899929
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for validating whether an <see cref="AssetBundlePack"/> can be included in an app bundle.
|
||||
/// </summary>
|
||||
public interface IAssetPackValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of errors that would prevent the specified asset pack from being included in an app bundle.
|
||||
/// </summary>
|
||||
IList<AssetPackError> CheckBuildErrors(AssetBundlePack assetBundlePack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56526b5a57974a7f8d23788f47d6a0db
|
||||
timeCreated: 1574729323
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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;
|
||||
using System.Text.RegularExpressions;
|
||||
using Google.Android.AppBundle.Editor.Internal.BuildTools;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Google.Android.AppBundle.Editor.Internal.AssetPacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Tools for adding texture compression format targeting to folders of an Android App Bundle.
|
||||
/// </summary>
|
||||
public static class TextureTargetingTools
|
||||
{
|
||||
/// <summary>
|
||||
/// The key used by bundletool to recognize a folder targeted for a specific texture format
|
||||
/// (for example, "tcf" in "textures#tcf_astc").
|
||||
/// </summary>
|
||||
private const string TextureCompressionFormatTargetingKey = "tcf";
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string used to designate a texture compression format in bundletool.
|
||||
/// </summary>
|
||||
public static string GetBundleToolTextureCompressionFormatName(TextureCompressionFormat format)
|
||||
{
|
||||
if (format == TextureCompressionFormat.Default)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var name = Enum.GetName(typeof(TextureCompressionFormat), format);
|
||||
return name == null ? format.ToString() : name.ToLower();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the targeting suffix for a given texture compression format, to
|
||||
/// be appended to a folder name containing AssetBundles.
|
||||
/// </summary>
|
||||
public static string GetTargetingSuffix(TextureCompressionFormat format)
|
||||
{
|
||||
if (format == TextureCompressionFormat.Default)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return string.Format("#{0}_{1}", TextureCompressionFormatTargetingKey,
|
||||
GetBundleToolTextureCompressionFormatName(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the folder name for a texture compression format, if any, and return it as
|
||||
/// well as the name of the folder without the targeting.
|
||||
/// </summary>
|
||||
public static void GetTextureCompressionFormatAndStripSuffix(string folderName,
|
||||
out TextureCompressionFormat targeting,
|
||||
out string tcfStrippedFolderName)
|
||||
{
|
||||
targeting = TextureCompressionFormat.Default;
|
||||
tcfStrippedFolderName = folderName;
|
||||
|
||||
// Parse the texture compression format used, if any, using the same convention
|
||||
// as bundletool for folder suffixes.
|
||||
Regex regex = new Regex(@"(?<base>.+?)#tcf_(?<value>.+)");
|
||||
GroupCollection matchedGroups = regex.Match(folderName).Groups;
|
||||
|
||||
if (!matchedGroups["base"].Success || !matchedGroups["value"].Success)
|
||||
{
|
||||
// No texture compression format targeting found.
|
||||
return;
|
||||
}
|
||||
|
||||
string tcfValue = matchedGroups["value"].Captures[0].Value;
|
||||
try
|
||||
{
|
||||
targeting = (TextureCompressionFormat) Enum.Parse(typeof(TextureCompressionFormat), tcfValue, true);
|
||||
tcfStrippedFolderName = matchedGroups["base"].Captures[0].Value;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
Debug.LogWarningFormat(
|
||||
"Ignoring unrecognized texture format \"{0}\" for folder: {1}",
|
||||
tcfValue, folderName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the specified SDK version is less than 21 and false if it's greater than or equal to 21.
|
||||
///
|
||||
/// This method can be used (along with the existence of install-time asset packs) to determine whether this
|
||||
/// app requires <see cref="BundletoolConfig.StandaloneConfig"/> to support pre-L devices.
|
||||
/// </summary>
|
||||
public static bool IsSdkVersionPreLollipop(AndroidSdkVersions sdkVersion)
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
// The minimum API level supported by Unity 2021.2+ is 22.
|
||||
return false;
|
||||
#else
|
||||
return sdkVersion < AndroidSdkVersions.AndroidApiLevel21;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d474e3691efbb4141a8e8f46aeb406ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user