// // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include #include #include #include "../ToolSystem.h" #include "../ToolEnvironment.h" #include "../Project/Project.h" #include "../Project/ProjectBuildSettings.h" #include "BuildMac.h" #include "BuildSystem.h" #include "AndroidProjectGenerator.h" namespace ToolCore { AndroidProjectGenerator::AndroidProjectGenerator(Context* context, BuildBase* buildBase) : Object(context), buildBase_(buildBase) { } AndroidProjectGenerator::~AndroidProjectGenerator() { } bool AndroidProjectGenerator::Generate() { if (!GenerateAndroidManifest()) return false; if (!GenerateStringXML()) return false; if (!GenerateLocalProperties()) return false; if (!GenerateProjectProperties()) return false; if (!GenerateActivitySource()) return false; if (!CopyUserIcons()) return false; if (!CopyDebugGdbserver()) return false; return true; } bool AndroidProjectGenerator::GenerateActivitySource() { ToolSystem* toolSystem = GetSubsystem(); Project* project = toolSystem->GetProject(); AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings(); String packageName = settings->GetPackageName(); if (!packageName.Length()) { errorText_ = "Invalid App Package name. The general naming convention is com.company.appname"; return false; } Vector elements = settings->GetPackageName().Split('.'); String path; path.Join(elements, "/"); path = buildPath_ + "/src/" + path; Poco::File dirs(path.CString()); dirs.createDirectories(); if (!dirs.exists()) { errorText_ = "Project generator unable to create dirs " + path; return false; } String source; source.AppendWithFormat("package %s;\n", packageName.CString()); source += "import android.os.Bundle;\n"; source += "import android.view.ViewGroup;\n"; source += "import android.widget.FrameLayout;\n"; source += "import android.widget.RelativeLayout;\n"; source += "import org.libsdl.app.SDLActivity;\n"; source += "import com.atomicgameengine.player.AtomicWebView;\n"; source += "public class AtomicGameEngine extends SDLActivity {\n"; source += " @Override\n"; source += " protected void onCreate(Bundle savedInstanceState) {\n"; source += " super.onCreate(savedInstanceState);\n"; source += " ViewGroup.LayoutParams framelayout_params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n"; source += " FrameLayout framelayout = new FrameLayout(this);\n"; source += " framelayout.setLayoutParams(framelayout_params);\n"; source += " ViewGroup webViewGroup = new RelativeLayout(this);\n"; source += " framelayout.addView(webViewGroup);\n"; source += " mLayout.addView(framelayout);\n"; source += " AtomicWebView.setRootLayout(webViewGroup);\n"; source += " }\n"; source += "}\n"; 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( ) { ToolEnvironment* tenv = GetSubsystem(); ToolPrefs* prefs = tenv->GetToolPrefs(); String sdkPath = prefs->GetAndroidSDKPath(); if (!sdkPath.Length()) { errorText_ = "Invalid Android SDK Path, select the path in Build Settings."; return false; } String props; props.AppendWithFormat("sdk.dir=%s", sdkPath.CString()); 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() > 2 ) // if release index is set ... { FileSystem* fileSystem = GetSubsystem(); 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 ( !buildBase_->BuildCopyFile ( antname, buildPath_ + "/ant.properties" )) return false; } return true; } bool AndroidProjectGenerator::GenerateProjectProperties() { ToolSystem* toolSystem = GetSubsystem(); Project* project = toolSystem->GetProject(); AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings(); String apiString = settings->GetSDKVersion(); if (!apiString.Length()) { errorText_ = "Invalid Android API level, Press Refresh and select a valid level."; return false; } String props; props.AppendWithFormat("target=%s", apiString.CString()); 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; } bool AndroidProjectGenerator::GenerateStringXML() { FileSystem* fs = GetSubsystem(); ToolSystem* toolSystem = GetSubsystem(); Project* project = toolSystem->GetProject(); AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings(); String appName = settings->GetAppName(); if (!appName.Length()) { errorText_ = "Invalid App Name, select the App Name in Build Settings."; return false; } String strings = "\n"; strings += "\n"; strings.AppendWithFormat("%s\n", appName.CString()); strings += "\n"; // Create res/values if it doesn't exist if (!fs->DirExists(buildPath_ + "/res/values")) { fs->CreateDirsRecursive(buildPath_ + "/res/values"); } // Check that we successfully created it if (!fs->DirExists(buildPath_ + "/res/values")) { errorText_ = "Unable to create directory: " + buildPath_ + "/res/values"; return false; } File file(context_, buildPath_ + "/res/values/strings.xml", FILE_WRITE); if (!file.IsOpen()) { errorText_ = "Unable to write: " + buildPath_ + "/res/values/strings.xml"; return false; } file.Write(strings.CString(), strings.Length()); return true; } bool AndroidProjectGenerator::GenerateAndroidManifest() { ToolSystem* toolSystem = GetSubsystem(); Project* project = toolSystem->GetProject(); AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings(); String package = settings->GetPackageName(); if (!package.Length()) { errorText_ = "Invalid App Package name. The general naming convention is com.company.appname"; return false; } // TODO: from settings -- should this be ProductName ? String activityName = "AtomicGameEngine"; if (!activityName.Length()) { errorText_ = "Invalid Activity Name"; return false; } String manifest = "\n"; manifest += "\n"; manifest += "android:installLocation=\"auto\">\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest.AppendWithFormat("\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; manifest += "\n"; File file(context_, buildPath_ + "/AndroidManifest.xml", FILE_WRITE); if (!file.IsOpen()) return false; file.Write(manifest.CString(), manifest.Length()); return true; } bool AndroidProjectGenerator::CopyUserIcons() { FileSystem* fileSystem = GetSubsystem(); ToolSystem* toolSystem = GetSubsystem(); 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 ( !buildBase_->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 ( !buildBase_->BuildCopyFile ( userIconFile, destDir + "/icon.png")) return false; } userIconDir = userIconPath + "/drawable-mdpi"; userIconFile = userIconDir + "/icon.png"; destDir = buildPath_ + "/res/drawable-mdpi"; { if ( !buildBase_->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 ( !buildBase_->BuildCopyFile ( userIconFile, destDir + "/icon.png" )) return false; } return true; } bool AndroidProjectGenerator::CopyDebugGdbserver() { ToolEnvironment* tenv = GetSubsystem(); ToolPrefs* prefs = tenv->GetToolPrefs(); // include gdbserver in APK if ( prefs->GetReleaseCheck() == 1 || prefs->GetReleaseCheck() == 2 ) { String ndkPath = prefs->GetNdkPath(); if (ndkPath.Empty()) { errorText_ = "NDK path not entered, this is required to add gdbserver to APK"; return false; } FileSystem* fileSystem = GetSubsystem(); if (!fileSystem->DirExists(ndkPath)) { errorText_ = "Invalid NDK Path, can not add gdbserver to APK."; return false; } // copy gdbserver file String gsstring = ndkPath + "/prebuilt/android-arm/gdbserver/gdbserver"; // assume arm type abi String destDir = buildPath_ + "/libs/armeabi-v7a"; // assume armeabi-v7a abi type if ( !fileSystem->FileExists (gsstring) ) { errorText_ = "gdbserver not found as " + gsstring; return false; } if ( prefs->GetReleaseCheck() == 1 ) // Debug Source with gdbserver { if ( !buildBase_->BuildCopyFile ( gsstring, destDir + "/gdbserver")) return false; } else if ( prefs->GetReleaseCheck() == 2 ) // Debug Source with libgdbserver.so { if ( !buildBase_->BuildCopyFile ( gsstring, destDir + "/libgdbserver.so")) return false; } } return true; } }