/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.jme3.gde.android; import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.project.Project; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.NotifyDescriptor.Message; import org.openide.filesystems.FileChooserBuilder; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; import org.openide.util.NbPreferences; import org.openide.util.Utilities; import org.openide.xml.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * * @author normenhansen */ public class AndroidSdkTool { /** * Starts the Android target configuration utility. */ public static void startAndroidTool() { startAndroidTool(false); } public static void startAndroidTool(boolean modal) { final String path = getAndroidToolPath(); if (path == null) { return; } Thread thread = new Thread(new Runnable() { @Override public void run() { String[] command = new String[]{path}; ProcessBuilder builder = new ProcessBuilder(command); try { Process proc = builder.start(); OutputReader outReader = new OutputReader(proc.getInputStream()); OutputReader errReader = new OutputReader(proc.getErrorStream()); outReader.start(); errReader.start(); proc.waitFor(); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } }); if (modal) { thread.run(); } else { thread.start(); } } /** * Returns a FileObject for the android SDK folder, null if none is specified * @return */ public static FileObject getSdkFolder() { String path = getSdkPath(); if (path == null) { return null; } FileObject fileObj = FileUtil.toFileObject(new File(path)); if (fileObj == null) { return null; } return fileObj; } /** * Returns a String with the path to the SDK or null if none is specified. * @return */ public static String getSdkPath() { String path = NbPreferences.forModule(AndroidSdkTool.class).get("sdk_path", null); if (path == null) { FileChooserBuilder builder = new FileChooserBuilder(AndroidSdkTool.class); builder.setTitle("Please select Android SDK Folder"); builder.setDirectoriesOnly(true); File file = builder.showOpenDialog(); if (file != null) { FileObject folder = FileUtil.toFileObject(file); if (folder.getFileObject("tools") == null) { Message msg = new NotifyDescriptor.Message( "Not a valid SDK folder!", NotifyDescriptor.ERROR_MESSAGE); DialogDisplayer.getDefault().notifyLater(msg); } else { String name = file.getPath(); NbPreferences.forModule(AndroidSdkTool.class).put("sdk_path", name); return name; } } } else { return path; } return null; } /** * Returns a string with the path to the android tool, specific for platform (.exe for windows) * @return */ public static String getAndroidToolPath() { FileObject executable = null; FileObject folder = getSdkFolder(); if (folder == null) { return null; } if (Utilities.isWindows()) { executable = folder.getFileObject("tools/android.bat"); } else { executable = folder.getFileObject("tools/android"); } if (executable != null) { return FileUtil.toFile(executable).getPath(); } else { return null; } } /** * Gets a list of android targets registered in the SDK * @return */ public static List getTargetList() { ArrayList list = new ArrayList(); final String path = getAndroidToolPath(); if (path == null) { return list; } String[] command = new String[]{path, "list", "targets"}; ProcessBuilder builder = new ProcessBuilder(command); try { Process proc = builder.start(); ListReader outReader = new ListReader(proc.getInputStream(), list); OutputReader errReader = new OutputReader(proc.getErrorStream()); outReader.start(); errReader.start(); proc.waitFor(); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } return list; } //TODO: check mainJmeClass public static void checkProject(Project project, String target, String name, String activity, String packag, String mainJmeClass) { final String path = getAndroidToolPath(); if (path == null) { return; } FileObject folder = project.getProjectDirectory().getFileObject("mobile"); if (folder == null) { try { folder = project.getProjectDirectory().createFolder("mobile"); createProject(project, target, name, activity, packag, mainJmeClass); } catch (IOException ex) { Exceptions.printStackTrace(ex); return; } } else { updateProject(project, target, name); } } public static void createProject(Project project, String target, String name, String activity, String packag, String mainJmeClass) { final String path = getAndroidToolPath(); if (path == null) { return; } FileObject folder = project.getProjectDirectory().getFileObject("mobile"); if (folder == null) { try { folder = project.getProjectDirectory().createFolder("mobile"); } catch (IOException ex) { Exceptions.printStackTrace(ex); return; } } String[] command = new String[]{path, "create", "project", "--target", target, "--name", name, "--path", FileUtil.toFile(folder).getPath(), "--activity", activity, "--package", packag}; ProcessBuilder builder = new ProcessBuilder(command); FileLock lock = null; try { Process proc = builder.start(); OutputReader outReader = new OutputReader(proc.getInputStream()); OutputReader errReader = new OutputReader(proc.getErrorStream()); outReader.start(); errReader.start(); proc.waitFor(); folder.refresh(); String mainActName = "mobile/src/" + packag.replaceAll("\\.", "/") + "/MainActivity.java"; FileObject mainAct = project.getProjectDirectory().getFileObject(mainActName); if (mainAct != null) { lock = mainAct.lock(); OutputStreamWriter out = new OutputStreamWriter(new BufferedOutputStream(mainAct.getOutputStream(lock))); out.write(mainActivityString(mainJmeClass, packag)); out.close(); lock.releaseLock(); } else { Logger.getLogger(AndroidSdkTool.class.getName()).log(Level.WARNING, "Cannot find {0}", mainActName); } } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { if (lock != null) { lock.releaseLock(); } Exceptions.printStackTrace(ex); } updateAndroidManifest(project); updateAndroidApplicationName(project, name); } public static void updateProject(Project project, String target, String name) { final String path = getAndroidToolPath(); if (path == null) { return; } FileObject folder = project.getProjectDirectory().getFileObject("mobile"); if (folder == null) { return; } String[] command = new String[]{path, "update", "project", "--target", target, "--name", name, "--path", FileUtil.toFile(folder).getPath()}; ProcessBuilder builder = new ProcessBuilder(command); try { Process proc = builder.start(); OutputReader outReader = new OutputReader(proc.getInputStream()); OutputReader errReader = new OutputReader(proc.getErrorStream()); outReader.start(); errReader.start(); proc.waitFor(); folder.refresh(); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } updateAndroidApplicationName(project, name); } private static void updateAndroidManifest(Project project) { FileObject manifest = project.getProjectDirectory().getFileObject("mobile/AndroidManifest.xml"); if (manifest == null) { Logger.getLogger(AndroidSdkTool.class.getName()).log(Level.WARNING, "Could not find AndroidManifest.xml"); return; } InputStream in = null; FileLock lock = null; OutputStream out = null; try { in = manifest.getInputStream(); Document configuration = XMLUtil.parse(new InputSource(in), false, false, null, null); in.close(); in = null; boolean changed = false; Element sdkElement = XmlHelper.findChildElement(configuration.getDocumentElement(), "uses-sdk"); if (sdkElement == null) { sdkElement = configuration.createElement("uses-sdk"); configuration.getDocumentElement().appendChild(sdkElement); changed = true; } if (!"8".equals(sdkElement.getAttribute("android:minSdkVersion"))) { sdkElement.setAttribute("android:minSdkVersion", "8"); changed = true; } Element screensElement = XmlHelper.findChildElement(configuration.getDocumentElement(), "supports-screens"); if (screensElement == null) { screensElement = configuration.createElement("supports-screens"); screensElement.setAttribute("android:anyDensity", "true"); // screensElement.setAttribute("android:xlargeScreens", "true"); screensElement.setAttribute("android:largeScreens", "true"); screensElement.setAttribute("android:smallScreens", "true"); screensElement.setAttribute("android:normalScreens", "true"); configuration.getDocumentElement().appendChild(screensElement); changed = true; } if (changed) { lock = manifest.lock(); out = manifest.getOutputStream(lock); XMLUtil.write(configuration, out, "UTF-8"); out.close(); out = null; lock.releaseLock(); lock = null; } } catch (SAXException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } finally { if (lock != null) { lock.releaseLock(); } try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ex1) { Exceptions.printStackTrace(ex1); } } } private static void updateAndroidApplicationName(Project project, String name) { FileObject manifest = project.getProjectDirectory().getFileObject("mobile/res/values/strings.xml"); if (manifest == null) { return; } InputStream in = null; FileLock lock = null; OutputStream out = null; try { in = manifest.getInputStream(); Document configuration = XMLUtil.parse(new InputSource(in), false, false, null, null); in.close(); in = null; Element sdkElement = XmlHelper.findChildElementWithAttribute(configuration.getDocumentElement(), "string", "name", "app_name"); if (sdkElement == null) { sdkElement = configuration.createElement("string"); sdkElement.setAttribute("name", "app_name"); configuration.getDocumentElement().appendChild(sdkElement); } if (!sdkElement.getTextContent().trim().equals(name)) { sdkElement.setTextContent(name); lock = manifest.lock(); out = manifest.getOutputStream(lock); XMLUtil.write(configuration, out, "UTF-8"); out.close(); out = null; lock.releaseLock(); lock = null; } } catch (SAXException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } finally { if (lock != null) { lock.releaseLock(); } try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ex1) { Exceptions.printStackTrace(ex1); } } } private static String mainActivityString(String mainClass, String packag) { String str = "package " + packag + ";\n" + " \n" + "import com.jme3.app.AndroidHarness;\n" + "import android.content.pm.ActivityInfo;\n" + "import com.jme3.system.android.AndroidConfigChooser.ConfigType;\n" + " \n" + "public class MainActivity extends AndroidHarness{\n" + " \n" + " /*\n" + " * Note that you can ignore the errors displayed in this file,\n" + " * the android project will build regardless.\n" + " * Install the 'Android' plugin under Tools->Plugins->Available Plugins\n" + " * to get error checks and code completion for the Android project files.\n" + " */\n" + " \n" + " public MainActivity(){\n" + " // Set the application class to run\n" + " appClass = \"" + mainClass + "\";\n" + " // Try ConfigType.FASTEST; or ConfigType.LEGACY if you have problems\n" + " eglConfigType = ConfigType.BEST;\n" + " // Exit Dialog title & message\n" + " exitDialogTitle = \"Exit?\";\n" + " exitDialogMessage = \"Press Yes\";\n" + " // Enable verbose logging\n" + " eglConfigVerboseLogging = false;\n" + " // Choose screen orientation\n" + " screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\n" + " // Invert the MouseEvents X (default = true)\n" + " mouseEventsInvertX = true;\n" + " // Invert the MouseEvents Y (default = true)\n" + " mouseEventsInvertY = true;\n" + " }\n" + " \n" + "}\n"; return str; } public static class AndroidTarget { private int id; private String name; private String title; private String platform; private int apiLevel; private int revision; private String skins; public AndroidTarget() { } public AndroidTarget(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPlatform() { return platform; } public void setPlatform(String platform) { this.platform = platform; } public int getApiLevel() { return apiLevel; } public void setApiLevel(int apiLevel) { this.apiLevel = apiLevel; } public int getRevision() { return revision; } public void setRevision(int revision) { this.revision = revision; } public String getSkins() { return skins; } public void setSkins(String skins) { this.skins = skins; } @Override public String toString() { return getTitle(); } @Override public boolean equals(Object obj) { if (obj instanceof String && getName() != null) { return getName().equals(obj); } else { return super.equals(obj); } } } }