/// <summary> /// Does the resolution of the play-services aars. /// </summary> /// <param name="svcSupport">Svc support.</param> /// <param name="destinationDirectory">Destination directory.</param> /// <param name="handleOverwriteConfirmation">Handle overwrite confirmation.</param> /// <param name="resolutionComplete">Delegate called when resolution is complete.</param> public override void DoResolution(PlayServicesSupport svcSupport, string destinationDirectory, PlayServicesSupport.OverwriteConfirmation handleOverwriteConfirmation, System.Action resolutionComplete) { string targetSdkVersion = UnityCompat.GetAndroidPlatform().ToString(); string minSdkVersion = UnityCompat.GetAndroidMinSDKVersion().ToString(); string buildToolsVersion = UnityCompat.GetAndroidBuildToolsVersion(); var config = new Dictionary <string, string>() { { "app_id", UnityCompat.ApplicationId }, { "sdk_version", targetSdkVersion }, { "min_sdk_version", minSdkVersion }, { "build_tools_version", buildToolsVersion }, { "android_sdk_dir", svcSupport.SDK } }; // This creates an enumerable of strings with the json lines for each dep like this: // "[\"namespace\", \"package\", \"version\"]" var dependencies = svcSupport.LoadDependencies(true, true, false); var depLines = from d in dependencies select "[" + ToJSONList(DepsVersionAsArray(d.Value)) + "]"; // Get a flattened list of dependencies, excluding any with the "$SDK" path variable, // since those will automatically be included in the gradle build. var repoLines = new HashSet <string>( dependencies.SelectMany(d => d.Value.Repositories) .Where(s => !s.Contains(PlayServicesSupport.SdkVariable))); var proguard_config_paths = new List <string>() { Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_UNITY_CONFIG), Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_MSG_FIX_CONFIG) }; // Build the full json config as a string. string json_config = @"{{ ""config"": {{ {0} }}, ""project_deps"": [ {1} ], ""extra_m2repositories"": [ {2} ], ""extra_proguard_configs"": [ {3} ] }}"; json_config = String.Format(json_config, ToJSONDictionary(config), ToJSONList(depLines, ",\n", 4, true), ToJSONList(repoLines, ",\n", 4), ToJSONList(proguard_config_paths, ",\n", 4)); System.IO.File.WriteAllText(GENERATE_CONFIG_PATH, json_config); RunGenGradleScript( " -c \"" + GENERATE_CONFIG_PATH + "\"" + " -b \"" + GENERATE_GRADLE_BUILD_PATH + "\"" + " -o \"" + Path.Combine(destinationDirectory, GENERATE_GRADLE_OUTPUT_DIR) + "\""); }
/// <summary> /// Perform the resolution and the exploding/cleanup as needed. /// </summary> public override void DoResolution( PlayServicesSupport svcSupport, string destinationDirectory, PlayServicesSupport.OverwriteConfirmation handleOverwriteConfirmation, System.Action resolutionComplete) { System.Action resolve = () => { DoResolutionNoAndroidPackageChecks(svcSupport, destinationDirectory, handleOverwriteConfirmation); resolutionComplete(); }; // Set of packages that need to be installed. Dictionary <string, bool> installPackages = new Dictionary <string, bool>(); // Retrieve the set of required packages and whether they're installed. Dictionary <string, Dictionary <string, bool> > requiredPackages = new Dictionary <string, Dictionary <string, bool> >(); foreach (Dependency dependency in svcSupport.LoadDependencies(true, keepMissing: true).Values) { if (dependency.PackageIds != null) { foreach (string packageId in dependency.PackageIds) { Dictionary <string, bool> dependencySet; if (!requiredPackages.TryGetValue(packageId, out dependencySet)) { dependencySet = new Dictionary <string, bool>(); } dependencySet[dependency.Key] = false; requiredPackages[packageId] = dependencySet; // If the dependency is missing, add it to the set that needs to be // installed. if (System.String.IsNullOrEmpty(dependency.BestVersionPath)) { installPackages[packageId] = false; } } } } // If no packages need to be installed or Android SDK package installation is disabled. if (installPackages.Count == 0 || !AndroidPackageInstallationEnabled()) { // Report missing packages as warnings and try to resolve anyway. foreach (string pkg in requiredPackages.Keys) { string depString = System.String.Join( ", ", CollectionToArray(requiredPackages[pkg].Keys)); if (installPackages.ContainsKey(pkg) && depString.Length > 0) { Debug.LogWarning(pkg + " not installed or out of date! This is " + "required by the following dependencies " + depString); } } // Attempt resolution. resolve(); return; } // Find the Android SDK manager. string sdkPath = svcSupport.SDK; string androidTool = FindAndroidSdkTool(svcSupport, "android"); if (androidTool == null || sdkPath == null || sdkPath == "") { Debug.LogError("Unable to find the Android SDK manager tool. " + "Required Android packages (" + System.String.Join(", ", CollectionToArray(installPackages.Keys)) + ") can not be installed. " + PlayServicesSupport.AndroidSdkConfigurationError); return; } // Get the set of available and installed packages. GetAvailablePackages( androidTool, svcSupport, (Dictionary <string, bool> packageInfo) => { if (packageInfo == null) { return; } // Filter the set of packages to install by what is available. foreach (string pkg in requiredPackages.Keys) { bool installed = false; string depString = System.String.Join( ", ", CollectionToArray(requiredPackages[pkg].Keys)); if (packageInfo.TryGetValue(pkg, out installed)) { if (!installed) { installPackages[pkg] = false; Debug.LogWarning(pkg + " not installed or out of date! " + "This is required by the following " + "dependencies " + depString); } } else { Debug.LogWarning(pkg + " referenced by " + depString + " not available in the Android SDK. This " + "package will not be installed."); installPackages.Remove(pkg); } } if (installPackages.Count == 0) { resolve(); return; } // Start installation. string installPackagesString = System.String.Join( ",", CollectionToArray(installPackages.Keys)); string packagesCommand = "update sdk -a -u -t " + installPackagesString; CommandLineDialog window = CommandLineDialog.CreateCommandLineDialog( "Install Android SDK packages"); window.summaryText = "Retrieving licenses..."; window.modal = false; window.progressTitle = window.summaryText; window.RunAsync( androidTool, packagesCommand, (CommandLine.Result getLicensesResult) => { // Get the start of the license text. int licenseTextStart = getLicensesResult.stdout.IndexOf("--------"); if (getLicensesResult.exitCode != 0 || licenseTextStart < 0) { window.Close(); Debug.LogError("Unable to retrieve licenses for packages " + installPackagesString); return; } // Remove the download output from the string. string licenseText = getLicensesResult.stdout.Substring( licenseTextStart); window.summaryText = ("License agreement(s) required to install " + "Android SDK packages"); window.bodyText = licenseText; window.yesText = "agree"; window.noText = "decline"; window.result = false; window.Repaint(); window.buttonClicked = (TextAreaDialog dialog) => { if (!dialog.result) { window.Close(); return; } window.summaryText = "Installing Android SDK packages..."; window.bodyText = ""; window.yesText = ""; window.noText = ""; window.buttonClicked = null; window.progressTitle = window.summaryText; window.autoScrollToBottom = true; window.Repaint(); // Kick off installation. ((CommandLineDialog)window).RunAsync( androidTool, packagesCommand, (CommandLine.Result updateResult) => { window.Close(); if (updateResult.exitCode == 0) { resolve(); } else { Debug.LogError("Android SDK update failed. " + updateResult.stderr + "(" + updateResult.exitCode.ToString() + ")"); } }, ioHandler: (new LicenseResponder(true)).AggregateLine, maxProgressLines: 500); }; }, ioHandler: (new LicenseResponder(false)).AggregateLine, maxProgressLines: 250); }); }
// Private method to avoid too deeply nested code in "DoResolution". private void GradleResolve(AndroidSdkPackageCollection packages, PlayServicesSupport svcSupport, string destinationDirectory, System.Action resolutionComplete) { string errorOutro = "make sure you have the latest version of this plugin and if you " + "still get this error, report it in a a bug here:\n" + "https://github.com/googlesamples/unity-jar-resolver/issues\n"; string errorIntro = null; int targetSdkVersion = UnityCompat.GetAndroidTargetSDKVersion(); string buildToolsVersion = null; if (targetSdkVersion < 0) { // A value of -1 means the targetSDK Version enum returned "Auto" // instead of an actual version, so it's up to us to actually figure it out. targetSdkVersion = GetLatestInstalledAndroidPlatformVersion(packages); PlayServicesSupport.Log( String.Format("TargetSDK is set to Auto-detect, and the latest Platform has been " + "detected as: android-{0}", targetSdkVersion), level: PlayServicesSupport.LogLevel.Info, verbose: true); errorIntro = String.Format("The Target SDK is set to automatically pick the highest " + "installed platform in the Android Player Settings, which appears to be " + "\"android-{0}\". This requires build-tools with at least the same version, " + "however ", targetSdkVersion); } else { errorIntro = String.Format("The target SDK version is set in the Android Player " + "Settings to \"android-{0}\" which requires build tools with " + "at least the same version, however ", targetSdkVersion); } // You can use a higher version of the build-tools than your compileSdkVersion, in order // to pick up new/better compiler while not changing what you build your app against. --Xav // https://stackoverflow.com/a/24523113 // Implicitly Xav is also saying, you can't use a build tool version less than the // platform level you're building. This is backed up from testing. if (targetSdkVersion > TESTED_BUILD_TOOLS_VERSION_MAJOR) { buildToolsVersion = GetLatestMinorBuildToolsVersion(packages, targetSdkVersion); if (buildToolsVersion == null) { PlayServicesSupport.Log(errorIntro + String.Format("no build-tools are available " + "at this level in the sdk manager. This plugin has been tested with " + "platforms up to android-{0} using build-tools {0}.{1}.{2}. You can try " + "selecting a lower targetSdkVersion in the Android Player Settings. Please ", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV) + errorOutro, level: PlayServicesSupport.LogLevel.Error); return; } else { PlayServicesSupport.Log(errorIntro + String.Format("this plugin has only been " + "tested with build-tools up to version {0}.{1}.{2}. Corresponding " + "build-tools version {3} will be used, however this is untested with this " + "plugin and MAY NOT WORK! If you have trouble, please select a target SDK " + "version at or below \"android-{0}\". If you need to get this working with " + "the latest platform, please ", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV, buildToolsVersion) + errorOutro, level: PlayServicesSupport.LogLevel.Warning); } } if (buildToolsVersion == null) { // Use the tested build tools version, which we know will be able to handle // this targetSDK version. buildToolsVersion = String.Format("{0}.{1}.{2}", TESTED_BUILD_TOOLS_VERSION_MAJOR, TESTED_BUILD_TOOLS_VERSION_MINOR, TESTED_BUILD_TOOLS_VERSION_REV); // We don't have to bother with checking if it's installed because gradle actually // does that for us. } string minSdkVersion = UnityCompat.GetAndroidMinSDKVersion().ToString(); var config = new Dictionary <string, string>() { { "app_id", UnityCompat.ApplicationId }, { "sdk_version", targetSdkVersion.ToString() }, { "min_sdk_version", minSdkVersion }, { "build_tools_version", buildToolsVersion }, { "android_sdk_dir", svcSupport.SDK } }; // This creates an enumerable of strings with the json lines for each dep like this: // "[\"namespace\", \"package\", \"version\"]" var dependencies = svcSupport.LoadDependencies(true, true, false); var depLines = from d in dependencies select "[" + ToJSONList(DepsVersionAsArray(d.Value)) + "]"; // Get a flattened list of dependencies, excluding any with the "$SDK" path variable, // since those will automatically be included in the gradle build. var repoLines = new HashSet <string>( dependencies.SelectMany(d => d.Value.Repositories) .Where(s => !s.Contains(PlayServicesSupport.SdkVariable))); var proguard_config_paths = new List <string>() { Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_UNITY_CONFIG), Path.Combine(GRADLE_SCRIPT_LOCATION, PROGUARD_MSG_FIX_CONFIG) }; // Build the full json config as a string. string json_config = @"{{ ""config"": {{ {0} }}, ""project_deps"": [ {1} ], ""extra_m2repositories"": [ {2} ], ""extra_proguard_configs"": [ {3} ] }}"; json_config = String.Format(json_config, ToJSONDictionary(config), ToJSONList(depLines, ",\n", 4, true), ToJSONList(repoLines, ",\n", 4), ToJSONList(proguard_config_paths, ",\n", 4)); // Escape any literal backslashes (such as those from paths on windows), since we want to // preserve them when reading the config as backslashes and not interpret them // as escape characters. json_config = json_config.Replace(@"\", @"\\"); System.IO.File.WriteAllText(GENERATE_CONFIG_PATH, json_config); var outDir = Path.Combine(destinationDirectory, GENERATE_GRADLE_OUTPUT_DIR); RunGenGradleScript( " -c \"" + GENERATE_CONFIG_PATH + "\"" + " -b \"" + GENERATE_GRADLE_BUILD_PATH + "\"" + " -o \"" + outDir + "\"", (result) => { if (result.exitCode == 0) { var currentAbi = PlayServicesResolver.AndroidTargetDeviceAbi; var activeAbis = GetSelectedABIDirs(currentAbi); var libsDir = Path.Combine(outDir, "libs"); if (Directory.Exists(libsDir)) { foreach (var directory in Directory.GetDirectories(libsDir)) { var abiDir = Path.GetFileName(directory).ToLower(); if (!activeAbis.Contains(abiDir)) { PlayServicesSupport.DeleteExistingFileOrDirectory( directory, includeMetaFiles: true); } } } if (Directory.Exists(outDir)) { PlayServicesResolver.LabelAssets(new [] { outDir }, true, true); } AssetDatabase.Refresh(); resolutionComplete(); } }); }