Browse Source

Android toolchain for debug and release apks

JimMarlowe 9 năm trước cách đây
mục cha
commit
d215461616

+ 29 - 18
Resources/EditorData/AtomicEditor/editor/ui/buildsettings_android.tb.txt

@@ -10,6 +10,7 @@ TBLayout: axis: y, distribution: gravity, position: left
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBEditField: id: app_package, autofocus: 1
 				lp: min-width: 220
+				tooltip The naming convention is : com.companyname.appname
 	TBLayout: distribution: gravity
 		TBTextField: text: "Company Name:"
 		TBLayout: gravity: left right, distribution-position: right bottom
@@ -20,40 +21,50 @@ TBLayout: axis: y, distribution: gravity, position: left
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBEditField: id: product_name, autofocus: 1
 				lp: min-width: 220
+	TBLayout: axis: y, position: left, distribution: gravity
+		TBLayout: gravity: left right, distribution-position: right bottom
+			TBButton: id: choose_icon, lp: min-width: 48, min-height: 48, max-width: 48, max-height: 48
+				tooltip Location of the drawable folders for app icons
+				TBImageWidget: id: and_icon, filename: "Resources/ToolData/Deployment/Android/res/drawable-ldpi/icon.png"
+					lp: min-width: 36, min-height: 36, max-width: 36, max-height: 36
+			TBEditField: id: icon_root, autofocus: 0
+				lp: min-width: 220
 	TBSeparator: gravity: left right, skin: AESeparator
 	TBLayout: axis: y, position: left, distribution: gravity
 		TBTextField: text: "Android SDK Path:", skin: DarkGrayText
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBEditField: id: sdk_path, autofocus: 0
 				lp: min-width: 250
-			TBButton: text: "Choose" id: choose_sdk_path
+			TBButton: text: "Choose", id: choose_sdk_path
+				tooltip Location of the Android SDK folder
 	TBLayout: axis: y, position: left, distribution: gravity
 		TBTextField: text: "Android API Level:", skin: DarkGrayText
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBSelectDropdown: id: sdk_target_select
 				lp: min-width: 250
-			TBButton: text: "Refresh" id: refresh_sdk_targets
+			TBButton: text: "Refresh", id: refresh_sdk_targets
 	TBLayout: axis: y, position: left, distribution: gravity
-		TBTextField: text: "Ant Path:" id: ant_path_text, skin: DarkGrayText
+		TBTextField: text: "Ant Path:", id: ant_path_text, skin: DarkGrayText
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBEditField: id: ant_path, autofocus: 0
 				lp: min-width: 250
-			TBButton: text: "Choose" id: choose_ant_path
+				tooltip May be left blank if installed locally
+			TBButton: text: "Choose", id: choose_ant_path
+				tooltip May be left blank if installed locally
 	TBLayout: axis: y, position: left, distribution: gravity
-		TBTextField: text: "JDK Root:" id: jdk_root_text, skin: DarkGrayText
+		TBTextField: text: "JDK Root:", id: jdk_root_text, skin: DarkGrayText
 		TBLayout: gravity: left right, distribution-position: right bottom
 			TBEditField: id: jdk_root, autofocus: 0
 				lp: min-width: 250
-			TBButton: text: "Choose" id: choose_jdk_root
-	TBSeparator: gravity: left right, skin: AESeparator
-	TBLayout:
-		TBLayout: axis: y
-			TBTextField: text: "Icon:"
-			TBLayout: gravity: left right, distribution-position: right bottom
-				TBSkinImage: skin: LogoAtomic64
-					lp: min-width: 64, min-height:64
-		TBLayout: axis: y
-			TBTextField: text: "Splash Screen:"
-			TBLayout: gravity: left right, distribution-position: right bottom
-				TBSkinImage: skin: LogoAtomic64
-					lp: min-width: 64, min-height:64
+			TBButton: text: "Choose", id: choose_jdk_root
+				tooltip May be left blank if installed locally
+	TBLayout: axis: y, position: left, distribution: gravity
+		TBLayout: gravity: left, distribution-position: right bottom
+			TBClickLabel: text: "Release Path", skin: DarkGrayText
+				TBCheckBox: id: and_auth_check, lp: min-height: 20
+		TBLayout: gravity: left right, distribution-position: right bottom
+			TBEditField: id: auth_root, autofocus: 0
+				lp: min-width: 250
+				tooltip Select Release Path for a release version APK
+			TBButton: text: "Choose", id: choose_and_auth
+				tooltip Directory containing the ant.properties

BIN
Resources/EditorData/AtomicEditor/resources/default_skin_light/checkbox_mark.png


BIN
Resources/EditorData/AtomicEditor/resources/default_skin_light/checkbox_mark_grey.png


+ 80 - 61
Script/AtomicEditor/ui/modal/build/platforms/AndroidSettingsWidget.ts

@@ -31,7 +31,8 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
         this.load("AtomicEditor/editor/ui/buildsettings_android.tb.txt");
 
         this.settings = ToolCore.toolSystem.project.buildSettings.androidBuildSettings;
-
+        
+        this.sdkPathEdit = <Atomic.UIEditField>this.getWidget("sdk_path");
         this.sdkTargetSelect = <Atomic.UISelectDropdown>this.getWidget("sdk_target_select");
         this.appNameEdit = <Atomic.UIEditField>this.getWidget("app_name");
         this.packageNameEdit = <Atomic.UIEditField>this.getWidget("app_package");
@@ -45,19 +46,20 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
         var jdkRootText = <Atomic.UITextField>this.getWidget("jdk_root_text");
         var antPathText = <Atomic.UITextField>this.getWidget("ant_path_text");
 
