Skip to content

Commit 56688b9

Browse files
committed
Merge remote-tracking branch 'origin/master'
Conflicts: src/processing/mode/android/Permissions.java
2 parents 70e2fa5 + c3a5eba commit 56688b9

15 files changed

+1220
-106
lines changed

build.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<property name="core.jar.path" value="android-core.zip" />
99
<property name="mode.jar.path" value="mode/AndroidMode.jar" />
1010
<property name="mode.dist.path" value="release/AndroidMode.zip" />
11-
11+
<property name="tools.jar.path" value="mode/tools.jar" />
1212

1313
<target name="clean" description="Clean the build directories">
1414
<delete dir="bin" />

core/src/processing/opengl/PGraphicsOpenGL.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6680,9 +6680,13 @@ public void shader(PShader shader) {
66806680

66816681

66826682
@Override
6683-
// TODO: deprecate this method, the kind arguments is not used anymore
66846683
public void shader(PShader shader, int kind) {
6685-
shader(shader);
6684+
flush(); // Flushing geometry drawn with a different shader.
6685+
6686+
if (kind == TRIANGLES) polyShader = shader;
6687+
else if (kind == LINES) lineShader = shader;
6688+
else if (kind == POINTS) pointShader = shader;
6689+
else PGraphics.showWarning(UNKNOWN_SHADER_KIND_ERROR);
66866690
}
66876691

66886692

src/processing/mode/android/AndroidBuild.java

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,23 @@
2121

2222
package processing.mode.android;
2323

24-
import java.io.*;
25-
26-
import org.apache.tools.ant.*;
27-
28-
import processing.app.*;
29-
import processing.app.exec.*;
24+
import org.apache.tools.ant.BuildException;
25+
import org.apache.tools.ant.DefaultLogger;
26+
import org.apache.tools.ant.Project;
27+
import org.apache.tools.ant.ProjectHelper;
28+
29+
import processing.app.Base;
30+
import processing.app.Library;
31+
import processing.app.Sketch;
32+
import processing.app.SketchException;
33+
import processing.app.exec.ProcessHelper;
34+
import processing.app.exec.ProcessResult;
3035
import processing.core.PApplet;
3136
import processing.mode.java.JavaBuild;
3237

38+
import java.io.*;
39+
import java.security.Permission;
40+
3341

3442
class AndroidBuild extends JavaBuild {
3543
// static final String basePackage = "changethispackage.beforesubmitting.tothemarket";
@@ -267,20 +275,95 @@ public File exportProject() throws IOException, SketchException {
267275
return null;
268276
}
269277

270-
271-
public boolean exportPackage() throws IOException, SketchException {
278+
public File exportPackage(String keyStorePassword) throws Exception {
272279
File projectFolder = build("release");
273-
if (projectFolder == null) {
274-
return false;
275-
}
280+
if (projectFolder == null) return null;
276281

277-
// TODO all the signing magic needs to happen here
282+
File signedPackage = signPackage(projectFolder, keyStorePassword);
283+
if (signedPackage == null) return null;
278284

279285
File exportFolder = createExportFolder();
280286
Base.copyDir(projectFolder, exportFolder);
281-
return true;
287+
return new File(exportFolder, "/bin/");
288+
}
289+
290+
private File signPackage(File projectFolder, String keyStorePassword) throws Exception {
291+
File keyStore = AndroidKeyStore.getKeyStore();
292+
if (keyStore == null) return null;
293+
294+
File unsignedPackage = new File(projectFolder, "bin/" + sketch.getName() + "-release-unsigned.apk");
295+
if (!unsignedPackage.exists()) return null;
296+
297+
JarSigner.signJar(unsignedPackage, AndroidKeyStore.ALIAS_STRING, keyStorePassword, keyStore.getAbsolutePath(), keyStorePassword);
298+
299+
//if (verifySignedPackage(unsignedPackage)) {
300+
File signedPackage = new File(projectFolder, "bin/" + sketch.getName() + "-release-signed.apk");
301+
if (signedPackage.exists()) {
302+
boolean deleteResult = signedPackage.delete();
303+
if (!deleteResult) {
304+
Base.showWarning("Error during package signing",
305+
"Unable to delete old signed package");
306+
return null;
307+
}
308+
}
309+
310+
boolean renameResult = unsignedPackage.renameTo(signedPackage);
311+
if (!renameResult) {
312+
Base.showWarning("Error during package signing",
313+
"Unable to rename package file");
314+
return null;
315+
}
316+
317+
File alignedPackage = zipalignPackage(signedPackage, projectFolder);
318+
return alignedPackage;
319+
/*} else {
320+
Base.showWarning("Error during package signing",
321+
"Verification of the signed package has failed");
322+
return null;
323+
}*/
282324
}
283325

326+
/*private boolean verifySignedPackage(File signedPackage) throws Exception {
327+
String[] args = {
328+
"-verify", signedPackage.getCanonicalPath()
329+
};
330+
331+
PrintStream defaultPrintStream = System.out;
332+
333+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
334+
PrintStream printStream = new PrintStream(baos);
335+
System.setOut(printStream);
336+
337+
SystemExitControl.forbidSystemExitCall();
338+
try {
339+
JarSigner.main(args);
340+
} catch (SystemExitControl.ExitTrappedException ignored) { }
341+
SystemExitControl.enableSystemExitCall();
342+
343+
System.setOut(defaultPrintStream);
344+
String result = baos.toString();
345+
346+
baos.close();
347+
printStream.close();
348+
349+
return result.contains("verified");
350+
} */
351+
352+
private File zipalignPackage(File signedPackage, File projectFolder) throws IOException, InterruptedException {
353+
String zipalignPath = sdk.getSdkFolder().getAbsolutePath() + "/tools/zipalign";
354+
File alignedPackage = new File(projectFolder, "bin/" + sketch.getName() + "-release-signed-aligned.apk");
355+
356+
String[] args = {
357+
zipalignPath, "-v", "-f", "4",
358+
signedPackage.getAbsolutePath(), alignedPackage.getAbsolutePath()
359+
};
360+
361+
Process alignProcess = Runtime.getRuntime().exec(args);
362+
alignProcess.waitFor();
363+
364+
if (alignedPackage.exists()) return alignedPackage;
365+
return null;
366+
}
284367

285368
/*
286369
// SDK tools 17 have a problem where 'dex' won't pick up the libs folder
@@ -815,3 +898,27 @@ public void cleanup() {
815898
tmpFolder.deleteOnExit();
816899
}
817900
}
901+
902+
// http://www.avanderw.co.za/preventing-calls-to-system-exit-in-java/
903+
class SystemExitControl {
904+
905+
@SuppressWarnings("serial")
906+
public static class ExitTrappedException extends SecurityException {
907+
}
908+
909+
public static void forbidSystemExitCall() {
910+
final SecurityManager securityManager = new SecurityManager() {
911+
@Override
912+
public void checkPermission(Permission permission) {
913+
if (permission.getName().contains("exitVM")) {
914+
throw new ExitTrappedException();
915+
}
916+
}
917+
};
918+
System.setSecurityManager(securityManager);
919+
}
920+
921+
public static void enableSystemExitCall() {
922+
System.setSecurityManager(null);
923+
}
924+
}

src/processing/mode/android/AndroidEditor.java

Lines changed: 134 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,103 @@
2121

2222
package processing.mode.android;
2323

24+
import processing.app.*;
25+
import processing.core.PApplet;
26+
import processing.mode.java.JavaEditor;
27+
28+
import javax.swing.*;
29+
import javax.swing.event.ChangeEvent;
30+
import javax.swing.event.ChangeListener;
2431
import java.awt.event.ActionEvent;
2532
import java.awt.event.ActionListener;
2633
import java.io.File;
2734
import java.io.IOException;
35+
import java.util.TimerTask;
2836

29-
import javax.swing.JMenu;
30-
import javax.swing.JMenuItem;
37+
@SuppressWarnings("serial")
38+
public class AndroidEditor extends JavaEditor {
39+
private AndroidMode androidMode;
3140

32-
import processing.app.*;
33-
import processing.mode.java.JavaEditor;
41+
class UpdateDeviceListTask extends TimerTask {
3442

35-
import processing.core.PApplet;
43+
private JMenu deviceMenu;
3644

45+
public UpdateDeviceListTask(JMenu deviceMenu) {
46+
this.deviceMenu = deviceMenu;
47+
}
3748

38-
public class AndroidEditor extends JavaEditor {
39-
private AndroidMode androidMode;
49+
@Override
50+
public void run() {
51+
if(androidMode.getSDK() == null) return;
52+
53+
final Devices devices = Devices.getInstance();
54+
java.util.List<Device> deviceList = devices.findMultiple(false);
55+
Device selectedDevice = devices.getSelectedDevice();
56+
57+
if (deviceList.size() == 0) {
58+
//if (deviceMenu.getItem(0).isEnabled()) {
59+
if (0 < deviceMenu.getItemCount()) {
60+
deviceMenu.removeAll();
61+
JMenuItem noDevicesItem = new JMenuItem("No connected devices");
62+
noDevicesItem.setEnabled(false);
63+
deviceMenu.add(noDevicesItem);
64+
}
65+
devices.setSelectedDevice(null);
66+
} else {
67+
deviceMenu.removeAll();
68+
69+
if (selectedDevice == null) {
70+
selectedDevice = deviceList.get(0);
71+
devices.setSelectedDevice(selectedDevice);
72+
} else {
73+
// check if selected device is still connected
74+
boolean found = false;
75+
for (Device device : deviceList) {
76+
if (device.equals(selectedDevice)) {
77+
found = true;
78+
break;
79+
}
80+
}
4081

82+
if (!found) {
83+
selectedDevice = deviceList.get(0);
84+
devices.setSelectedDevice(selectedDevice);
85+
}
86+
}
87+
88+
for (final Device device : deviceList) {
89+
final JCheckBoxMenuItem deviceItem = new JCheckBoxMenuItem(device.getName());
90+
deviceItem.setEnabled(true);
91+
92+
if (device.equals(selectedDevice)) deviceItem.setState(true);
93+
94+
// prevent checkboxmenuitem automatic state changing onclick
95+
deviceItem.addChangeListener(new ChangeListener() {
96+
@Override
97+
public void stateChanged(ChangeEvent e) {
98+
if (device.equals(devices.getSelectedDevice())) deviceItem.setState(true);
99+
else deviceItem.setState(false);
100+
}
101+
});
102+
103+
deviceItem.addActionListener(new ActionListener() {
104+
@Override
105+
public void actionPerformed(ActionEvent e) {
106+
devices.setSelectedDevice(device);
107+
108+
for (int i = 0; i < deviceMenu.getItemCount(); i++) {
109+
((JCheckBoxMenuItem) deviceMenu.getItem(i)).setState(false);
110+
}
111+
112+
deviceItem.setState(true);
113+
}
114+
});
115+
116+
deviceMenu.add(deviceItem);
117+
}
118+
}
119+
}
120+
}
41121

42122
protected AndroidEditor(Base base, String path, EditorState state, Mode mode) throws Exception {
43123
super(base, path, state, mode);
@@ -114,14 +194,28 @@ public void actionPerformed(ActionEvent e) {
114194

115195
menu.addSeparator();
116196

117-
item = new JMenuItem("Signing Key Setup");
197+
/*item = new JMenuItem("Signing Key Setup");
118198
item.addActionListener(new ActionListener() {
119199
public void actionPerformed(ActionEvent e) {
120200
new Keys(AndroidEditor.this);
121201
}
122202
});
123203
item.setEnabled(false);
124-
menu.add(item);
204+
menu.add(item); */
205+
206+
final JMenu deviceMenu = new JMenu("Select device");
207+
208+
JMenuItem noDevicesItem = new JMenuItem("No connected devices");
209+
noDevicesItem.setEnabled(false);
210+
deviceMenu.add(noDevicesItem);
211+
menu.add(deviceMenu);
212+
213+
// start updating device menus
214+
UpdateDeviceListTask task = new UpdateDeviceListTask(deviceMenu);
215+
java.util.Timer timer = new java.util.Timer();
216+
timer.schedule(task, 5000, 5000);
217+
218+
menu.addSeparator();
125219

126220
item = new JMenuItem("Android SDK Manager");
127221
item.addActionListener(new ActionListener() {
@@ -376,27 +470,37 @@ public void run() {
376470
public void handleExportPackage() {
377471
// Need to implement an entire signing setup first
378472
// http://dev.processing.org/bugs/show_bug.cgi?id=1430
379-
statusError("Exporting signed packages is not yet implemented.");
380-
deactivateExport();
381-
382-
// make a release build
383-
// try {
384-
// buildReleaseForExport("release");
385-
// } catch (final MonitorCanceled ok) {
386-
// statusNotice("Canceled.");
387-
// } finally {
388-
// deactivateExport();
389-
// }
390-
391-
// TODO now sign it... lots of fun signing code mess to go here. yay!
473+
if (handleExportCheckModified()) {
474+
deactivateExport();
475+
new KeyStoreManager(this);
476+
}
477+
}
392478

393-
// maybe even send it to the device? mmm?
394-
// try {
395-
// runSketchOnDevice(AndroidEnvironment.getInstance().getHardware(), "release");
396-
// } catch (final MonitorCanceled ok) {
397-
// editor.statusNotice("Canceled.");
398-
// } finally {
399-
// editor.deactivateExport();
400-
// }
479+
public void startExportPackage(final String keyStorePassword) {
480+
new Thread() {
481+
public void run() {
482+
startIndeterminate();
483+
statusNotice("Exporting signed package...");
484+
AndroidBuild build = new AndroidBuild(sketch, androidMode);
485+
try {
486+
File projectFolder = build.exportPackage(keyStorePassword);
487+
if (projectFolder != null) {
488+
statusNotice("Done with export.");
489+
Base.openFolder(projectFolder);
490+
} else {
491+
statusError("Error with export");
492+
}
493+
} catch (IOException e) {
494+
statusError(e);
495+
} catch (SketchException e) {
496+
statusError(e);
497+
} catch (InterruptedException e) {
498+
e.printStackTrace();
499+
} catch (Exception e) {
500+
e.printStackTrace();
501+
}
502+
stopIndeterminate();
503+
}
504+
}.start();
401505
}
402506
}

0 commit comments

Comments
 (0)