You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
18 KiB
520 lines
18 KiB
/*
|
|
* 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<AndroidTarget> getTargetList() {
|
|
ArrayList<AndroidTarget> list = new ArrayList<AndroidTarget>();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|