+        this.releaseChooseButton = <Atomic.UIButton>this.getWidget("choose_and_auth");
+        this.releaseNameEdit = <Atomic.UIEditField>this.getWidget("auth_root");
+        this.releaseCheck = <Atomic.UICheckBox>this.getWidget("and_auth_check");
+        this.iconNameEdit = <Atomic.UIEditField>this.getWidget("icon_root");
+        this.iconChooseButton = <Atomic.UIButton>this.getWidget("choose_icon");
+        this.iconImage = <Atomic.UIImageWidget>this.getWidget("and_icon");
+        
         if (Atomic.platform == "Windows") {
 
             jdkRootText.text = "JDK Root: (Ex. C:\\Program Files\\Java\\jdk1.8.0_31)";
             antPathText.text = "Ant Path: (The folder that contains ant.bat)";
 
-        } else {
-
-            jdkRootText.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
-            this.jdkRootChooseButton.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
-            this.jdkRootEdit.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
-
         }
-
+        
         this.antPathEdit = <Atomic.UIEditField>this.getWidget("ant_path");
 
         this.refreshWidgets();
@@ -73,19 +75,9 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
             if (ev.target.id == "choose_sdk_path") {
 
                 var fileUtils = new Editor.FileUtils();
-                var path = fileUtils.getAndroidSDKPath("");
-
-                if (path.length) {
-
-                    var toolPrefs = ToolCore.toolEnvironment.toolPrefs;
-                    if (toolPrefs.androidSDKPath != path) {
-                        toolPrefs.androidSDKPath = path;
-                        toolPrefs.save();
-                    }
-
-                    this.refreshWidgets();
-
-                }
+                var path = fileUtils.findPath("Please choose the root folder of your Android SDK","");
+                if ( path.length > 0 )
+                    this.sdkPathEdit.text = path;
 
                 return true;
 
@@ -93,37 +85,17 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
 
                 var fileUtils = new Editor.FileUtils();
                 var path = fileUtils.getAntPath("");
-
-                if (path.length) {
-
-                    var toolPrefs = ToolCore.toolEnvironment.toolPrefs;
-                    if (toolPrefs.antPath != path) {
-                        toolPrefs.antPath = path;
-                        toolPrefs.save();
-                    }
-
-                    this.refreshWidgets();
-
-                }
+                if ( path.length > 0 )
+                    this.antPathEdit.text = path;
 
                 return true;
 
             }  else if (ev.target.id == "choose_jdk_root") {
 
                 var fileUtils = new Editor.FileUtils();
-                var path = fileUtils.getJDKRootPath("");
-
-                if (path.length) {
-
-                    var toolPrefs = ToolCore.toolEnvironment.toolPrefs;
-                    if (toolPrefs.jDKRootPath != path) {
-                        toolPrefs.jDKRootPath = path;
-                        toolPrefs.save();
-                    }
-
-                    this.refreshWidgets();
-
-                }
+                var path = fileUtils.findPath("Please choose the root folder of your JDK","");
+                if ( path.length > 0 )
+                    this.jdkRootEdit.text = path;
 
                 return true;
 
@@ -131,10 +103,27 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
 
                 this.refreshAndroidTargets();
 
-            }
+            }  else if (ev.target.id == "choose_and_auth") {
+                var fileUtils = new Editor.FileUtils();
+                var path = fileUtils.findPath( "Please choose the folder of your ant.properties", "");
+                if ( path.length > 0 )
+                    this.releaseNameEdit.text = path;                
+                return true;
 
-        }
+            }  else if (ev.target.id == "choose_icon") {
+                var fileUtils = new Editor.FileUtils();
+                var path = fileUtils.findPath("Please choose the folder with drawable folders","");
+                if ( path.length > 0 )
+                {
+                    this.iconNameEdit.text = path;
+                    this.updateIconButton();
+               }
+                return true;
+                
+           } 
 
+        }
+ 
         return false;
     }
 
@@ -164,26 +153,46 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
         });
 
     }
