1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00
JUCE/extras/Projucer/Source/Project Saving/jucer_ProjectExport_AndroidStudio.h

853 lines
32 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
class AndroidStudioProjectExporter : public AndroidProjectExporterBase
{
public:
//==============================================================================
bool usesMMFiles() const override { return false; }
bool canCopeWithDuplicateFiles() override { return false; }
bool supportsUserDefinedConfigurations() const override { return false; }
bool isAndroidStudio() const override { return true; }
bool isAndroidAnt() const override { return false; }
static const char* getName() { return "Android Studio"; }
static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; }
static AndroidStudioProjectExporter* createForSettings (Project& project, const ValueTree& settings)
{
if (settings.hasType (getValueTreeTypeName()))
return new AndroidStudioProjectExporter (project, settings);
return nullptr;
}
//==============================================================================
CachedValue<String> gradleVersion, gradleWrapperVersion, gradleToolchain;
//==============================================================================
AndroidStudioProjectExporter (Project& p, const ValueTree& t)
: AndroidProjectExporterBase (p, t),
gradleVersion (settings, Ids::gradleVersion, nullptr, "2.10"),
gradleWrapperVersion (settings, Ids::gradleWrapperVersion, nullptr, "0.7.0-alpha4"),
gradleToolchain (settings, Ids::gradleToolchain, nullptr, "clang"),
androidStudioExecutable (findAndroidStudioExecutable())
{
name = getName();
if (getTargetLocationString().isEmpty())
getTargetLocationValue() = getDefaultBuildsRootFolder() + "AndroidStudio";
}
//==============================================================================
void createToolchainExporterProperties (PropertyListBuilder& props) override
{
props.add (new TextWithDefaultPropertyComponent<String> (gradleVersion, "gradle version", 32),
"The version of gradle that Android Studio should use to build this app");
props.add (new TextWithDefaultPropertyComponent<String> (gradleWrapperVersion, "gradle-experimental wrapper version", 32),
"The version of the gradle-experimental wrapper that Android Studio should use to build this app");
static const char* toolchains[] = { "clang", "gcc", nullptr };
props.add (new ChoicePropertyComponent (gradleToolchain.getPropertyAsValue(), "NDK Toolchain", StringArray (toolchains), Array<var> (toolchains)),
"The toolchain that gradle should invoke for NDK compilation (variable model.android.ndk.tooclhain in app/build.gradle)");
}
void createLibraryModuleExporterProperties (PropertyListBuilder&) override
{
// gradle cannot do native library modules as far as we know...
}
//==============================================================================
bool canLaunchProject() override
{
return androidStudioExecutable.exists();
}
bool launchProject() override
{
if (! androidStudioExecutable.exists())
{
jassertfalse;
return false;
}
const File targetFolder (getTargetFolder());
// we have to surround the path with extra quotes, otherwise Android Studio
// will choke if there are any space characters in the path.
return androidStudioExecutable.startAsProcess ("\"" + targetFolder.getFullPathName() + "\"");
}
Value getNDKPlatformVersionValue() { return getSetting (Ids::androidNdkPlatformVersion); }
String getNDKPlatformVersionString() const { return settings [Ids::androidNdkPlatformVersion]; }
Value getBuildToolsVersionValue() { return getSetting (Ids::buildToolsVersion); }
String getBuildToolsVersionString() const { return settings [Ids::buildToolsVersion]; }
//==============================================================================
void create (const OwnedArray<LibraryModule>& modules) const override
{
const File targetFolder (getTargetFolder());
removeOldFiles (targetFolder);
{
const String package (getActivityClassPackage());
const String path (package.replaceCharacter ('.', File::separator));
const File javaTarget (targetFolder.getChildFile ("app/src/main/java").getChildFile (path));
copyActivityJavaFiles (modules, javaTarget, package);
}
writeFile (targetFolder, "settings.gradle", getSettingsGradleFileContent());
writeFile (targetFolder, "build.gradle", getProjectBuildGradleFileContent());
writeFile (targetFolder, "app/build.gradle", getAppBuildGradleFileContent());
writeFile (targetFolder, "local.properties", getLocalPropertiesFileContent());
writeFile (targetFolder, "gradle/wrapper/gradle-wrapper.properties", getGradleWrapperPropertiesFileContent());
writeAndroidManifest (targetFolder);
writeStringsXML (targetFolder);
writeAppIcons (targetFolder);
createSourceSymlinks (targetFolder);
}
void removeOldFiles (const File& targetFolder) const
{
targetFolder.getChildFile ("app/src").deleteRecursively();
targetFolder.getChildFile ("app/build").deleteRecursively();
targetFolder.getChildFile ("app/build.gradle").deleteFile();
targetFolder.getChildFile ("gradle").deleteRecursively();
targetFolder.getChildFile ("local.properties").deleteFile();
targetFolder.getChildFile ("settings.gradle").deleteFile();
}
void writeFile (const File& gradleProjectFolder, const String& filePath, const String& fileContent) const
{
MemoryOutputStream outStream;
outStream << fileContent;
overwriteFileIfDifferentOrThrow (gradleProjectFolder.getChildFile (filePath), outStream);
}
//==============================================================================
static File findAndroidStudioExecutable()
{
#if JUCE_WINDOWS
const File defaultInstallation ("C:\\Program Files\\Android\\Android Studio\\bin");
if (defaultInstallation.exists())
{
{
const File studio64 = defaultInstallation.getChildFile ("studio64.exe");
if (studio64.existsAsFile())
return studio64;
}
{
const File studio = defaultInstallation.getChildFile ("studio.exe");
if (studio.existsAsFile())
return studio;
}
}
#elif JUCE_MAC
const File defaultInstallation ("/Applications/Android Studio.app");
if (defaultInstallation.exists())
return defaultInstallation;
#endif
return File::nonexistent;
}
protected:
//==============================================================================
class AndroidStudioBuildConfiguration : public BuildConfiguration
{
public:
AndroidStudioBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
: BuildConfiguration (p, settings, e)
{
if (getArchitectures().isEmpty())
{
if (isDebug())
getArchitecturesValue() = "armeabi x86";
else
getArchitecturesValue() = "armeabi armeabi-v7a x86";
}
}
Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); }
String getArchitectures() const { return config [Ids::androidArchitectures]; }
var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
void createConfigProperties (PropertyListBuilder& props) override
{
addGCCOptimisationProperty (props);
props.add (new TextPropertyComponent (getArchitecturesValue(), "Architectures", 256, false),
"A list of the ARM architectures to build (for a fat binary).");
}
};
BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
{
return new AndroidStudioBuildConfiguration (project, v, *this);
}
private:
static void createSymlinkAndParentFolders (const File& originalFile, const File& linkFile)
{
{
const File linkFileParentDirectory (linkFile.getParentDirectory());
// this will recursively creative the parent directories for the file.
// without this, the symlink would fail because it doesn't automatically create
// the folders if they don't exist
if (! linkFileParentDirectory.createDirectory())
throw SaveError (String ("Could not create directory ") + linkFileParentDirectory.getFullPathName());
}
if (! originalFile.createSymbolicLink (linkFile, true))
throw SaveError (String ("Failed to create symlink from ")
+ linkFile.getFullPathName() + " to "
+ originalFile.getFullPathName() + "!");
}
void makeSymlinksForGroup (const Project::Item& group, const File& targetFolder) const
{
if (! group.isGroup())
{
throw SaveError ("makeSymlinksForGroup was called with something other than a group!");
}
for (int i = 0; i < group.getNumChildren(); ++i)
{
const Project::Item& projectItem = group.getChild (i);
if (projectItem.isGroup())
{
makeSymlinksForGroup (projectItem, targetFolder.getChildFile (projectItem.getName()));
}
else if (projectItem.shouldBeAddedToTargetProject()) // must be a file then
{
const File originalFile (projectItem.getFile());
const File targetFile (targetFolder.getChildFile (originalFile.getFileName()));
createSymlinkAndParentFolders (originalFile, targetFile);
}
}
}
void createSourceSymlinks (const File& folder) const
{
const File targetFolder (folder.getChildFile ("app/src/main/jni"));
// here we make symlinks to only to files included in the groups inside the project
// this is because Android Studio does not have a concept of groups and just uses
// the file system layout to determine what's to be compiled
{
const Array<Project::Item>& groups = getAllGroups();
for (int i = 0; i < groups.size(); ++i)
{
const Project::Item projectItem (groups.getReference (i));
const String projectItemName (projectItem.getName());
if (projectItem.isGroup())
makeSymlinksForGroup (projectItem, projectItemName == "Juce Modules" ? targetFolder.getChildFile ("JuceModules") : targetFolder);
}
}
}
void writeAppIcons (const File& folder) const
{
writeIcons (folder.getChildFile ("app/src/main/res/"));
}
static String sanitisePath (String path)
{
return expandHomeFolderToken (path).replace ("\\", "\\\\");
}
static String expandHomeFolderToken (const String& path)
{
String homeFolder = File::getSpecialLocation (File::userHomeDirectory).getFullPathName();
return path.replace ("${user.home}", homeFolder)
.replace ("~", homeFolder);
}
//==============================================================================
struct GradleElement
{
virtual ~GradleElement() {}
String toString() const { return toStringIndented (0); }
virtual String toStringIndented (int indentLevel) const = 0;
static String indent (int indentLevel) { return String::repeatedString (" ", indentLevel); }
};
//==============================================================================
struct GradleStatement : public GradleElement
{
GradleStatement (const String& s) : statement (s) {}
String toStringIndented (int indentLevel) const override { return indent (indentLevel) + statement; }
String statement;
};
//==============================================================================
struct GradleCppFlag : public GradleStatement
{
GradleCppFlag (const String& flag)
: GradleStatement ("cppFlags.add(" + flag.quoted() + ")") {}
};
struct GradlePreprocessorDefine : public GradleStatement
{
GradlePreprocessorDefine (const String& define, const String& value)
: GradleStatement ("cppFlags.add(\"-D" + define + "=" + value + "\")") {}
};
struct GradleHeaderIncludePath : public GradleStatement
{
GradleHeaderIncludePath (const String& path)
: GradleStatement ("cppFlags.add(\"-I" + sanitisePath (path) + "\".toString())") {}
};
struct GradleLibrarySearchPath : public GradleStatement
{
GradleLibrarySearchPath (const String& path)
: GradleStatement ("cppFlags.add(\"-L" + sanitisePath (path) + "\".toString())") {}
};
struct GradleLinkerFlag : public GradleStatement
{
GradleLinkerFlag (const String& flag)
: GradleStatement ("ldFlags.add(" + flag.quoted() + "\")") {}
};
struct GradleLinkLibrary : public GradleStatement
{
GradleLinkLibrary (const String& lib)
: GradleStatement ("ldLibs.add(" + lib.quoted() + ")") {}
};
//==============================================================================
struct GradleValue : public GradleElement
{
template <typename ValueType>
GradleValue (const String& k, const ValueType& v)
: key (k), value (v) {}
GradleValue (const String& k, bool boolValue)
: key (k), value (boolValue ? "true" : "false") {}
String toStringIndented (int indentLevel) const override
{
return indent (indentLevel) + key + " = " + value;
}
protected:
String key, value;
};
struct GradleString : public GradleValue
{
GradleString (const String& k, const String& str)
: GradleValue (k, str.quoted())
{
if (str.containsAnyOf ("${\"\'"))
value += ".toString()";
}
};
struct GradleFilePath : public GradleValue
{
GradleFilePath (const String& k, const String& path)
: GradleValue (k, "new File(\"" + sanitisePath (path) + "\")") {}
};
//==============================================================================
struct GradleObject : public GradleElement
{
GradleObject (const String& nm) : name (nm) {}
#if JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES
template <typename GradleType, typename... Args>
void add (Args... args)
{
children.add (new GradleType (args...));
// Note: can't use std::forward because it doesn't compile for OS X 10.8
}
#else // Remove this workaround once we drop VS2012 support!
template <typename GradleType, typename Arg1>
void add (Arg1 arg1)
{
children.add (new GradleType (arg1));
}
template <typename GradleType, typename Arg1, typename Arg2>
void add (Arg1 arg1, Arg2 arg2)
{
children.add (new GradleType (arg1, arg2));
}
#endif
void addChildObject (GradleObject* objectToAdd) noexcept
{
children.add (objectToAdd);
}
String toStringIndented (int indentLevel) const override
{
String result;
result << indent (indentLevel) << name << " {" << newLine;
for (const auto& child : children)
result << child->toStringIndented (indentLevel + 1) << newLine;
result << indent (indentLevel) << "}";
if (indentLevel == 0)
result << newLine;
return result;
}
private:
String name;
OwnedArray<GradleElement> children;
};
//==============================================================================
String getSettingsGradleFileContent() const
{
return "include ':app'";
}
String getProjectBuildGradleFileContent() const
{
String projectBuildGradle;
projectBuildGradle << getGradleBuildScript();
projectBuildGradle << getGradleAllProjects();
return projectBuildGradle;
}
//==============================================================================
String getGradleBuildScript() const
{
GradleObject buildScript ("buildscript");
buildScript.addChildObject (getGradleRepositories());
buildScript.addChildObject (getGradleDependencies());
return buildScript.toString();
}
GradleObject* getGradleRepositories() const
{
auto repositories = new GradleObject ("repositories");
repositories->add<GradleStatement> ("jcenter()");
return repositories;
}
GradleObject* getGradleDependencies() const
{
auto dependencies = new GradleObject ("dependencies");
dependencies->add<GradleStatement> ("classpath 'com.android.tools.build:gradle-experimental:"
+ gradleWrapperVersion.get() + "'");
return dependencies;
}
String getGradleAllProjects() const
{
GradleObject allProjects ("allprojects");
allProjects.addChildObject (getGradleRepositories());
return allProjects.toString();
}
//==============================================================================
String getAppBuildGradleFileContent() const
{
String appBuildGradle;
appBuildGradle << "apply plugin: 'com.android.model.application'" << newLine;
appBuildGradle << getAndroidModel();
appBuildGradle << getAppDependencies();
return appBuildGradle;
}
String getAndroidModel() const
{
GradleObject model ("model");
model.addChildObject (getAndroidObject());
model.addChildObject (getAndroidNdkSettings());
model.addChildObject (getAndroidSources());
model.addChildObject (getAndroidBuildConfigs());
model.addChildObject (getAndroidSigningConfigs());
model.addChildObject (getAndroidProductFlavours());
return model.toString();
}
String getAppDependencies() const
{
GradleObject dependencies ("dependencies");
dependencies.add<GradleStatement> ("compile \"com.android.support:support-v4:+\"");
return dependencies.toString();
}
//==============================================================================
GradleObject* getAndroidObject() const
{
auto android = new GradleObject ("android");
android->add<GradleValue> ("compileSdkVersion", androidMinimumSDK.get().getIntValue());
android->add<GradleString> ("buildToolsVersion", getBuildToolsVersionString());
android->addChildObject (getAndroidDefaultConfig());
return android;
}
GradleObject* getAndroidDefaultConfig() const
{
const String bundleIdentifier = project.getBundleIdentifier().toString().toLowerCase();
const int minSdkVersion = androidMinimumSDK.get().getIntValue();
auto defaultConfig = new GradleObject ("defaultConfig.with");
defaultConfig->add<GradleString> ("applicationId", bundleIdentifier);
defaultConfig->add<GradleValue> ("minSdkVersion.apiLevel", minSdkVersion);
defaultConfig->add<GradleValue> ("targetSdkVersion.apiLevel", minSdkVersion);
return defaultConfig;
}
GradleObject* getAndroidNdkSettings() const
{
const String toolchain = gradleToolchain.get();
const bool isClang = (toolchain == "clang");
auto ndkSettings = new GradleObject ("android.ndk");
ndkSettings->add<GradleString> ("moduleName", "juce_jni");
ndkSettings->add<GradleString> ("toolchain", toolchain);
if (isClang)
{
ndkSettings->add<GradleString> ("stl", "c++_static");
}
else
{
ndkSettings->add<GradleValue> ("toolchainVersion", "4.9");
ndkSettings->add<GradleString> ("stl", "gnustl_static");
}
ndkSettings->addChildObject (getNdkJuceExtraProperties());
addAllNdkCompilerSettings (ndkSettings);
return ndkSettings;
}
GradleObject* getNdkJuceExtraProperties() const
{
auto ext = new GradleObject ("ext");
ext->add<GradleString> ("juceRootDir", "${project.rootDir}/../../../../");
ext->add<GradleString> ("juceModuleDir", "${juceRootDir}/modules");
return ext;
}
void addAllNdkCompilerSettings (GradleObject* ndk) const
{
addNdkCppFlags (ndk);
addNdkPreprocessorDefines (ndk);
addNdkHeaderIncludePaths (ndk);
addNdkLinkerFlags (ndk);
addNdkLibraries (ndk);
}
void addNdkCppFlags (GradleObject* ndk) const
{
const char* alwaysUsedFlags[] = { "-fsigned-char", "-fexceptions", "-frtti", "-std=c++11", nullptr };
StringArray cppFlags (alwaysUsedFlags);
cppFlags.mergeArray (StringArray::fromTokens (getExtraCompilerFlagsString(), " ", ""));
for (int i = 0; i < cppFlags.size(); ++i)
ndk->add<GradleCppFlag> (cppFlags[i]);
}
void addNdkPreprocessorDefines (GradleObject* ndk) const
{
const auto& defines = getAllPreprocessorDefs();
for (int i = 0; i < defines.size(); ++i)
ndk->add<GradlePreprocessorDefine> ( defines.getAllKeys()[i], defines.getAllValues()[i]);
}
void addNdkHeaderIncludePaths (GradleObject* ndk) const
{
const char* basicJucePaths[] = { "${project.rootDir}/app", "${ext.juceRootDir}", "${ext.juceModuleDir}", nullptr };
StringArray includePaths (basicJucePaths);
auto cppFiles = getAllIncludedCppFiles();
for (const auto& cppFile : cppFiles)
includePaths.addIfNotAlreadyThere (getIncludePathForFile (cppFile));
for (const auto& path : includePaths)
ndk->add<GradleHeaderIncludePath> (path);
}
Array<RelativePath> getAllIncludedCppFiles() const
{
Array<RelativePath> cppFiles;
const auto& groups = getAllGroups();
for (int i = 0; i < groups.size(); ++i)
findAllProjectItemsWithPredicate (groups.getReference (i), cppFiles, ShouldBeAddedToProjectPredicate());
return cppFiles;
}
String getIncludePathForFile (const RelativePath& file) const
{
return sanitisePath (project.getProjectFolder().getFullPathName() + "/"
+ file.rebased (getTargetFolder(),
project.getProjectFolder(),
RelativePath::projectFolder)
.toUnixStyle()
.upToLastOccurrenceOf ("/", false, false));
}
void addNdkLinkerFlags (GradleObject* ndk) const
{
const auto linkerFlags = StringArray::fromTokens (getExtraLinkerFlagsString(), " ", "");
for (const auto& flag : linkerFlags)
ndk->add<GradleLinkerFlag> (flag);
}
void addNdkLibraries (GradleObject* ndk) const
{
const char* requiredAndroidLibs[] = { "android", "EGL", "GLESv2", "log", nullptr };
StringArray libs (requiredAndroidLibs);
libs.addArray (StringArray::fromTokens(getExternalLibrariesString(), ";", ""));
for (const auto& lib : libs)
ndk->add<GradleLinkLibrary> (lib);
}
GradleObject* getAndroidSources() const
{
auto source = new GradleObject ("source"); // app source folder
source->add<GradleStatement> ("exclude \"**/JuceModules/\"");
auto jni = new GradleObject ("jni"); // all C++ sources for app
jni->addChildObject (source);
auto main = new GradleObject ("main"); // all sources for app
main->addChildObject (jni);
auto sources = new GradleObject ("android.sources"); // all sources
sources->addChildObject (main);
return sources;
}
GradleObject* getAndroidBuildConfigs() const
{
auto buildConfigs = new GradleObject ("android.buildTypes");
for (ConstConfigIterator config (*this); config.next();)
buildConfigs->addChildObject (getBuildConfig (*config));
return buildConfigs;
}
GradleObject* getBuildConfig (const BuildConfiguration& config) const
{
const String configName (config.getName());
// Note: at the moment, Android Studio only supports a "debug" and a "release"
// build config, but no custom build configs like Projucer's other exporters do.
if (configName != "Debug" && configName != "Release")
throw SaveError ("Build configurations other than Debug and Release are not yet support for Android Studio");
auto gradleConfig = new GradleObject (configName.toLowerCase());
if (! config.isDebug())
gradleConfig->add<GradleValue> ("signingConfig", "$(\"android.signingConfigs.releaseConfig\")");
addConfigNdkSettings (gradleConfig, config);
return gradleConfig;
}
void addConfigNdkSettings (GradleObject* buildConfig, const BuildConfiguration& config) const
{
auto ndkSettings = new GradleObject ("ndk.with");
if (config.isDebug())
{
ndkSettings->add<GradleValue> ("debuggable", true);
ndkSettings->add<GradleCppFlag> ("-g");
ndkSettings->add<GradlePreprocessorDefine> ("DEBUG", "1");
ndkSettings->add<GradlePreprocessorDefine> ("_DEBUG", "1");
}
else
{
ndkSettings->add<GradlePreprocessorDefine> ("NDEBUG", "1");
}
ndkSettings->add<GradleCppFlag> ("-O" + config.getGCCOptimisationFlag());
for (const auto& path : config.getHeaderSearchPaths())
ndkSettings->add<GradleHeaderIncludePath> (path);
for (const auto& path : config.getLibrarySearchPaths())
ndkSettings->add<GradleLibrarySearchPath> (path);
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID", "1");
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get());
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_'));
ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSPATH","\\\"" + androidActivityClass.get().replaceCharacter('.', '/') + "\\\"");
const auto defines = config.getAllPreprocessorDefs();
for (int i = 0; i < defines.size(); ++i)
ndkSettings->add<GradlePreprocessorDefine> (defines.getAllKeys()[i], defines.getAllValues()[i]);
buildConfig->addChildObject (ndkSettings);
}
GradleObject* getAndroidSigningConfigs() const
{
auto releaseConfig = new GradleObject ("create(\"releaseConfig\")");
releaseConfig->add<GradleFilePath> ("storeFile", androidKeyStore.get());
releaseConfig->add<GradleString> ("storePassword", androidKeyStorePass.get());
releaseConfig->add<GradleString> ("keyAlias", androidKeyAlias.get());
releaseConfig->add<GradleString> ("keyPassword", androidKeyAliasPass.get());
releaseConfig->add<GradleString> ("storeType", "jks");
auto signingConfigs = new GradleObject ("android.signingConfigs");
signingConfigs->addChildObject (releaseConfig);
// Note: no need to add a debugConfig, Android Studio will use debug.keystore by default
return signingConfigs;
}
GradleObject* getAndroidProductFlavours() const
{
auto flavours = new GradleObject ("android.productFlavors");
StringArray architectures (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (true), " ", ""));
architectures.mergeArray (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (false), " ", ""));
if (architectures.size() == 0)
throw SaveError ("Can't build for no architectures!");
for (int i = 0; i < architectures.size(); ++i)
{
String arch (architectures[i].trim());
if ((arch).isEmpty())
continue;
flavours->addChildObject (getGradleProductFlavourForArch (arch));
}
return flavours;
}
GradleObject* getGradleProductFlavourForArch (const String& arch) const
{
auto flavour = new GradleObject ("create(\"" + arch + "\")");
flavour->add<GradleStatement> ("ndk.abiFilters.add(\"" + arch + "\")");
return flavour;
}
//==============================================================================
String getLocalPropertiesFileContent() const
{
String props;
props << "ndk.dir=" << sanitisePath (ndkPath.toString()) << newLine
<< "sdk.dir=" << sanitisePath (sdkPath.toString()) << newLine;
return props;
}
String getGradleWrapperPropertiesFileContent() const
{
String props;
props << "distributionUrl=https\\://services.gradle.org/distributions/gradle-"
<< gradleVersion.get() << "-all.zip";
return props;
}
//==============================================================================
void writeStringsXML (const File& folder) const
{
XmlElement strings ("resources");
XmlElement* resourceName = strings.createNewChildElement ("string");
resourceName->setAttribute ("name", "app_name");
resourceName->addTextElement (projectName);
writeXmlOrThrow (strings, folder.getChildFile ("app/src/main/res/values/string.xml"), "utf-8", 100, true);
}
//==============================================================================
void writeAndroidManifest (const File& folder) const
{
ScopedPointer<XmlElement> manifest (createManifestXML());
writeXmlOrThrow (*manifest, folder.getChildFile ("app/src/main/AndroidManifest.xml"), "utf-8", 100, true);
}
//==============================================================================
struct ShouldBeAddedToProjectPredicate
{
bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeAddedToTargetProject(); }
};
//==============================================================================
const File androidStudioExecutable;
JUCE_DECLARE_NON_COPYABLE (AndroidStudioProjectExporter)
};