Skip to content

Commit c7ad37a

Browse files
committed
download emulator image separately
1 parent 75e8232 commit c7ad37a

File tree

6 files changed

+511
-111
lines changed

6 files changed

+511
-111
lines changed

src/processing/mode/android/AVD.java

+27-12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import processing.app.exec.ProcessResult;
2828
import processing.core.PApplet;
2929

30+
import java.awt.Frame;
3031
import java.io.IOException;
3132
import java.util.ArrayList;
3233
import java.util.HashMap;
@@ -74,7 +75,7 @@ public class AVD {
7475
// static ArrayList<String> skinList;
7576

7677
private Map<String, String> preferredAbi = new HashMap<>(30);
77-
private static List<String> abiList = new ArrayList<>();
78+
private List<String> abiList = new ArrayList<>();
7879

7980
/** Default virtual device used by Processing. */
8081
static public final AVD defaultAVD =
@@ -85,19 +86,15 @@ public class AVD {
8586
public AVD(final String name, final String target) {
8687
this.name = name;
8788
this.target = target;
88-
8989
initializeAbiList();
9090
}
9191

9292
private void initializeAbiList() {
9393
if (abiList.size() == 0) {
9494
// The order in this list determines the preference of one abi over the other
9595
abiList.add("default/x86");
96-
abiList.add("google_apis/x86");
9796
abiList.add("default/x86_64");
98-
abiList.add("google_apis/x86_64");
99-
abiList.add("default/armeabi-v7a");
100-
abiList.add("google_apis/armeabi-v7a");
97+
abiList.add("default/armeabi-v7a");
10198
}
10299
}
103100

@@ -170,9 +167,9 @@ protected boolean badness() {
170167
return false;
171168
}
172169

173-
174-
protected boolean create(final AndroidSDK sdk) throws IOException {
175-
170+
171+
protected void initTargets(final AndroidSDK sdk) throws IOException {
172+
preferredAbi.clear();
176173
final String[] list_abi = {
177174
sdk.getAndroidToolPath(),
178175
"list", "targets"
@@ -203,6 +200,7 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
203200

204201
if (api != null && abis != null) {
205202
for (String abi: abis) {
203+
if (abiList.indexOf(abi) == -1) continue;
206204
if (preferredAbi.get(api) == null) {
207205
preferredAbi.put(api, abi);
208206
} else if (abiList.indexOf(preferredAbi.get(api)) < abiList.indexOf(abi)) {
@@ -233,7 +231,17 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
233231
// }
234232
// }
235233
}
236-
} catch (InterruptedException e) {}
234+
} catch (InterruptedException e) {}
235+
}
236+
237+
protected boolean noTargets(final AndroidSDK sdk) throws IOException {
238+
initTargets(sdk);
239+
return preferredAbi.size() == 0;
240+
}
241+
242+
243+
protected boolean create(final AndroidSDK sdk) throws IOException {
244+
initTargets(sdk);
237245

238246
final String[] params = {
239247
sdk.getAndroidToolPath(),
@@ -248,7 +256,7 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
248256
// Set the list to null so that exists() will check again
249257
avdList = null;
250258

251-
p = new ProcessHelper(params);
259+
ProcessHelper p = new ProcessHelper(params);
252260
try {
253261
// Passes 'no' to "Do you wish to create a custom hardware profile [no]"
254262
final ProcessResult createAvdResult = p.execute("no");
@@ -270,7 +278,8 @@ protected boolean create(final AndroidSDK sdk) throws IOException {
270278
}
271279

272280

273-
static public boolean ensureProperAVD(final AndroidSDK sdk) {
281+
static public boolean ensureProperAVD(final Frame window, final AndroidMode mode,
282+
final AndroidSDK sdk) {
274283
try {
275284
if (defaultAVD.exists(sdk)) {
276285
return true;
@@ -279,6 +288,12 @@ static public boolean ensureProperAVD(final AndroidSDK sdk) {
279288
Messages.showWarningTiered("Android Error", AVD_LOAD_PRIMARY, AVD_LOAD_SECONDARY, null);
280289
return false;
281290
}
291+
if (defaultAVD.noTargets(sdk)) {
292+
boolean res = AndroidSDK.locateSysImage(window, mode);
293+
if (!res) {
294+
return false;
295+
}
296+
}
282297
if (defaultAVD.create(sdk)) {
283298
return true;
284299
}

src/processing/mode/android/AndroidEditor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ public void run() {
508508
startIndeterminate();
509509
prepareRun();
510510
try {
511-
androidMode.handleRunEmulator(sketch, AndroidEditor.this);
511+
androidMode.handleRunEmulator(sketch, AndroidEditor.this, AndroidEditor.this);
512512
} catch (SketchException e) {
513513
statusError(e);
514514
} catch (IOException e) {

src/processing/mode/android/AndroidMode.java

+60-2
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@
3232
import processing.app.ui.Editor;
3333
import processing.app.ui.EditorException;
3434
import processing.app.ui.EditorState;
35+
import processing.core.PApplet;
3536
import processing.mode.android.AndroidSDK.CancelException;
3637
import processing.mode.java.JavaMode;
3738

39+
import java.io.BufferedInputStream;
40+
import java.io.BufferedOutputStream;
3841
import java.io.File;
42+
import java.io.FileOutputStream;
3943
import java.io.IOException;
4044
import java.text.SimpleDateFormat;
4145
import java.util.Date;
46+
import java.util.Enumeration;
47+
import java.util.zip.ZipEntry;
48+
import java.util.zip.ZipFile;
4249

4350

4451
public class AndroidMode extends JavaMode {
@@ -242,15 +249,16 @@ static public String getDateStamp(long stamp) {
242249
// runtime.launch(false);
243250
// }
244251
// }
245-
public void handleRunEmulator(Sketch sketch, RunnerListener listener) throws SketchException, IOException {
252+
public void handleRunEmulator(Sketch sketch, AndroidEditor editor,
253+
RunnerListener listener) throws SketchException, IOException {
246254
listener.startIndeterminate();
247255
listener.statusNotice("Starting build...");
248256
AndroidBuild build = new AndroidBuild(sketch, this);
249257

250258
listener.statusNotice("Building Android project...");
251259
build.build("debug");
252260

253-
boolean avd = AVD.ensureProperAVD(sdk);
261+
boolean avd = AVD.ensureProperAVD(editor, this, sdk);
254262
if (!avd) {
255263
SketchException se =
256264
new SketchException("Could not create a virtual device for the emulator.");
@@ -306,6 +314,56 @@ public void handleStop(RunnerListener listener) {
306314
}
307315
}
308316

317+
318+
public static void extractFolder(File file, File newPath, boolean setExec) throws IOException {
319+
int BUFFER = 2048;
320+
ZipFile zip = new ZipFile(file);
321+
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
322+
323+
// Process each entry
324+
while (zipFileEntries.hasMoreElements()) {
325+
// grab a zip file entry
326+
ZipEntry entry = zipFileEntries.nextElement();
327+
String currentEntry = entry.getName();
328+
File destFile = new File(newPath, currentEntry);
329+
//destFile = new File(newPath, destFile.getName());
330+
File destinationParent = destFile.getParentFile();
331+
332+
// create the parent directory structure if needed
333+
destinationParent.mkdirs();
334+
335+
String ext = PApplet.getExtension(currentEntry);
336+
if (setExec && ext.equals("unknown")) {
337+
// On some OS X machines the android binaries loose their executable
338+
// attribute, rendering the mode unusable
339+
destFile.setExecutable(true);
340+
}
341+
342+
if (!entry.isDirectory()) {
343+
// should preserve permissions
344+
// https://bitbucket.org/atlassian/amps/pull-requests/21/amps-904-preserve-executable-file-status/diff
345+
BufferedInputStream is = new BufferedInputStream(zip
346+
.getInputStream(entry));
347+
int currentByte;
348+
// establish buffer for writing file
349+
byte data[] = new byte[BUFFER];
350+
351+
// write the current file to disk
352+
FileOutputStream fos = new FileOutputStream(destFile);
353+
BufferedOutputStream dest = new BufferedOutputStream(fos,
354+
BUFFER);
355+
356+
// read and write until last byte is encountered
357+
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
358+
dest.write(data, 0, currentByte);
359+
}
360+
dest.flush();
361+
dest.close();
362+
is.close();
363+
}
364+
}
365+
zip.close();
366+
}
309367

310368
// public void handleExport(Sketch sketch, )
311369

src/processing/mode/android/AndroidSDK.java

+75-2
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@
4141

4242

4343
class AndroidSDK {
44-
static final String DOWNLOAD_URL ="https://developer.android.com/studio/index.html#downloads";
45-
4644
private final File folder;
4745
private final File tools;
4846
private final File platforms;
4947
private final File platformTools;
5048
private final File androidTool;
5149

50+
static final String DOWNLOAD_URL ="https://developer.android.com/studio/index.html#downloads";
51+
5252
private static final String ANDROID_SDK_PRIMARY =
5353
"Is the Android SDK installed?";
5454

@@ -62,6 +62,14 @@ class AndroidSDK {
6262
"the command line tools from <a href=\"" + DOWNLOAD_URL + "\">here</a>. Make sure to install<br>" +
6363
"the SDK platform for API 15 (Android 4.0.3) or higher.";
6464

65+
private static final String ANDROID_SYS_IMAGE_PRIMARY =
66+
"Download emulator?";
67+
68+
private static final String ANDROID_SYS_IMAGE_SECONDARY =
69+
"The emulator does not appear to be installed, <br>" +
70+
"Do you want Processing to download and install it now? <br>" +
71+
"Otherwise, you will need to do it through SDK manager.";
72+
6573
private static final String SELECT_ANDROID_SDK_FOLDER =
6674
"Choose the location of the Android SDK";
6775

@@ -266,6 +274,19 @@ static public AndroidSDK locate(final Frame window, final AndroidMode androidMod
266274
}
267275
}
268276

277+
static public boolean locateSysImage(final Frame window, final AndroidMode androidMode)
278+
throws BadSDKException, CancelException, IOException {
279+
final int result = showDownloadSysImageDialog(window);
280+
if (result == JOptionPane.YES_OPTION) {
281+
return downloadSysImage(window, androidMode);
282+
} else if (result == JOptionPane.NO_OPTION) {
283+
return false;
284+
} else {
285+
return false;
286+
}
287+
}
288+
289+
269290
static public AndroidSDK download(final Frame editor, final AndroidMode androidMode)
270291
throws BadSDKException, CancelException {
271292
final SDKDownloader downloader = new SDKDownloader(editor, androidMode);
@@ -281,6 +302,23 @@ static public AndroidSDK download(final Frame editor, final AndroidMode androidM
281302
return sdk;
282303
}
283304

305+
306+
static public boolean downloadSysImage(final Frame editor, final AndroidMode androidMode)
307+
throws BadSDKException, CancelException {
308+
final SysImageDownloader downloader = new SysImageDownloader(editor, androidMode);
309+
downloader.run(); // This call blocks until the SDK download complete, or user cancels.
310+
311+
if (downloader.cancelled()) {
312+
throw new CancelException("User canceled emulator download");
313+
}
314+
boolean res = downloader.getResult();
315+
if (!res) {
316+
throw new BadSDKException("Emulator could not be downloaded");
317+
}
318+
return res;
319+
}
320+
321+
284322
static public int showLocateDialog(Frame editor) {
285323
// Pane formatting adapted from the Quaqua guide
286324
// http://www.randelshofer.ch/quaqua/guide/joptionpane.html
@@ -316,7 +354,42 @@ static public int showLocateDialog(Frame editor) {
316354
return JOptionPane.CLOSED_OPTION;
317355
}
318356
}
357+
358+
static public int showDownloadSysImageDialog(Frame editor) {
359+
String msg1 =ANDROID_SYS_IMAGE_PRIMARY;
360+
String msg2 = ANDROID_SYS_IMAGE_SECONDARY;
361+
362+
JOptionPane pane =
363+
new JOptionPane("<html> " +
364+
"<head> <style type=\"text/css\">"+
365+
"b { font: 13pt \"Lucida Grande\" }"+
366+
"p { font: 11pt \"Lucida Grande\"; margin-top: 8px; width: 300px }"+
367+
"</style> </head>" +
368+
"<b>" + msg1 + "</b>" +
369+
"<p>" + msg2 + "</p>",
370+
JOptionPane.QUESTION_MESSAGE);
371+
372+
String[] options = new String[] { "Yes", "No" };
373+
pane.setOptions(options);
319374

375+
// highlight the safest option ala apple hig
376+
pane.setInitialValue(options[0]);
377+
378+
JDialog dialog = pane.createDialog(editor, null);
379+
dialog.setTitle("");
380+
dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
381+
dialog.setVisible(true);
382+
383+
Object result = pane.getValue();
384+
if (result == options[0]) {
385+
return JOptionPane.YES_OPTION;
386+
} else if (result == options[1]) {
387+
return JOptionPane.NO_OPTION;
388+
} else {
389+
return JOptionPane.CLOSED_OPTION;
390+
}
391+
}
392+
320393
// this was banished from Base because it encourages bad practice.
321394
// TODO figure out a better way to handle the above.
322395
static public File selectFolder(String prompt, File folder, Frame frame) {

0 commit comments

Comments
 (0)