+    
+    updateIconButton() {
+    
+        var fileSystem = Atomic.getFileSystem();
 
-    refreshWidgets() {
+        if ( this.iconNameEdit.text.length > 0 ) {
+
+            let myicon = this.iconNameEdit.text + "/drawable-ldpi/icon.png";
+            if ( fileSystem.fileExists(myicon) ) {
+
+                this.iconImage.setImage( myicon );
+                return;
+            }
+        }
 
-        var sdkPathEdit = <Atomic.UIEditField>this.getWidget("sdk_path");
-        var antPathEdit = <Atomic.UIEditField>this.getWidget("ant_path");
-        var jdkRootEdit = <Atomic.UIEditField>this.getWidget("jdk_root");
+        let defaulticon = fileSystem.getProgramDir() + "Resources/ToolData/Deployment/Android/res/drawable-ldpi/icon.png";
+        this.iconImage.setImage( defaulticon );
+
+    }
+    
+
+    refreshWidgets() {
 
         var toolPrefs = ToolCore.toolEnvironment.toolPrefs;
 
-        sdkPathEdit.text = toolPrefs.androidSDKPath;
-        antPathEdit.text = toolPrefs.antPath;
-        jdkRootEdit.text = toolPrefs.jDKRootPath;
+        this.sdkPathEdit.text = toolPrefs.androidSDKPath;
+        this.antPathEdit.text = toolPrefs.antPath;
+        this.jdkRootEdit.text = toolPrefs.jDKRootPath;
+        this.releaseNameEdit.text = toolPrefs.releasePath;
+        this.releaseCheck.value = toolPrefs.releaseCheck;
 
         this.appNameEdit.text = this.settings.appName;
         this.packageNameEdit.text = this.settings.packageName;
         this.productNameEdit.text = this.settings.productName;
         this.companyNameEdit.text = this.settings.companyName;
+        this.iconNameEdit.text = this.settings.iconPath;
 
         this.sdkTargetSelect.text = this.settings.sDKVersion;
 
+        this.updateIconButton();
     }
 
     storeValues() {
@@ -192,17 +201,22 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
         this.settings.packageName = this.packageNameEdit.text;
         this.settings.productName = this.productNameEdit.text;
         this.settings.companyName = this.companyNameEdit.text;
-
-        if (this.antPathEdit.text.length && this.antPathEdit.text != ToolCore.toolEnvironment.toolPrefs.antPath)
-            ToolCore.toolEnvironment.toolPrefs.antPath = this.antPathEdit.text;
-
         this.settings.sDKVersion = this.sdkTargetSelect.text;
+        this.settings.iconPath = this.iconNameEdit.text;
+
+        ToolCore.toolEnvironment.toolPrefs.antPath = this.antPathEdit.text;
+        ToolCore.toolEnvironment.toolPrefs.androidSDKPath = this.sdkPathEdit.text;
+        ToolCore.toolEnvironment.toolPrefs.jDKRootPath = this.jdkRootEdit.text;
+        ToolCore.toolEnvironment.toolPrefs.releasePath = this.releaseNameEdit.text;
+        ToolCore.toolEnvironment.toolPrefs.releaseCheck = this.releaseCheck.value;
 
+        ToolCore.toolEnvironment.saveToolPrefs();
     }
 
     settings: ToolCore.AndroidBuildSettings;
     sdkTargetSource: Atomic.UISelectItemSource = new Atomic.UISelectItemSource();
     sdkTargetSelect: Atomic.UISelectDropdown;
+    sdkPathEdit: Atomic.UIEditField;
 
     jdkRootChooseButton: Atomic.UIButton;
     jdkRootEdit: Atomic.UIEditField;
@@ -213,8 +227,13 @@ class AndroidSettingsWidget extends Atomic.UIWidget implements BuildSettingsWind
     packageNameEdit: Atomic.UIEditField;
     productNameEdit: Atomic.UIEditField;
     companyNameEdit: Atomic.UIEditField;
-
-
+    
+    releaseNameEdit : Atomic.UIEditField;
+    releaseChooseButton : Atomic.UIButton;
+    releaseCheck : Atomic.UICheckBox;
+    iconNameEdit : Atomic.UIEditField; 
+    iconChooseButton : Atomic.UIButton;
+    iconImage : Atomic.UIImageWidget;
 }
 
 export = AndroidSettingsWidget;

+ 34 - 0
Script/ToolCore/build/BuildSettings.ts

@@ -37,4 +37,38 @@ class MacBuildSettings {
 
 }
 
+class WindowsBuildSettings {   
+    appName: string;
+    packageName: string;
+    companyName: string;
+    productName: string;
+}
+
+class WebBuildSettings {
+    appName: string;
+    packageName: string;
+    companyName: string;
+    productName: string;
+}
+
+class AndroidBuildSettings {
+    appName: string;
+    packageName: string;
+    companyName: string;
+    productName: string;
+    sDKVersion: string;
+    minSDKVersion: string;
+    activityName: string;
+    iconPath: string;
+}
+
+class IOSBuildSettings {
+    appName: string;
+    packageName: string;
+    companyName: string;
+    productName: string;
+    provisionFile: string;
+    appIDPrefix: string;
+}
+
 export = BuildSettings;

+ 43 - 48
Source/AtomicEditor/Utils/FileUtils.cpp

@@ -127,30 +127,6 @@ String FileUtils::GetBuildPath(const String& defaultPath)
 
 }
 
-String FileUtils::GetAndroidSDKPath(const String& defaultPath)
-{
-    String sdkPath;
-
-    nfdchar_t *outPath = NULL;
-
-    nfdresult_t result = NFD_ChooseDirectory( "Please choose the root folder of your Android SDK",
-                                defaultPath.Length() ? defaultPath.CString() : NULL,
-                                &outPath);
-
-    if (outPath && result == NFD_OKAY)
-    {
-        sdkPath = outPath;
-    }
-
-    if (outPath)
-        free(outPath);
-
-    GetSubsystem<Graphics>()->RaiseWindow();
-
-    return GetInternalPath(sdkPath);
-
-}
-
 String FileUtils::GetAntPath(const String& defaultPath)
 {
     String antPath;
@@ -180,30 +156,6 @@ String FileUtils::GetAntPath(const String& defaultPath)
     return GetInternalPath(antPath);
 }
 
-String FileUtils::GetJDKRootPath(const String& defaultPath)
-{
-    String jdkPath;
-
-    nfdchar_t *outPath = NULL;
-
-    nfdresult_t result = NFD_ChooseDirectory("Please choose the root folder of your JDK",
-        defaultPath.Length() ? defaultPath.CString() : NULL,
-        &outPath);
-
-    if (outPath && result == NFD_OKAY)
-    {
-        jdkPath = outPath;
-    }
-
-    if (outPath)
-        free(outPath);
-
-    GetSubsystem<Graphics>()->RaiseWindow();
-
-    return GetInternalPath(jdkPath);
-
-}
-
 String FileUtils::GetMobileProvisionPath()
 {
     nfdchar_t *outPath = NULL;
@@ -239,5 +191,48 @@ void FileUtils::RevealInFinder(const String& fullpath)
         fs->SystemOpen(GetPath(fullpath));
 }
 
+String FileUtils::FindPath(const String& title, const String& defaultPath)
+{
+    String resultPath;
+    nfdchar_t *outPath = NULL;
+
+    nfdresult_t result = NFD_ChooseDirectory(title.CString(),
+        defaultPath.Length() ? defaultPath.CString() : NULL,
+        &outPath);
+
+    if (outPath && result == NFD_OKAY)
+    {
+        resultPath = outPath;
+    }
+
+    if (outPath)
+        free(outPath);
+
+    GetSubsystem<Graphics>()->RaiseWindow();
+
+    return GetInternalPath(resultPath);
+}
+
+String FileUtils::FindFile (const String& filterlist, const String& defaultPath)
+{
+    String fullpath;
+    nfdchar_t *outPath = NULL;
+
+    nfdresult_t result = NFD_OpenDialog( filterlist.CString(),
+        defaultPath.Length() ? defaultPath.CString() : NULL,
+        &outPath);
+
+    if (outPath && result == NFD_OKAY)
+    {
+        fullpath = outPath;
+    }
+
+    GetSubsystem<Graphics>()->RaiseWindow();
+
+    if (outPath)
+        free(outPath);
+
+    return fullpath;
+}
 
 }

