Skip to content

Support for ARM system images #371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 99 additions & 47 deletions src/processing/mode/android/AVD.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

import processing.app.Base;
import processing.app.Platform;
import processing.app.Preferences;
import processing.app.exec.LineProcessor;
import processing.app.exec.StreamPump;
import processing.core.PApplet;

import java.awt.Frame;
Expand Down Expand Up @@ -74,12 +77,12 @@ public class AVD {
protected String name;

/** "system-images;android-25;google_apis;x86" */
protected String sdkId;

protected ArrayList<String> watchImages;
protected ArrayList<String> phoneImages;

protected String device;
protected String skin;

static boolean invalidPackage;
static ArrayList<String> avdList;
static ArrayList<String> badList;
// static ArrayList<String> skinList;
Expand All @@ -89,25 +92,23 @@ public class AVD {
/** Default virtual device used by Processing. */
static public final AVD mobileAVD =
new AVD("processing-phone",
"system-images;" + AndroidBuild.TARGET_PLATFORM + ";" +
SysImageDownloader.SYSTEM_IMAGE_TAG + ";x86",
DEVICE_DEFINITION, DEVICE_SKIN);

/** Default virtual wear device used by Processing. */
static public final AVD wearAVD =
new AVD("processing-watch",
"system-images;" + AndroidBuild.TARGET_PLATFORM + ";" +
SysImageDownloader.SYSTEM_IMAGE_WEAR_TAG + ";x86",
DEVICE_WEAR_DEFINITION, DEVICE_WEAR_SKIN);


public AVD(final String name, final String sdkId,
final String device, final String skin) {
public AVD(final String name, final String device, final String skin) {
this.name = name;
this.sdkId = sdkId;
this.device = device;
this.skin = skin;
//initializeAbiList(tag);

if (name.contains("phone"))
phoneImages = new ArrayList<>();
else
watchImages = new ArrayList<>();
}


Expand All @@ -123,14 +124,16 @@ static protected void list(final AndroidSDK sdk) throws IOException {
pb.redirectErrorStream(true);

process = pb.start();
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));

StringWriter outWriter = new StringWriter();
new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start();
process.waitFor();

String[] lines = PApplet.split(outWriter.toString(), '\n');

if (process.exitValue() == 0) {
boolean badness = false;
String line;
while ((line = reader.readLine()) != null) {
for (String line : lines) {
String[] m = PApplet.match(line, "\\s+Name\\:\\s+(\\S+)");
if (m != null) {
if (!badness) {
Expand All @@ -154,9 +157,7 @@ static protected void list(final AndroidSDK sdk) throws IOException {
}
} else {
System.err.println("Unhappy inside exists()");
String line;
while ((line = reader.readLine()) != null)
System.err.println(line);
System.err.println(outWriter.toString());
}
} catch (final InterruptedException ie) { }
finally {
Expand Down Expand Up @@ -195,6 +196,65 @@ protected boolean badness() {
return false;
}

protected void getImages(AndroidSDK sdk) throws IOException {
// Dummy avdmanager creation command to get the list of installed images
// TODO : Find a better way to get the list of installed images
ProcessBuilder pb = new ProcessBuilder(
sdk.getAvdManagerPath(),
"create", "avd",
"-n", "dummy",
"-k", "dummy"
);

Map<String, String> env = pb.environment();
env.clear();
env.put("JAVA_HOME", Platform.getJavaHome().getCanonicalPath());
pb.redirectErrorStream(true);

try {
process = pb.start();

StreamPump output = new StreamPump(process.getInputStream(), "out: ");
output.addTarget(new LineProcessor() {
@Override
public void processLine(String line) {
if (phoneImages != null && line.contains(AndroidBuild.TARGET_PLATFORM) &&
line.contains(SysImageDownloader.SYSTEM_IMAGE_TAG))
phoneImages.add(line);
else if (watchImages != null && line.contains(AndroidBuild.TARGET_PLATFORM) &&
line.contains(SysImageDownloader.SYSTEM_IMAGE_WEAR_TAG))
watchImages.add(line);
}
}).start();

process.waitFor();
} catch (final InterruptedException ie) {
ie.printStackTrace();
} finally {
process.destroy();
}
}

protected String getSdkId() throws IOException {
if (Preferences.get("android.system.image.type") == null)
Preferences.set("android.system.image.type", "x86"); // Prefer x86

if (this.name.contains("phone")) {
for (String image : phoneImages) {
if (image.contains(Preferences.get("android.system.image.type")))
return image;
}
} else {
for (String image : watchImages) {
if (image.contains(Preferences.get("android.system.image.type")))
return image;
}
}

// Could not find any suitable package
return "null";
}


protected boolean create(final AndroidSDK sdk) throws IOException {
//initTargets(sdk);
Expand All @@ -207,7 +267,7 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
sdk.getAvdManagerPath(),
"create", "avd",
"-n", name,
"-k", sdkId,
"-k", getSdkId(),
"-c", DEFAULT_SDCARD_SIZE,
"-d", device,
"-p", avdPath.getAbsolutePath(),
Expand All @@ -227,9 +287,6 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
try {
process = pb.start();

InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));

// Passes 'no' to "Do you wish to create a custom hardware profile [no]"
OutputStream os = process.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
Expand All @@ -238,7 +295,10 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
pw.close();
os.flush();
os.close();


StringWriter outWriter = new StringWriter();
new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start();

process.waitFor();

if (process.exitValue() == 0) {
Expand All @@ -253,21 +313,15 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
return true;
}

String line;
StringBuilder output = new StringBuilder();
while ((line = reader.readLine()) != null) {
output.append(line);
}
if (output.toString().contains("Package path is not valid")) {
if (outWriter.toString().contains("Package path is not valid")) {
// They didn't install the Google APIs
AndroidUtil.showMessage(AVD_TARGET_TITLE, AVD_TARGET_MESSAGE);
invalidPackage = true;
} else {
// Just generally not working
AndroidUtil.showMessage(AVD_CREATE_TITLE,
String.format(AVD_CREATE_MESSAGE, AndroidBuild.TARGET_SDK));
}
System.out.println(output.toString());
System.err.println(outWriter.toString());
//System.err.println(createAvdResult);
} catch (final InterruptedException ie) {
ie.printStackTrace();
Expand Down Expand Up @@ -299,20 +353,19 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode
AndroidUtil.showMessage(AVD_LOAD_TITLE, AVD_LOAD_MESSAGE);
return false;
}
if (wearAVD.create(sdk)) {
return true;
}
if (invalidPackage) {
wearAVD.getImages(sdk);
if (wearAVD.watchImages.isEmpty()) {
boolean res = AndroidSDK.locateSysImage(window, mode, true);
if (!res) {
return false;
} else {
// Try again
if (wearAVD.create(sdk)) {
return true;
}
// Refresh images list
wearAVD.getImages(sdk);
}
}
if (wearAVD.create(sdk)) {
return true;
}
} else {
if (mobileAVD.exists(sdk)) {
return true;
Expand All @@ -321,20 +374,19 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode
AndroidUtil.showMessage(AVD_LOAD_TITLE, AVD_LOAD_MESSAGE);
return false;
}
if (mobileAVD.create(sdk)) {
return true;
}
if (invalidPackage) {
mobileAVD.getImages(sdk);
if (mobileAVD.phoneImages.isEmpty()) {
boolean res = AndroidSDK.locateSysImage(window, mode, false);
if (!res) {
return false;
} else {
// Try again
if (mobileAVD.create(sdk)) {
return true;
}
// Refresh images list
mobileAVD.getImages(sdk);
}
}
if (mobileAVD.create(sdk)) {
return true;
}
}
} catch (final Exception e) {
e.printStackTrace();
Expand Down
70 changes: 69 additions & 1 deletion src/processing/mode/android/SysImageDownloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand All @@ -50,6 +52,21 @@

@SuppressWarnings("serial")
public class SysImageDownloader extends JDialog implements PropertyChangeListener {
final static private int FONT_SIZE = Toolkit.zoom(11);
final static private int TEXT_MARGIN = Toolkit.zoom(8);
final static private int TEXT_WIDTH = Toolkit.zoom(300);

private static final String EMULATOR_GUIDE_URL =
"https://developer.android.com/studio/run/emulator-acceleration.html";

private static final String SYS_IMAGE_SELECTION_MESSAGE =
"The Android emulator requires a system image to run." +
"There are two types of system images available -" +
"<ol><li>ARM image - slow but compatible with all computers, no extra configuration required</li>" +
"<li>x86 image - fast but compatible only with Intel CPUs, extra configuration may be required</li>" +
"</ol><br>If you choose to download the x86 image, please follow " +
"<a href=\"" + EMULATOR_GUIDE_URL + "\">this guide</a> to setup the emulator correctly.<br>";

private static final String SYS_IMAGES_URL = "https://dl.google.com/android/repository/sys-img/google_apis/";
private static final String SYS_IMAGES_LIST = "sys-img2-1.xml";

Expand Down Expand Up @@ -228,7 +245,11 @@ private void getDownloadUrls(UrlHolder urlHolder,
XPathExpression expr;
NodeList remotePackages;

expr = xpath.compile("//remotePackage[contains(@path, '" + AndroidBuild.TARGET_SDK + "')" +
if (Preferences.get("android.system.image.type").equals("arm"))
expr = xpath.compile("//remotePackage[contains(@path, '" + AndroidBuild.TARGET_SDK + "')" +
"and contains(@path, \"armeabi-v7a\")]");
else
expr = xpath.compile("//remotePackage[contains(@path, '" + AndroidBuild.TARGET_SDK + "')" +
"and contains(@path, \"x86\")]");

if (wear) {
Expand Down Expand Up @@ -298,6 +319,44 @@ public static String humanReadableByteCount(long bytes, boolean si) {
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}

public int showMessage() {
String htmlString = "<html> " +
"<head> <style type=\"text/css\">" +
"p { font: " + FONT_SIZE + "pt \"Lucida Grande\"; " +
"margin: " + TEXT_MARGIN + "px; " +
"width: " + TEXT_WIDTH + "px }" +
"</style> </head>";

htmlString += "<body>" + SYS_IMAGE_SELECTION_MESSAGE + "</body> </html>";
String title = "Choose system image type to download";
JEditorPane pane = new JEditorPane("text/html", htmlString);
pane.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
Platform.openURL(e.getURL().toString());
}
}
});
pane.setEditable(false);
JLabel label = new JLabel();
pane.setBackground(label.getBackground());

String[] options = new String[] {
"Download ARM image", "Download x86 image"
};
int result = JOptionPane.showOptionDialog(null, pane, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,
null, options, options[0]);
if (result == JOptionPane.YES_OPTION) {
return JOptionPane.YES_OPTION;
} else if (result == JOptionPane.NO_OPTION) {
return JOptionPane.NO_OPTION;
} else {
return JOptionPane.CLOSED_OPTION;
}
}

public SysImageDownloader(Frame editor, boolean wear) {
super(editor, "Emulator download", true);
this.editor = editor;
Expand All @@ -308,6 +367,15 @@ public SysImageDownloader(Frame editor, boolean wear) {

public void run() {
cancelled = false;

final int result = showMessage();
if (result == JOptionPane.YES_OPTION || result == JOptionPane.CLOSED_OPTION) {
// ARM
Preferences.set("android.system.image.type", "arm");
} else {
// x86
Preferences.set("android.system.image.type", "x86");
}
downloadTask = new DownloadTask();
downloadTask.addPropertyChangeListener(this);
downloadTask.execute();
Expand Down