Skip to content

Commit c1a30af

Browse files
authored
Merge pull request #371 from rupak0577/avdmanager
Support for ARM system images
2 parents 6dc3ed1 + b6e967c commit c1a30af

File tree

2 files changed

+168
-48
lines changed

2 files changed

+168
-48
lines changed

src/processing/mode/android/AVD.java

Lines changed: 99 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
import processing.app.Base;
2525
import processing.app.Platform;
26+
import processing.app.Preferences;
27+
import processing.app.exec.LineProcessor;
28+
import processing.app.exec.StreamPump;
2629
import processing.core.PApplet;
2730

2831
import java.awt.Frame;
@@ -74,12 +77,12 @@ public class AVD {
7477
protected String name;
7578

7679
/** "system-images;android-25;google_apis;x86" */
77-
protected String sdkId;
78-
80+
protected ArrayList<String> watchImages;
81+
protected ArrayList<String> phoneImages;
82+
7983
protected String device;
8084
protected String skin;
8185

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

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

103102

104-
public AVD(final String name, final String sdkId,
105-
final String device, final String skin) {
103+
public AVD(final String name, final String device, final String skin) {
106104
this.name = name;
107-
this.sdkId = sdkId;
108105
this.device = device;
109106
this.skin = skin;
110-
//initializeAbiList(tag);
107+
108+
if (name.contains("phone"))
109+
phoneImages = new ArrayList<>();
110+
else
111+
watchImages = new ArrayList<>();
111112
}
112113

113114

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

125126
process = pb.start();
126-
InputStream stdout = process.getInputStream();
127-
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
127+
128+
StringWriter outWriter = new StringWriter();
129+
new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start();
128130
process.waitFor();
129131

132+
String[] lines = PApplet.split(outWriter.toString(), '\n');
133+
130134
if (process.exitValue() == 0) {
131135
boolean badness = false;
132-
String line;
133-
while ((line = reader.readLine()) != null) {
136+
for (String line : lines) {
134137
String[] m = PApplet.match(line, "\\s+Name\\:\\s+(\\S+)");
135138
if (m != null) {
136139
if (!badness) {
@@ -154,9 +157,7 @@ static protected void list(final AndroidSDK sdk) throws IOException {
154157
}
155158
} else {
156159
System.err.println("Unhappy inside exists()");
157-
String line;
158-
while ((line = reader.readLine()) != null)
159-
System.err.println(line);
160+
System.err.println(outWriter.toString());
160161
}
161162
} catch (final InterruptedException ie) { }
162163
finally {
@@ -195,6 +196,65 @@ protected boolean badness() {
195196
return false;
196197
}
197198

199+
protected void getImages(AndroidSDK sdk) throws IOException {
200+
// Dummy avdmanager creation command to get the list of installed images
201+
// TODO : Find a better way to get the list of installed images
202+
ProcessBuilder pb = new ProcessBuilder(
203+
sdk.getAvdManagerPath(),
204+
"create", "avd",
205+
"-n", "dummy",
206+
"-k", "dummy"
207+
);
208+
209+
Map<String, String> env = pb.environment();
210+
env.clear();
211+
env.put("JAVA_HOME", Platform.getJavaHome().getCanonicalPath());
212+
pb.redirectErrorStream(true);
213+
214+
try {
215+
process = pb.start();
216+
217+
StreamPump output = new StreamPump(process.getInputStream(), "out: ");
218+
output.addTarget(new LineProcessor() {
219+
@Override
220+
public void processLine(String line) {
221+
if (phoneImages != null && line.contains(AndroidBuild.TARGET_PLATFORM) &&
222+
line.contains(SysImageDownloader.SYSTEM_IMAGE_TAG))
223+
phoneImages.add(line);
224+
else if (watchImages != null && line.contains(AndroidBuild.TARGET_PLATFORM) &&
225+
line.contains(SysImageDownloader.SYSTEM_IMAGE_WEAR_TAG))
226+
watchImages.add(line);
227+
}
228+
}).start();
229+
230+
process.waitFor();
231+
} catch (final InterruptedException ie) {
232+
ie.printStackTrace();
233+
} finally {
234+
process.destroy();
235+
}
236+
}
237+
238+
protected String getSdkId() throws IOException {
239+
if (Preferences.get("android.system.image.type") == null)
240+
Preferences.set("android.system.image.type", "x86"); // Prefer x86
241+
242+
if (this.name.contains("phone")) {
243+
for (String image : phoneImages) {
244+
if (image.contains(Preferences.get("android.system.image.type")))
245+
return image;
246+
}
247+
} else {
248+
for (String image : watchImages) {
249+
if (image.contains(Preferences.get("android.system.image.type")))
250+
return image;
251+
}
252+
}
253+
254+
// Could not find any suitable package
255+
return "null";
256+
}
257+
198258

199259
protected boolean create(final AndroidSDK sdk) throws IOException {
200260
//initTargets(sdk);
@@ -207,7 +267,7 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
207267
sdk.getAvdManagerPath(),
208268
"create", "avd",
209269
"-n", name,
210-
"-k", sdkId,
270+
"-k", getSdkId(),
211271
"-c", DEFAULT_SDCARD_SIZE,
212272
"-d", device,
213273
"-p", avdPath.getAbsolutePath(),
@@ -227,9 +287,6 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
227287
try {
228288
process = pb.start();
229289

230-
InputStream stdout = process.getInputStream();
231-
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
232-
233290
// Passes 'no' to "Do you wish to create a custom hardware profile [no]"
234291
OutputStream os = process.getOutputStream();
235292
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
@@ -238,7 +295,10 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
238295
pw.close();
239296
os.flush();
240297
os.close();
241-
298+
299+
StringWriter outWriter = new StringWriter();
300+
new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start();
301+
242302
process.waitFor();
243303

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

256-
String line;
257-
StringBuilder output = new StringBuilder();
258-
while ((line = reader.readLine()) != null) {
259-
output.append(line);
260-
}
261-
if (output.toString().contains("Package path is not valid")) {
316+
if (outWriter.toString().contains("Package path is not valid")) {
262317
// They didn't install the Google APIs
263318
AndroidUtil.showMessage(AVD_TARGET_TITLE, AVD_TARGET_MESSAGE);
264-
invalidPackage = true;
265319
} else {
266320
// Just generally not working
267321
AndroidUtil.showMessage(AVD_CREATE_TITLE,
268322
String.format(AVD_CREATE_MESSAGE, AndroidBuild.TARGET_SDK));
269323
}
270-
System.out.println(output.toString());
324+
System.err.println(outWriter.toString());
271325
//System.err.println(createAvdResult);
272326
} catch (final InterruptedException ie) {
273327
ie.printStackTrace();
@@ -299,20 +353,19 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode
299353
AndroidUtil.showMessage(AVD_LOAD_TITLE, AVD_LOAD_MESSAGE);
300354
return false;
301355
}
302-
if (wearAVD.create(sdk)) {
303-
return true;
304-
}
305-
if (invalidPackage) {
356+
wearAVD.getImages(sdk);
357+
if (wearAVD.watchImages.isEmpty()) {
306358
boolean res = AndroidSDK.locateSysImage(window, mode, true);
307359
if (!res) {
308360
return false;
309361
} else {
310-
// Try again
311-
if (wearAVD.create(sdk)) {
312-
return true;
313-
}
362+
// Refresh images list
363+
wearAVD.getImages(sdk);
314364
}
315365
}
366+
if (wearAVD.create(sdk)) {
367+
return true;
368+
}
316369
} else {
317370
if (mobileAVD.exists(sdk)) {
318371
return true;
@@ -321,20 +374,19 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode
321374
AndroidUtil.showMessage(AVD_LOAD_TITLE, AVD_LOAD_MESSAGE);
322375
return false;
323376
}
324-
if (mobileAVD.create(sdk)) {
325-
return true;
326-
}
327-
if (invalidPackage) {
377+
mobileAVD.getImages(sdk);
378+
if (mobileAVD.phoneImages.isEmpty()) {
328379
boolean res = AndroidSDK.locateSysImage(window, mode, false);
329380
if (!res) {
330381
return false;
331382
} else {
332-
// Try again
333-
if (mobileAVD.create(sdk)) {
334-
return true;
335-
}
383+
// Refresh images list
384+
mobileAVD.getImages(sdk);
336385
}
337386
}
387+
if (mobileAVD.create(sdk)) {
388+
return true;
389+
}
338390
}
339391
} catch (final Exception e) {
340392
e.printStackTrace();

src/processing/mode/android/SysImageDownloader.java

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
import javax.swing.*;
3535
import javax.swing.border.EmptyBorder;
36+
import javax.swing.event.HyperlinkEvent;
37+
import javax.swing.event.HyperlinkListener;
3638
import javax.xml.parsers.DocumentBuilder;
3739
import javax.xml.parsers.DocumentBuilderFactory;
3840
import javax.xml.parsers.ParserConfigurationException;
@@ -50,6 +52,21 @@
5052

5153
@SuppressWarnings("serial")
5254
public class SysImageDownloader extends JDialog implements PropertyChangeListener {
55+
final static private int FONT_SIZE = Toolkit.zoom(11);
56+
final static private int TEXT_MARGIN = Toolkit.zoom(8);
57+
final static private int TEXT_WIDTH = Toolkit.zoom(300);
58+
59+
private static final String EMULATOR_GUIDE_URL =
60+
"https://developer.android.com/studio/run/emulator-acceleration.html";
61+
62+
private static final String SYS_IMAGE_SELECTION_MESSAGE =
63+
"The Android emulator requires a system image to run." +
64+
"There are two types of system images available -" +
65+
"<ol><li>ARM image - slow but compatible with all computers, no extra configuration required</li>" +
66+
"<li>x86 image - fast but compatible only with Intel CPUs, extra configuration may be required</li>" +
67+
"</ol><br>If you choose to download the x86 image, please follow " +
68+
"<a href=\"" + EMULATOR_GUIDE_URL + "\">this guide</a> to setup the emulator correctly.<br>";
69+
5370
private static final String SYS_IMAGES_URL = "https://dl.google.com/android/repository/sys-img/google_apis/";
5471
private static final String SYS_IMAGES_LIST = "sys-img2-1.xml";
5572

@@ -228,7 +245,11 @@ private void getDownloadUrls(UrlHolder urlHolder,
228245
XPathExpression expr;
229246
NodeList remotePackages;
230247

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

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

322+
public int showMessage() {
323+
String htmlString = "<html> " +
324+
"<head> <style type=\"text/css\">" +
325+
"p { font: " + FONT_SIZE + "pt \"Lucida Grande\"; " +
326+
"margin: " + TEXT_MARGIN + "px; " +
327+
"width: " + TEXT_WIDTH + "px }" +
328+
"</style> </head>";
329+
330+
htmlString += "<body>" + SYS_IMAGE_SELECTION_MESSAGE + "</body> </html>";
331+
String title = "Choose system image type to download";
332+
JEditorPane pane = new JEditorPane("text/html", htmlString);
333+
pane.addHyperlinkListener(new HyperlinkListener() {
334+
@Override
335+
public void hyperlinkUpdate(HyperlinkEvent e) {
336+
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
337+
Platform.openURL(e.getURL().toString());
338+
}
339+
}
340+
});
341+
pane.setEditable(false);
342+
JLabel label = new JLabel();
343+
pane.setBackground(label.getBackground());
344+
345+
String[] options = new String[] {
346+
"Download ARM image", "Download x86 image"
347+
};
348+
int result = JOptionPane.showOptionDialog(null, pane, title,
349+
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,
350+
null, options, options[0]);
351+
if (result == JOptionPane.YES_OPTION) {
352+
return JOptionPane.YES_OPTION;
353+
} else if (result == JOptionPane.NO_OPTION) {
354+
return JOptionPane.NO_OPTION;
355+
} else {
356+
return JOptionPane.CLOSED_OPTION;
357+
}
358+
}
359+
301360
public SysImageDownloader(Frame editor, boolean wear) {
302361
super(editor, "Emulator download", true);
303362
this.editor = editor;
@@ -308,6 +367,15 @@ public SysImageDownloader(Frame editor, boolean wear) {
308367

309368
public void run() {
310369
cancelled = false;
370+
371+
final int result = showMessage();
372+
if (result == JOptionPane.YES_OPTION || result == JOptionPane.CLOSED_OPTION) {
373+
// ARM
374+
Preferences.set("android.system.image.type", "arm");
375+
} else {
376+
// x86
377+
Preferences.set("android.system.image.type", "x86");
378+
}
311379
downloadTask = new DownloadTask();
312380
downloadTask.addPropertyChangeListener(this);
313381
downloadTask.execute();

0 commit comments

Comments
 (0)