+ 3 - 3
Source/AtomicEditor/Utils/FileUtils.h

@@ -43,13 +43,13 @@ public:
     bool CreateDirs(const String& folder);
 
     String GetMobileProvisionPath();
-    String GetAndroidSDKPath(const String& defaultPath);
     String GetAntPath(const String& defaultPath);
-    String GetJDKRootPath(const String& defaultPath);
     String OpenProjectFileDialog();
     String NewProjectFileDialog();
     String GetBuildPath(const String& defaultPath);
-    void RevealInFinder(const String& fullpath);
+    void RevealInFinder(const String& fullpath);    
+    String FindPath (const String& title, const String& defaultPath );
+    String FindFile (const String& filterlist, const String& defaultPath );
 
 private:
 

+ 94 - 13
Source/ToolCore/Build/AndroidProjectGenerator.cpp

@@ -48,7 +48,7 @@ AndroidProjectGenerator::~AndroidProjectGenerator()
 
 }
 
-bool AndroidProjectGenerator::Generate()
+bool AndroidProjectGenerator::Generate( BuildBase *baseOps )
 {
     if (!GenerateAndroidManifest())
         return false;
@@ -56,7 +56,7 @@ bool AndroidProjectGenerator::Generate()
     if (!GenerateStringXML())
         return false;
 
-    if (!GenerateLocalProperties())
+    if (!GenerateLocalProperties(baseOps))
         return false;
 
     if (!GenerateProjectProperties())
@@ -65,6 +65,9 @@ bool AndroidProjectGenerator::Generate()
     if (!GenerateActivitySource())
         return false;
 
+    if (!CopyUserIcons(baseOps))
+        return false;
+
     return true;
 }
 
@@ -78,7 +81,7 @@ bool AndroidProjectGenerator::GenerateActivitySource()
 
     if (!packageName.Length())
     {
-        errorText_ = "Invalid Package Name";
+        errorText_ = "Invalid App Package name. The general naming convention is com.company.appname";
         return false;
     }
 
@@ -93,7 +96,7 @@ bool AndroidProjectGenerator::GenerateActivitySource()
 
     if (!dirs.exists())
     {
-        errorText_ = "Unable to create ";
+        errorText_ = "Project generator unable to create dirs " + path;
         return false;
     }
 
@@ -109,15 +112,17 @@ bool AndroidProjectGenerator::GenerateActivitySource()
     File file(context_, path + "/AtomicGameEngine.java", FILE_WRITE);
 
     if (!file.IsOpen())
+    {
+        errorText_ = "Project generator unable to open file " + path + "/AtomicGameEngine.java";
         return false;
-
+    }
     file.Write(source.CString(), source.Length());
 
     return true;
 
 }
 
-bool AndroidProjectGenerator::GenerateLocalProperties()
+bool AndroidProjectGenerator::GenerateLocalProperties( BuildBase *fileOps )
 {
     ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
     ToolPrefs* prefs = tenv->GetToolPrefs();
@@ -125,7 +130,7 @@ bool AndroidProjectGenerator::GenerateLocalProperties()
 
     if (!sdkPath.Length())
     {
-        errorText_ = "Invalid Android SDK Path";
+        errorText_ = "Invalid Android SDK Path, select the path in Build Settings.";
         return false;
     }
 
@@ -135,10 +140,35 @@ bool AndroidProjectGenerator::GenerateLocalProperties()
     File file(context_, buildPath_ + "/local.properties", FILE_WRITE);
 
     if (!file.IsOpen())
+    {
+        errorText_ = "Project generator unable to open file " + buildPath_ + "/local.properties ";
         return false;
-
+    }
+    
     file.Write(props.CString(), props.Length());
 
+
+    if ( prefs->GetReleaseCheck() > 0 ) // if release flag is set ...
+    {
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+        String Reldir = prefs->GetReleasePath();
+        if (!fileSystem->DirExists(Reldir))
+        {
+            errorText_ = "Invalid Release Path, select the path in Build Settings.";
+            return false;
+        }
+        
+        String antname = Reldir + "/ant.properties";
+        if ( !fileSystem->FileExists ( antname) ) 
+        {
+            errorText_ = "The file ant.properties not found in " + Reldir + ", unable to generate Release APK.";
+            return false;
+        }
+
+        if ( !fileOps->BuildCopyFile ( antname, buildPath_ + "/ant.properties" ))
+            return false;
+
+    }
     return true;
 
 }
@@ -153,7 +183,7 @@ bool AndroidProjectGenerator::GenerateProjectProperties()
 
     if (!apiString.Length())
     {
-        errorText_ = "Invalid Android API";
+        errorText_ = "Invalid Android API level, Press Refresh and select a valid level.";
         return false;
     }
 
@@ -164,8 +194,10 @@ bool AndroidProjectGenerator::GenerateProjectProperties()
     File file(context_, buildPath_ + "/project.properties", FILE_WRITE);
 
     if (!file.IsOpen())
+    {
+        errorText_ = "Project generator unable to open file project.properties in " + buildPath_;
         return false;
-
+    }
     file.Write(props.CString(), props.Length());
 
     return true;
@@ -183,7 +215,7 @@ bool AndroidProjectGenerator::GenerateStringXML()
 
     if (!appName.Length())
     {
-        errorText_ = "Invalid App Name";
+        errorText_ = "Invalid App Name, select the App Name in Build Settings.";
         return false;
     }
 
