@@ -277,14 +277,116 @@ public File exportPackage() throws IOException, SketchException, InterruptedExce
277
277
File projectFolder = build ("release" );
278
278
if (projectFolder == null ) return null ;
279
279
280
- File keyStore = getKeyStore ();
281
- if (keyStore == null ) return null ;
280
+ File signedPackage = signPackage ();
281
+ if (signedPackage == null ) return null ;
282
282
283
283
File exportFolder = createExportFolder ();
284
284
Base .copyDir (projectFolder , exportFolder );
285
285
return new File (exportFolder , "/bin/" );
286
286
}
287
287
288
+ String combine (String [] s , String glue )
289
+ {
290
+ int k = s .length ;
291
+ if ( k == 0 )
292
+ {
293
+ return null ;
294
+ }
295
+ StringBuilder out = new StringBuilder ();
296
+ out .append ( s [0 ] );
297
+ for ( int x =1 ; x < k ; ++x )
298
+ {
299
+ out .append (glue ).append (s [x ]);
300
+ }
301
+ return out .toString ();
302
+ }
303
+
304
+ private File signPackage () throws IOException , InterruptedException {
305
+ File keyStore = getKeyStore ();
306
+ if (keyStore == null ) return null ;
307
+
308
+ File unsignedPackage = new File (sketch .getFolder (), "android/bin/" + sketch .getName () + "-release-unsigned.apk" );
309
+ if (!unsignedPackage .exists ()) return null ;
310
+
311
+ // NOT sure that works: java.home returns JRE path here, need to walk back to ../../bin to find jarsigner
312
+ // TODO receive generated password here
313
+ String password = "generatedpasswordhere" ;
314
+
315
+ String [] args = {
316
+ System .getProperty ("java.home" ) + System .getProperty ("file.separator" ) + ".."
317
+ + System .getProperty ("file.separator" ) + "bin"
318
+ + System .getProperty ("file.separator" ) + "jarsigner" ,
319
+ "-sigalg" , "SHA1withRSA" ,
320
+ "-digestalg" , "SHA1" ,
321
+ "-keypass" , password ,
322
+ "-storepass" , password ,
323
+ "-keystore" , keyStore .getCanonicalPath (),
324
+ unsignedPackage .getCanonicalPath (),
325
+ sketch .getName ()
326
+ };
327
+
328
+ System .out .println ("Started signing process" );
329
+ System .out .println (combine (args , " " ));
330
+
331
+ Process signingProcess = Runtime .getRuntime ().exec (args );
332
+ signingProcess .waitFor ();
333
+
334
+ System .out .println ("Finished signing process" );
335
+
336
+ if (verifySignedPackage (unsignedPackage )) {
337
+ File signedPackage = new File (sketch .getFolder (), "android/bin/" + sketch .getName () + "-release-signed.apk" );
338
+ if (signedPackage .exists ()) {
339
+ boolean deleteResult = signedPackage .delete ();
340
+ if (!deleteResult ) {
341
+ Base .showWarning ("Error during package signing" ,
342
+ "Unable to delete old signed package" );
343
+ return null ;
344
+ }
345
+ }
346
+
347
+ boolean renameResult = unsignedPackage .renameTo (signedPackage );
348
+ if (!renameResult ) {
349
+ Base .showWarning ("Error during package signing" ,
350
+ "Unable to rename package file" );
351
+ return null ;
352
+ }
353
+
354
+ // TODO zipalign package
355
+ return signedPackage ;
356
+ } else {
357
+ Base .showWarning ("Error during package signing" ,
358
+ "Verification of the signed package has failed" );
359
+ return null ;
360
+ }
361
+ }
362
+
363
+ // probably not the best way to do it; should we really verify?
364
+ private boolean verifySignedPackage (File signedPackage ) throws IOException , InterruptedException {
365
+ // same concerns about jarsigner path here as above
366
+ String [] args = {
367
+ System .getProperty ("java.home" ) + System .getProperty ("file.separator" ) + ".."
368
+ + System .getProperty ("file.separator" ) + "bin"
369
+ + System .getProperty ("file.separator" ) + "jarsigner" ,
370
+ "-verify" , signedPackage .getCanonicalPath ()
371
+ };
372
+
373
+ Process signingProcess = Runtime .getRuntime ().exec (args );
374
+ signingProcess .waitFor ();
375
+
376
+ InputStream is = signingProcess .getInputStream ();
377
+ InputStreamReader isr = new InputStreamReader (is );
378
+ BufferedReader br = new BufferedReader (isr );
379
+ String line ;
380
+
381
+ while ((line = br .readLine ()) != null ) {
382
+ if (line .contains ("verified" )) {
383
+ return true ;
384
+ }
385
+ }
386
+
387
+ return false ;
388
+ }
389
+
288
390
private File getKeyStore () throws IOException , InterruptedException {
289
391
File keyStoreFolder = new File (sketch .getFolder (), "keystore" );
290
392
if (!keyStoreFolder .exists ()) {
@@ -305,7 +407,9 @@ private File getKeyStore() throws IOException, InterruptedException {
305
407
String password = "generatedpasswordhere" ;
306
408
307
409
String [] args = {
308
- "keytool" , "-genkey" ,
410
+ System .getProperty ("java.home" )
411
+ + System .getProperty ("file.separator" ) + "bin"
412
+ + System .getProperty ("file.separator" ) + "keytool" , "-genkey" ,
309
413
"-keystore" , keyStore .getCanonicalPath (),
310
414
"-alias" , sketch .getName (),
311
415
"-keyalg" , "RSA" ,
0 commit comments