2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-10 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #ifndef __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__
27 #define __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__
29 #include "jucer_ProjectExporter.h"
32 //==============================================================================
33 class AndroidProjectExporter
: public ProjectExporter
36 //==============================================================================
37 static const char* getNameAndroid() { return "Android Project"; }
38 static const char* getValueTreeTypeName() { return "ANDROID"; }
40 static AndroidProjectExporter
* createForSettings (Project
& project
, const ValueTree
& settings
)
42 if (settings
.hasType (getValueTreeTypeName()))
43 return new AndroidProjectExporter (project
, settings
);
48 //==============================================================================
49 AndroidProjectExporter (Project
& project_
, const ValueTree
& settings_
)
50 : ProjectExporter (project_
, settings_
)
52 name
= getNameAndroid();
54 if (getTargetLocation().toString().isEmpty())
55 getTargetLocation() = getDefaultBuildsRootFolder() + "Android";
57 if (getSDKPath().toString().isEmpty())
58 getSDKPath() = "${user.home}/SDKs/android-sdk-mac_x86";
60 if (getNDKPath().toString().isEmpty())
61 getNDKPath() = "${user.home}/SDKs/android-ndk-r5";
63 if (getInternetNeeded().toString().isEmpty())
64 getInternetNeeded() = true;
67 //==============================================================================
68 bool isDefaultFormatForCurrentOS()
77 bool isPossibleForCurrentProject() { return project
.isGUIApplication(); }
78 bool usesMMFiles() const { return false; }
84 void createPropertyEditors (Array
<PropertyComponent
*>& props
)
86 ProjectExporter::createPropertyEditors (props
);
88 props
.add (new TextPropertyComponent (getSDKPath(), "Android SDK Path", 1024, false));
89 props
.getLast()->setTooltip ("The path to the Android SDK folder on the target build machine");
91 props
.add (new TextPropertyComponent (getNDKPath(), "Android NDK Path", 1024, false));
92 props
.getLast()->setTooltip ("The path to the Android NDK folder on the target build machine");
94 props
.add (new BooleanPropertyComponent (getInternetNeeded(), "Internet Access", "Specify internet access permission in the manifest"));
95 props
.getLast()->setTooltip ("If enabled, this will set the android.permission.INTERNET flag in the manifest.");
98 Value
getSDKPath() const { return getSetting (Ids::androidSDKPath
); }
99 Value
getNDKPath() const { return getSetting (Ids::androidNDKPath
); }
100 Value
getInternetNeeded() const { return getSetting (Ids::androidInternetNeeded
); }
102 //==============================================================================
105 const File
target (getTargetFolder());
106 const File
jniFolder (target
.getChildFile ("jni"));
108 createDirectoryOrThrow (target
.getChildFile ("src/com"));
109 createDirectoryOrThrow (jniFolder
);
110 createDirectoryOrThrow (target
.getChildFile ("res/drawable-hdpi"));
111 createDirectoryOrThrow (target
.getChildFile ("res/drawable-mdpi"));
112 createDirectoryOrThrow (target
.getChildFile ("res/drawable-ldpi"));
113 createDirectoryOrThrow (target
.getChildFile ("res/values"));
114 createDirectoryOrThrow (target
.getChildFile ("libs"));
115 createDirectoryOrThrow (target
.getChildFile ("bin"));
118 ScopedPointer
<XmlElement
> manifest (createManifestXML());
119 writeXmlOrThrow (*manifest
, target
.getChildFile ("AndroidManifest.xml"), "utf-8", 100);
122 writeApplicationMk (jniFolder
.getChildFile ("Application.mk"));
123 writeAndroidMk (jniFolder
.getChildFile ("Android.mk"));
126 ScopedPointer
<XmlElement
> antBuildXml (createAntBuildXML());
127 writeXmlOrThrow (*antBuildXml
, target
.getChildFile ("build.xml"), "UTF-8", 100);
130 writeBuildPropertiesFile (target
.getChildFile ("build.properties"));
131 writeDefaultPropertiesFile (target
.getChildFile ("default.properties"));
132 writeLocalPropertiesFile (target
.getChildFile ("local.properties"));
134 writeIcon (target
.getChildFile ("res/drawable-hdpi/icon.png"), 72);
135 writeIcon (target
.getChildFile ("res/drawable-mdpi/icon.png"), 48);
136 writeIcon (target
.getChildFile ("res/drawable-ldpi/icon.png"), 36);
138 writeStringsFile (target
.getChildFile ("res/values/strings.xml"));
142 //==============================================================================
143 XmlElement
* createManifestXML()
145 XmlElement
* manifest
= new XmlElement ("manifest");
147 manifest
->setAttribute ("xmlns:android", "http://schemas.android.com/apk/res/android");
148 manifest
->setAttribute ("android:versionCode", "1");
149 manifest
->setAttribute ("android:versionName", "1.0");
150 manifest
->setAttribute ("package", "com.juce");
152 XmlElement
* screens
= manifest
->createNewChildElement ("supports-screens");
153 screens
->setAttribute ("android:smallScreens", "true");
154 screens
->setAttribute ("android:normalScreens", "true");
155 screens
->setAttribute ("android:largeScreens", "true");
156 screens
->setAttribute ("android:xlargeScreens", "true");
157 screens
->setAttribute ("android:anyDensity", "true");
159 if (getInternetNeeded().getValue())
161 XmlElement
* permission
= manifest
->createNewChildElement ("uses-permission");
162 permission
->setAttribute ("android:name", "android.permission.INTERNET");
165 XmlElement
* app
= manifest
->createNewChildElement ("application");
166 app
->setAttribute ("android:label", "@string/app_name");
167 app
->setAttribute ("android:icon", "@drawable/icon");
169 XmlElement
* act
= app
->createNewChildElement ("activity");
170 act
->setAttribute ("android:name", "JuceAppActivity");
171 act
->setAttribute ("android:label", "@string/app_name");
173 XmlElement
* intent
= act
->createNewChildElement ("intent-filter");
174 intent
->createNewChildElement ("action")->setAttribute ("android:name", "android.intent.action.MAIN");
175 intent
->createNewChildElement ("category")->setAttribute ("android:name", "android.intent.category.LAUNCHER");
180 //==============================================================================
181 void findAllFilesToCompile (const Project::Item
& projectItem
, Array
<RelativePath
>& results
)
183 if (projectItem
.isGroup())
185 for (int i
= 0; i
< projectItem
.getNumChildren(); ++i
)
186 findAllFilesToCompile (projectItem
.getChild(i
), results
);
190 if (projectItem
.shouldBeCompiled())
191 results
.add (RelativePath (projectItem
.getFile(), getTargetFolder(), RelativePath::buildTargetFolder
));
195 void writeApplicationMk (const File
& file
)
197 MemoryOutputStream mo
;
199 mo
<< "# Automatically generated makefile, created by the Jucer" << newLine
200 << "# Don't edit this file! Your changes will be overwritten when you re-save the Jucer project!" << newLine
202 << "APP_STL := gnustl_static" << newLine
203 << "APP_CPPFLAGS += -fsigned-char -fexceptions -frtti" << newLine
;
205 overwriteFileIfDifferentOrThrow (file
, mo
);
208 void writeAndroidMk (const File
& file
)
210 Array
<RelativePath
> files
;
211 findAllFilesToCompile (project
.getMainGroup(), files
);
213 for (int i
= 0; i
< juceWrapperFiles
.size(); ++i
)
214 if (shouldFileBeCompiledByDefault (juceWrapperFiles
.getReference(i
)))
215 files
.add (juceWrapperFiles
.getReference(i
));
217 MemoryOutputStream mo
;
218 writeAndroidMk (mo
, files
);
220 overwriteFileIfDifferentOrThrow (file
, mo
);
223 void writeAndroidMk (OutputStream
& out
, const Array
<RelativePath
>& files
)
225 out
<< "# Automatically generated makefile, created by the Jucer" << newLine
226 << "# Don't edit this file! Your changes will be overwritten when you re-save the Jucer project!" << newLine
228 << "LOCAL_PATH := $(call my-dir)" << newLine
230 << "include $(CLEAR_VARS)" << newLine
232 << "LOCAL_CPP_EXTENSION := cpp" << newLine
233 << "LOCAL_MODULE := juce_jni" << newLine
234 << "LOCAL_SRC_FILES := \\" << newLine
;
236 for (int i
= 0; i
< files
.size(); ++i
)
237 out
<< " ../" << escapeSpaces (files
.getReference(i
).toUnixStyle()) << "\\" << newLine
;
240 << "ifeq ($(CONFIG),Debug)" << newLine
241 << " LOCAL_CPPFLAGS += " << createCPPFlags (true) << newLine
243 << " LOCAL_CPPFLAGS += " << createCPPFlags (false) << newLine
244 << "endif" << newLine
246 << "include $(BUILD_SHARED_LIBRARY)" << newLine
;
249 const String
createCPPFlags (bool forDebug
)
251 String
flags ("-fsigned-char -fexceptions -frtti");
256 StringPairArray defines
;
257 defines
.set ("JUCE_ANDROID", "1");
261 defines
.set ("DEBUG", "1");
262 defines
.set ("_DEBUG", "1");
266 defines
.set ("NDEBUG", "1");
269 for (int i
= 0; i
< project
.getNumConfigurations(); ++i
)
271 Project::BuildConfiguration
config (project
.getConfiguration(i
));
273 if (config
.isDebug() == forDebug
)
275 flags
<< " -O" << config
.getGCCOptimisationFlag();
277 defines
= mergePreprocessorDefs (defines
, getAllPreprocessorDefs (config
));
282 return flags
+ createGCCPreprocessorFlags (defines
);
285 //==============================================================================
286 XmlElement
* createAntBuildXML()
288 XmlElement
* proj
= new XmlElement ("project");
289 proj
->setAttribute ("name", project
.getProjectName().toString());
290 proj
->setAttribute ("default", "debug");
292 proj
->createNewChildElement ("property")->setAttribute ("file", "local.properties");
293 proj
->createNewChildElement ("property")->setAttribute ("file", "build.properties");
294 proj
->createNewChildElement ("property")->setAttribute ("file", "default.properties");
296 XmlElement
* path
= proj
->createNewChildElement ("path");
297 path
->setAttribute ("id", "android.antlibs");
298 path
->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/anttasks.jar");
299 path
->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/sdklib.jar");
300 path
->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/androidprefs.jar");
302 XmlElement
* taskdef
= proj
->createNewChildElement ("taskdef");
303 taskdef
->setAttribute ("name", "setup");
304 taskdef
->setAttribute ("classname", "com.android.ant.SetupTask");
305 taskdef
->setAttribute ("classpathref", "android.antlibs");
307 addNDKBuildStep (proj
, "clean", "clean");
309 //addLinkStep (proj, "${basedir}/" + rebaseFromProjectFolderToBuildTarget (RelativePath()).toUnixStyle() + "/", "jni/app");
310 addLinkStep (proj
, "${basedir}/" + getJucePathFromTargetFolder().toUnixStyle() + "/src/native/android/java/", "src/com/juce");
312 addNDKBuildStep (proj
, "debug", "CONFIG=Debug");
313 addNDKBuildStep (proj
, "release", "CONFIG=Release");
315 proj
->createNewChildElement ("setup");
320 static void addNDKBuildStep (XmlElement
* project
, const String
& type
, const String
& arg
)
322 XmlElement
* target
= project
->createNewChildElement ("target");
323 target
->setAttribute ("name", type
);
325 XmlElement
* executable
= target
->createNewChildElement ("exec");
326 executable
->setAttribute ("executable", "${ndk.dir}/ndk-build");
327 executable
->setAttribute ("dir", "${basedir}");
328 executable
->setAttribute ("failonerror", "true");
330 executable
->createNewChildElement ("arg")->setAttribute ("value", "--jobs=2");
331 executable
->createNewChildElement ("arg")->setAttribute ("value", arg
);
334 static void addLinkStep (XmlElement
* project
, const String
& from
, const String
& to
)
336 XmlElement
* executable
= project
->createNewChildElement ("exec");
337 executable
->setAttribute ("executable", "ln");
338 executable
->setAttribute ("dir", "${basedir}");
339 executable
->setAttribute ("failonerror", "false");
341 executable
->createNewChildElement ("arg")->setAttribute ("value", "-s");
342 executable
->createNewChildElement ("arg")->setAttribute ("value", from
);
343 executable
->createNewChildElement ("arg")->setAttribute ("value", to
);
346 void writeBuildPropertiesFile (const File
& file
)
348 MemoryOutputStream mo
;
349 mo
<< "# This file is used to override default values used by the Ant build system." << newLine
;
350 overwriteFileIfDifferentOrThrow (file
, mo
);
353 void writeDefaultPropertiesFile (const File
& file
)
355 MemoryOutputStream mo
;
356 mo
<< "# This file is used to override default values used by the Ant build system." << newLine
357 << "# It is automatically generated - DO NOT EDIT IT or your changes will be lost!." << newLine
359 << "target=android-9"
362 overwriteFileIfDifferentOrThrow (file
, mo
);
365 void writeLocalPropertiesFile (const File
& file
)
367 MemoryOutputStream mo
;
368 mo
<< "# This file is used to override default values used by the Ant build system." << newLine
369 << "# It is automatically generated by the Jucer - DO NOT EDIT IT or your changes will be lost!." << newLine
371 << "sdk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getSDKPath().toString())) << newLine
372 << "ndk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getNDKPath().toString())) << newLine
375 overwriteFileIfDifferentOrThrow (file
, mo
);
378 void writeIcon (const File
& file
, int size
)
380 Image
im (project
.getBestIconForSize (size
, false));
385 MemoryOutputStream mo
;
387 if (! png
.writeImageToStream (im
, mo
))
388 throw SaveError ("Can't generate Android icon file");
390 overwriteFileIfDifferentOrThrow (file
, mo
);
394 void writeStringsFile (const File
& file
)
396 XmlElement
strings ("resources");
397 XmlElement
* name
= strings
.createNewChildElement ("string");
398 name
->setAttribute ("name", "app_name");
399 name
->addTextElement (project
.getProjectName().toString());
401 writeXmlOrThrow (strings
, file
, "utf-8", 100);
404 //==============================================================================
405 JUCE_DECLARE_NON_COPYABLE (AndroidProjectExporter
);
409 #endif // __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__