@@ -233,11 +265,11 @@ bool AndroidProjectGenerator::GenerateAndroidManifest()
 
     if (!package.Length())
     {
-        errorText_ = "Invalid Package Name";
+        errorText_ = "Invalid App Package name. The general naming convention is com.company.appname";
         return false;
     }
 
-    // TODO: from settings
+    // TODO: from settings -- should this be ProductName ?
     String activityName = "AtomicGameEngine";
     if (!activityName.Length())
     {
@@ -281,6 +313,55 @@ bool AndroidProjectGenerator::GenerateAndroidManifest()
 
 }
 
+bool AndroidProjectGenerator::CopyUserIcons( BuildBase *fileOps )
+{
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
+    Project* project = toolSystem->GetProject();
+    AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings();
+
+    String userIconPath = settings->GetIconPath();
+    if (!fileSystem->DirExists(userIconPath))               // dont do anything if there is no path defined.
+        return true;
+            
+    String userIconDir = userIconPath + "/drawable";        // 1st target dir 
+    String userIconFile = userIconDir + "/logo_large.png";  // 1st target file
+    String destDir = buildPath_ + "/res/drawable";          // where it should be in the build
+    if ( fileSystem->FileExists (userIconFile) )            // is there a file there?
+    {
+        if ( !fileOps->BuildCopyFile ( userIconFile, destDir + "/logo_large.png" ))
+            return false;
+    }
+
+    userIconDir = userIconPath + "/drawable-ldpi"; 
+    userIconFile = userIconDir + "/icon.png"; 
+    destDir = buildPath_ + "/res/drawable-ldpi";
+    if ( fileSystem->FileExists (userIconFile) )
+    {
+        if ( !fileOps->BuildCopyFile ( userIconFile, destDir + "/icon.png"))
+            return false;
+    } 
+
+    userIconDir = userIconPath + "/drawable-mdpi"; 
+    userIconFile = userIconDir + "/icon.png"; 
+    destDir = buildPath_ + "/res/drawable-mdpi";
+    {
+        if ( !fileOps->BuildCopyFile ( userIconFile, destDir + "/icon.png" ))
+            return false;
+    } 
+
+    userIconDir = userIconPath + "/drawable-hdpi"; 
+    userIconFile = userIconDir + "/icon.png"; 
+    destDir = buildPath_ + "/res/drawable-hdpi";
+    if ( fileSystem->FileExists (userIconFile) )
+    {
+        if ( !fileOps->BuildCopyFile ( userIconFile, destDir + "/icon.png" ))
+            return false;
+    }
+
+    return true;
+}
+
 }
 
 /*

+ 4 - 2
Source/ToolCore/Build/AndroidProjectGenerator.h

@@ -31,6 +31,7 @@ using namespace Atomic;
 
 namespace ToolCore
 {
+class BuildBase;
 
 class AndroidProjectGenerator : public Object
 {
@@ -44,7 +45,7 @@ public:
 
     void SetBuildPath(const String& buildpath) { buildPath_ = buildpath; }
 
-    bool Generate();
+    bool Generate( BuildBase * );
 
     const String& GetErrorText() { return errorText_; }
 
@@ -52,9 +53,10 @@ private:
 
     bool GenerateAndroidManifest();
     bool GenerateStringXML();
-    bool GenerateLocalProperties();
+    bool GenerateLocalProperties(BuildBase *);
     bool GenerateProjectProperties();
     bool GenerateActivitySource();
+    bool CopyUserIcons( BuildBase *);
 
     String buildPath_;
     String errorText_;

+ 43 - 21
Source/ToolCore/Build/BuildAndroid.cpp

@@ -82,7 +82,7 @@ void BuildAndroid::RunADBStartActivity()
     Subprocess* subprocess = subs->Launch(adbCommand, args, buildPath_);
     if (!subprocess)
     {
-        FailBuild("BuildFailed::RunStartActivity");
+        FailBuild("StartActivity operation did not launch successfully.");
         return;
     }
 
@@ -114,18 +114,23 @@ void BuildAndroid::HandleRunADBInstallComplete(StringHash eventType, VariantMap&
 
 void BuildAndroid::RunADBInstall()
 {
-
+    ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+    ToolPrefs* prefs = tenv->GetToolPrefs();
     SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
     String adbCommand = platformAndroid_->GetADBCommand();
 
-    Vector<String> args = String("install -r ./bin/Atomic-debug-unaligned.apk").Split(' ');
+    Vector<String> args;
+
+    if ( prefs->GetReleaseCheck() > 0 ) // install release apk
+        args = String("install -r ./bin/Atomic-release.apk").Split(' ');
+    else
+        args = String("install -r ./bin/Atomic-debug-unaligned.apk").Split(' ');
 
     currentBuildPhase_ = ADBInstall;
     Subprocess* subprocess = subs->Launch(adbCommand, args, buildPath_);
-
     if (!subprocess)
     {
-        FailBuild("BuildFailed::RunADBInstall");
+        FailBuild("APK Device Installation operation did not launch successfully.");
         return;
     }
 
@@ -209,7 +214,7 @@ void BuildAndroid::RunADBListDevices()
 
     if (!subprocess)
     {
-        FailBuild("BuildFailed::RunADBListDevices");
+        FailBuild("Android List Device operation did not launch successfully.");
         return;
     }
 
@@ -245,10 +250,16 @@ void BuildAndroid::RunAntDebug()
 
     Poco::Process::Env env;
 
+    String buildApk = "debug";  // the default
+
+    if ( tprefs->GetReleaseCheck() > 0 ) // create release apk
+        buildApk = "release";
+
+
 #ifdef ATOMIC_PLATFORM_OSX
     String antCommand = tprefs->GetAntPath();
     Vector<String> args;
-    args.Push("debug");
+    args.Push(buildApk);
 #endif
 #ifdef ATOMIC_PLATFORM_WINDOWS
     // C:\ProgramData\Oracle\Java\javapath;
@@ -259,7 +270,7 @@ void BuildAndroid::RunAntDebug()
     // ant is a batch file on windows, so have to run with cmd /c
     args.Push("/c");
     args.Push("\"" + antPath + "\"");
-    args.Push("debug");
+    args.Push(buildApk);
 #endif
 #ifdef ATOMIC_PLATFORM_LINUX 
 
@@ -275,10 +286,10 @@ void BuildAndroid::RunAntDebug()
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     if ( !fileSystem->FileExists ( antCommand) ) 
     {
-        FailBuild("BuildFailed ant program not installed");
+        FailBuild("The ant program can not be found, check the Ant Path in Build Settings.");
     }
     Vector<String> args;
-    args.Push("debug");
+    args.Push(buildApk);
 #endif
 
     currentBuildPhase_ = AntBuildDebug;
@@ -286,12 +297,12 @@ void BuildAndroid::RunAntDebug()
 
     if (!subprocess)
     {
-        FailBuild("BuildFailed::RunAntDebug");
+        FailBuild("The ant build operation did not launch successfully.");
         return;
     }
 
     VariantMap buildOutput;
-    buildOutput[BuildOutput::P_TEXT] = "<color #D4FB79>Starting Android Deployment</color>\n\n";
+    buildOutput[BuildOutput::P_TEXT] = "<color #D4FB79>Starting Android " + buildApk + " Deployment</color>\n\n";
     SendEvent(E_BUILDOUTPUT, buildOutput);
 
     SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildAndroid, HandleAntDebugComplete));
@@ -329,6 +340,9 @@ void BuildAndroid::Build(const String& buildPath)
     buildPath_ = AddTrailingSlash(buildPath) + GetBuildSubfolder();
 
     Initialize();
+ 
+    if (!BuildClean(buildPath_))
+        return;
 
     //generate manifest file
     String manifest;
@@ -340,16 +354,14 @@ void BuildAndroid::Build(const String& buildPath)
             manifest += ";";
     }
 
-    FileSystem* fileSystem = GetSubsystem<FileSystem>();
-    if (fileSystem->DirExists(buildPath_))
-        fileSystem->RemoveDir(buildPath_, true);
-
     String buildSourceDir = tenv->GetToolDataDir();
 
     String androidProject = buildSourceDir + "Deployment/Android";
 
     // Copy the base android project
-    fileSystem->CopyDir(androidProject, buildPath_);
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    if( !BuildCopyDir(androidProject, buildPath_))
+        return;
 
     Vector<String> defaultResourcePaths;
     GetDefaultResourcePaths(defaultResourcePaths);
@@ -357,21 +369,31 @@ void BuildAndroid::Build(const String& buildPath)
 
     for (unsigned i = 0; i < defaultResourcePaths.Size(); i++)
     {
-        fileSystem->CopyDir(defaultResourcePaths[i], buildPath_ + "/assets/" + GetFileName(RemoveTrailingSlash(defaultResourcePaths[i])));
+        if ( !BuildCopyDir(defaultResourcePaths[i], buildPath_ + "/assets/" + GetFileName(RemoveTrailingSlash(defaultResourcePaths[i]))))
+            return;
     }
 
-    fileSystem->CopyDir(project->GetProjectPath() + "Cache/", buildPath_ + "/assets/Cache");
-    fileSystem->CopyDir(projectResources, buildPath_ + "/assets/AtomicResources");
+    if( !BuildCopyDir(project->GetProjectPath() + "Cache/", buildPath_ + "/assets/Cache"))
+        return;
+    if( !BuildCopyDir(projectResources, buildPath_ + "/assets/AtomicResources"))
+        return;
 
     // write the manifest
     SharedPtr<File> mfile(new File(context_, buildPath_ + "/assets/AtomicManifest", FILE_WRITE));
     mfile->WriteString(manifest);
     mfile->Close();
 
+    //check for Deployment/Android/libs/armeabi-v7a/libAtomicPlayer.so
+    if ( !fileSystem->FileExists(androidProject + "/libs/armeabi-v7a/libAtomicPlayer.so")  )
+    {
+        FailBuild( " the file libAtomicPlayer.so is not found. This is required for APK generation." );
+        return;
+    }
+
     AndroidProjectGenerator gen(context_);
     gen.SetBuildPath(buildPath_);
 
-    if (!gen.Generate())
+    if (!gen.Generate( static_cast<BuildBase*>(this)))
     {
         FailBuild(gen.GetErrorText());
         return;

+ 30 - 0
Source/ToolCore/Build/BuildBase.cpp

@@ -19,6 +19,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
+#include <Poco/File.h>
 
 #include <Atomic/IO/Log.h>
 #include <Atomic/IO/FileSystem.h>
@@ -164,6 +165,27 @@ bool BuildBase::BuildCopyFile(const String& srcFileName, const String& destFileN
     return true;
 }
 
+bool BuildBase::BuildCopyDir(const String& srcDir, const String& destDir)
+{
+    if (buildFailed_)
+    {
+        LOGERRORF("BuildBase::BuildCopyDir - Attempt to copy directory of failed build, %s", srcDir.CString());
+        return false;
+    }
+
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+    bool result = fileSystem->CopyDir(srcDir, destDir);
+
+    if (!result)
+    {
+        FailBuild(ToString("BuildBase::BuildCopyDir: Unable to copy dir %s -> %s", srcDir.CString(), destDir.CString()));
+        return false;
+    }
+
+    return true;
+}
+
 bool BuildBase::CheckIncludeResourceFile(const String & resourceDir, const String & fileName)
 {
     return (GetExtension(fileName) != ".psd");
@@ -181,7 +203,15 @@ bool BuildBase::BuildRemoveDirectory(const String& path)
     if (!fileSystem->DirExists(path))
         return true;
 
+#ifdef ATOMIC_PLATFORM_LINUX
+    bool result = true;   // fileSystem->RemoveDir(path, true); crashes on linux
+    Poco::File dirs(buildPath_.CString());
+    dirs.remove(true);
+    if (fileSystem->DirExists(buildPath_))
+        result = false;
+#else
     bool result = fileSystem->RemoveDir(path, true);
+#endif
 
     if (!result)
     {

+ 4 - 0
Source/ToolCore/Build/BuildBase.h

@@ -38,6 +38,8 @@ class BuildBase : public Object
 {
     OBJECT(BuildBase);
 
+friend class AndroidProjectGenerator;
+
 public:
 
     BuildBase(Context* context, Project* project, PlatformID platform);
@@ -70,6 +72,8 @@ protected:
     bool BuildRemoveDirectory(const String& path);
     bool BuildCreateDirectory(const String& path);
     bool BuildCopyFile(const String& srcFileName, const String& destFileName);
+    bool BuildCopyDir(const String& srcDir, const String& destDir);
+
     virtual bool CheckIncludeResourceFile(const String& resourceDir, const String& fileName);
 
     void GenerateResourcePackage(const String& resourcePackagePath);

+ 13 - 9
Source/ToolCore/Build/BuildIOS.cpp

@@ -311,27 +311,31 @@ void BuildIOS::Build(const String& buildPath)
 
     Initialize();
 
-    if (fileSystem->DirExists(buildPath_))
-        fileSystem->RemoveDir(buildPath_, true);
+    if (!BuildClean(buildPath_))
+        return;
 
     String buildSourceDir = tenv->GetToolDataDir();
 
     String buildAppSourceDir = buildSourceDir + "Deployment/IOS/AtomicPlayer.app";
 
-    fileSystem->CreateDir(buildPath_);
+    if (!BuildCreateDirectory(buildPath_))
+        return;
 
     String buildDestDist = buildPath_ + "/AtomicPlayer.app";
 
-    fileSystem->CreateDir(buildDestDist);
+    if (!BuildCreateDirectory(buildDestDist))
+        return;
 
     String resourcePackagePath = buildDestDist + "/AtomicResources" + PAK_EXTENSION;
     GenerateResourcePackage(resourcePackagePath);
 
-    fileSystem->Copy(buildAppSourceDir + "/AtomicPlayer", buildDestDist + "/AtomicPlayer");
-    fileSystem->Copy(buildAppSourceDir + "/PkgInfo", buildDestDist + "/PkgInfo");
-
-    fileSystem->Copy(iosSettings->GetProvisionFile(), buildDestDist + "/embedded.mobileprovision");
-
+    if (!BuildCopyFile(buildAppSourceDir + "/AtomicPlayer", buildDestDist + "/AtomicPlayer"))
+        return;
+    if (!BuildCopyFile(buildAppSourceDir + "/PkgInfo", buildDestDist + "/PkgInfo"))
+        return;
+    if (!BuildCopyFile(iosSettings->GetProvisionFile(), buildDestDist + "/embedded.mobileprovision"))
+        return;
+        
     String entitlements = GenerateEntitlements();
     String plist = GenerateInfoPlist();
 

+ 1 - 1
Source/ToolCore/Build/BuildMac.cpp

@@ -102,7 +102,7 @@ void BuildMac::Build(const String& buildPath)
 
     BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
 
-    if (!BuildRemoveDirectory(buildPath_))
+    if (!BuildClean(buildPath_))
         return;
 
     FileSystem* fileSystem = GetSubsystem<FileSystem>();

+ 16 - 10
Source/ToolCore/Build/BuildWeb.cpp

@@ -79,25 +79,31 @@ void BuildWeb::Build(const String& buildPath)
 
     Initialize();
 
-    FileSystem* fileSystem = GetSubsystem<FileSystem>();
-    if (fileSystem->DirExists(buildPath_))
-        fileSystem->RemoveDir(buildPath_, true);
+    if (!BuildClean(buildPath_))
+        return;
 
     String dataPath = tenv->GetToolDataDir();
 
     String buildSourceDir  = dataPath + "Deployment/Web";
 
-    fileSystem->CreateDir(buildPath_);
+    if (!BuildCreateDirectory(buildPath_))
+        return;
 
     String resourcePackagePath = buildPath_ + "/AtomicResources.data";
     GenerateResourcePackage(resourcePackagePath);
 
-    fileSystem->Copy(buildSourceDir + "/AtomicPlayer.html", buildPath_ + "/AtomicPlayer.html");
-    fileSystem->Copy(buildSourceDir + "/AtomicPlayer.html.mem", buildPath_ + "/AtomicPlayer.html.mem");
-    fileSystem->Copy(buildSourceDir + "/AtomicPlayer.js", buildPath_ + "/AtomicPlayer.js");
-    fileSystem->Copy(buildSourceDir + "/AtomicLoader.js", buildPath_ + "/AtomicLoader.js");
-    fileSystem->Copy(buildSourceDir + "/index.html", buildPath_ + "/index.html");
-    fileSystem->Copy(buildSourceDir + "/Atomic_Logo_Header.png", buildPath_ + "/Atomic_Logo_Header.png");
+    if (!BuildCopyFile(buildSourceDir + "/AtomicPlayer.html", buildPath_ + "/AtomicPlayer.html"))
+        return;
+    if (!BuildCopyFile(buildSourceDir + "/AtomicPlayer.html.mem", buildPath_ + "/AtomicPlayer.html.mem"))
+        return;
+    if (!BuildCopyFile(buildSourceDir + "/AtomicPlayer.js", buildPath_ + "/AtomicPlayer.js"))
+        return;
+    if (!BuildCopyFile(buildSourceDir + "/AtomicLoader.js", buildPath_ + "/AtomicLoader.js"))
+        return;
+    if (!BuildCopyFile(buildSourceDir + "/index.html", buildPath_ + "/index.html"))
+        return;
+    if (!BuildCopyFile(buildSourceDir + "/Atomic_Logo_Header.png", buildPath_ + "/Atomic_Logo_Header.png"))
+        return;
 
     File file(context_, buildSourceDir + "/AtomicResources_js.template", FILE_READ);
     unsigned size = file.GetSize();

+ 1 - 1
Source/ToolCore/Build/BuildWindows.cpp

@@ -86,7 +86,7 @@ bool BuildWindows::CheckIncludeResourceFile(const String& resourceDir, const Str
         }
     }
     // #623 END TODO
-    
+
     return BuildBase::CheckIncludeResourceFile(resourceDir, fileName);
 }
 

+ 2 - 0
Source/ToolCore/Project/ProjectBuildSettings.cpp

@@ -123,6 +123,7 @@ void AndroidBuildSettings::Write(JSONValue& parent)
     json.Set("targetSDKVersion", targetSDKVersion_);
     json.Set("minSDKVersion", minSDKVersion_);
     json.Set("activityName", activityName_);
+    json.Set("iconPath", iconPath_);
 
     parent.Set("AndroidBuildSettings", json);
 
@@ -144,6 +145,7 @@ void AndroidBuildSettings::Read(JSONValue& parent)
     targetSDKVersion_ = json.Get("targetSDKVersion").GetString();
     minSDKVersion_ = json.Get("minSDKVersion").GetString();
     activityName_ = json.Get("activityName").GetString();
+    iconPath_ = json.Get("iconPath").GetString();
 
 }
 

+ 3 - 0
Source/ToolCore/Project/ProjectBuildSettings.h

@@ -133,6 +133,7 @@ public:
     const String& GetSDKVersion() const { return targetSDKVersion_; }
     const String& GetMinSDKVersion() const { return minSDKVersion_; }
     const String& GetActivityName() const { return activityName_; }
+    const String& GetIconPath() const { return iconPath_; }
 
     void SetAppName(const String& name) { appName_ = name; }
     void SetPackageName(const String& packageName) { packageName_ = packageName; }
@@ -142,6 +143,7 @@ public:
     void SetSDKVersion(const String& value) { targetSDKVersion_ = value; }
     void SetMinSDKVersion(const String& value) { minSDKVersion_ = value; }
     void SetActivityName(const String& value) { activityName_ = value; }
+    void SetIconPath(const String& value) { iconPath_ = value; }
 
     void Write(JSONValue& parent);
     void Read(JSONValue& parent);
@@ -156,6 +158,7 @@ private:
     String targetSDKVersion_;
     String minSDKVersion_;
     String activityName_;
+    String iconPath_;
 };
 
 class IOSBuildSettings : public RefCounted

+ 20 - 13
Source/ToolCore/ToolPrefs.cpp

@@ -31,7 +31,12 @@
 namespace ToolCore
 {
 
-ToolPrefs::ToolPrefs(Context* context) : Object(context)
+ToolPrefs::ToolPrefs(Context* context) : Object(context),
+    androidSDKPath_(),
+    jdkRootPath_(),
+    antPath_(),
+    releasePath_(),
+    releaseCheck_(false)
 {
 
 }
@@ -94,6 +99,8 @@ void ToolPrefs::Load()
         androidSDKPath_ = androidRoot.Get("androidSDKPath").GetString();
         jdkRootPath_ = androidRoot.Get("jdkRootPath").GetString();
         antPath_ = androidRoot.Get("antPath").GetString();
+        releasePath_ = androidRoot.Get("releasePath").GetString();
+        releaseCheck_ = androidRoot.Get("releaseCheck").GetInt();
     }
 
 }
@@ -101,21 +108,21 @@ void ToolPrefs::Load()
 void ToolPrefs::Save()
 {
     String path = GetPrefsPath();
-
+ 
     SharedPtr<JSONFile> jsonFile(new JSONFile(context_));
-
-    JSONValue root = jsonFile->GetRoot();
+    JSONValue& root = jsonFile->GetRoot();
+    root.Clear();
+    
+    JSONValue androidRoot; 
+    androidRoot["androidSDKPath"] = androidSDKPath_;
+    androidRoot["jdkRootPath"] = jdkRootPath_;
+    androidRoot["antPath"] = antPath_;
+    androidRoot["releasePath"] = releasePath_;
+    androidRoot["releaseCheck"] = releaseCheck_;
+   root["android"] = androidRoot;
 
     SharedPtr<File> file(new File(context_, path, FILE_WRITE));
-
-    JSONValue androidRoot;
-    androidRoot.Set("androidSDKPath", androidSDKPath_);
-    androidRoot.Set("jdkRootPath", jdkRootPath_);
-    androidRoot.Set("antPath", antPath_);
-    root.Set("android", androidRoot);
-
-    jsonFile->Save(*file, String("   "));
-
+    jsonFile->Save(*file, "   ");
     file->Close();
 
 }

+ 6 - 1
Source/ToolCore/ToolPrefs.h

@@ -41,10 +41,14 @@ public:
     const String& GetAndroidSDKPath() { return androidSDKPath_; }
     const String& GetJDKRootPath() { return jdkRootPath_; }
     const String& GetAntPath();
+    const String& GetReleasePath() { return releasePath_; }
+    const int GetReleaseCheck() { return releaseCheck_; }
 
     void SetAndroidSDKPath(const String& path) { androidSDKPath_ = path; }
     void SetJDKRootPath(const String& path) { jdkRootPath_ = path; }
     void SetAntPath(const String& path) { antPath_ = path; }
+    void SetReleasePath(const String& path) { releasePath_ = path; }
+    void SetReleaseCheck(const int value) { releaseCheck_ = value; }
 
     String GetPrefsPath();
     void Load();
@@ -55,7 +59,8 @@ private:
     String androidSDKPath_;
     String jdkRootPath_;
     String antPath_;
-
+    String releasePath_;
+    int releaseCheck_;
 };
 
 }