From 75007619695b916b892620b95b5b3f46b218d046 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:51:57 +0530 Subject: [PATCH 1/9] Bundle Support Utility in File Menu --- .../mode/android/AndroidEditor.java | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/mode/src/processing/mode/android/AndroidEditor.java b/mode/src/processing/mode/android/AndroidEditor.java index e9c36639..74a28720 100644 --- a/mode/src/processing/mode/android/AndroidEditor.java +++ b/mode/src/processing/mode/android/AndroidEditor.java @@ -136,7 +136,15 @@ public void actionPerformed(ActionEvent e) { } }); - return buildFileMenu(new JMenuItem[] { exportPackage, exportProject}); + String exportBundleTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT_BUNDLE, false); + JMenuItem exportBundle = Toolkit.newJMenuItem(exportBundleTitle, 'B'); + exportBundle.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExportBundle(); + } + }); + + return buildFileMenu(new JMenuItem[] { exportPackage, exportProject, exportBundle}); } @@ -496,15 +504,53 @@ public void run() { } } + /** + * Create a release bundle of the sketch + */ + public void handleExportBundle() { + if (androidMode.checkPackageName(sketch, appComponent, true) && + androidMode.checkAppIcons(sketch, appComponent, true) && handleExportCheckModified()) { + new KeyStoreManager(this, true); + } + } + + public void startExportBundle(final String keyStorePassword) { + new Thread() { + public void run() { + startIndeterminate(); + statusNotice(AndroidMode.getTextString("android_editor.status.exporting_bundle")); + AndroidBuild build = new AndroidBuild(sketch, androidMode, appComponent); + try { + File projectFolder = build.exportBundle(keyStorePassword); + if (projectFolder != null) { + + statusNotice(AndroidMode.getTextString("android_editor.status.bundle_export_completed")); + Platform.openFolder(projectFolder); + } else { + statusError(AndroidMode.getTextString("android_editor.status.bundle_export_failed")); + } + } catch (IOException e) { + statusError(e); + } catch (SketchException e) { + statusError(e); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + stopIndeterminate(); + } + }.start(); + } /** * Create a release build of the sketch and install its apk files on the * attached device. */ public void handleExportPackage() { - if (androidMode.checkPackageName(sketch, appComponent) && - androidMode.checkAppIcons(sketch, appComponent) && handleExportCheckModified()) { - new KeyStoreManager(this); + if (androidMode.checkPackageName(sketch, appComponent, false) && + androidMode.checkAppIcons(sketch, appComponent, false) && handleExportCheckModified()) { + new KeyStoreManager(this, false); } } @@ -753,4 +799,4 @@ public void actionPerformed(ActionEvent e) { } } } -} \ No newline at end of file +} From 025c83e4d342f90748721622de617d6957344691 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:53:53 +0530 Subject: [PATCH 2/9] Update AndroidBuild.java --- .../processing/mode/android/AndroidBuild.java | 126 +++++++++++++++--- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/mode/src/processing/mode/android/AndroidBuild.java b/mode/src/processing/mode/android/AndroidBuild.java index 379dd321..72a2d1a2 100644 --- a/mode/src/processing/mode/android/AndroidBuild.java +++ b/mode/src/processing/mode/android/AndroidBuild.java @@ -188,7 +188,21 @@ public String getPathForAPK() { return null; } return apkFile.getAbsolutePath(); - } + } + + /** + * Build into temporary folders for building bundles (needed for the Windows 8.3 bugs in the Android SDK) + * @param target "debug" or "release" + * @throws SketchException + * @throws IOException + */ + public File buildBundle(String target) throws IOException, SketchException { + this.target = target; + File folder = createProject(true); + if (folder == null) return null; + if (!gradleBuildBundle()) return null; + return folder; + } /** @@ -242,6 +256,44 @@ protected File createProject(boolean external) return tmpFolder; } + + protected boolean gradleBuildBundle() throws SketchException { + ProjectConnection connection = GradleConnector.newConnector() + .forProjectDirectory(tmpFolder) + .connect(); + + boolean success = false; + BuildLauncher build = connection.newBuild(); + build.setStandardOutput(System.out); + build.setStandardError(System.err); + + try { + if (target.equals("debug")) build.forTasks("bundleDebug"); + else build.forTasks("bundleRelease"); + build.run(); + renameAAB(); + success = true; + } catch (org.gradle.tooling.UnsupportedVersionException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.BuildException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.BuildCancelledException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.GradleConnectionException e) { + e.printStackTrace(); + success = false; + } catch (Exception e) { + e.printStackTrace(); + success = false; + } finally { + connection.close(); + } + + return success; + } protected boolean gradleBuild() throws SketchException { @@ -676,7 +728,25 @@ public File exportProject() throws IOException, SketchException { Util.copyDir(projectFolder, exportFolder); installGradlew(exportFolder); return exportFolder; - } + } + + + // --------------------------------------------------------------------------- + // Export bundle + + + public File exportBundle(String keyStorePassword) throws Exception { + File projectFolder = buildBundle("release"); + if (projectFolder == null) return null; + + File signedPackage = signPackage(projectFolder, keyStorePassword, "aab"); + if (signedPackage == null) return null; + + // Final export folder + File exportFolder = createExportFolder("buildBundle"); + Util.copyDir(new File(projectFolder, getPathToAAB()), exportFolder); + return exportFolder; + } // --------------------------------------------------------------------------- @@ -687,7 +757,7 @@ public File exportPackage(String keyStorePassword) throws Exception { File projectFolder = build("release"); if (projectFolder == null) return null; - File signedPackage = signPackage(projectFolder, keyStorePassword); + File signedPackage = signPackage(projectFolder, keyStorePassword, "apk"); if (signedPackage == null) return null; // Final export folder @@ -697,26 +767,34 @@ public File exportPackage(String keyStorePassword) throws Exception { } - private File signPackage(File projectFolder, String keyStorePassword) throws Exception { + private File signPackage(File projectFolder, String keyStorePassword, String fileExt) throws Exception { File keyStore = AndroidKeyStore.getKeyStore(); if (keyStore == null) return null; - - File unsignedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_unsigned.apk"); - if (!unsignedPackage.exists()) return null; - File signedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_signed.apk"); + + String path=getPathToAPK(); + if(fileExt.equals("aab")){ + path = getPathToAAB(); + } + File unsignedPackage = new File(projectFolder, + path + sketch.getName().toLowerCase() + "_release_unsigned." + fileExt); + if (!unsignedPackage.exists()) return null; + File signedPackage = new File(projectFolder, + path + sketch.getName().toLowerCase() + "_release_signed." + fileExt); + JarSigner.signJar(unsignedPackage, signedPackage, AndroidKeyStore.ALIAS_STRING, keyStorePassword, keyStore.getAbsolutePath(), keyStorePassword); - File alignedPackage = zipalignPackage(signedPackage, projectFolder); + if(fileExt.equals("aab")){ + return signedPackage; + } + File alignedPackage = zipalignPackage(signedPackage, projectFolder, fileExt); return alignedPackage; } - private File zipalignPackage(File signedPackage, File projectFolder) + private File zipalignPackage(File signedPackage, File projectFolder, String fileExt) throws IOException, InterruptedException { File zipAlign = sdk.getZipAlignTool(); if (zipAlign == null || !zipAlign.exists()) { @@ -724,9 +802,10 @@ private File zipalignPackage(File signedPackage, File projectFolder) AndroidMode.getTextString("android_build.warn.cannot_find_zipalign.body")); return null; } + File alignedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_signed_aligned.apk"); + getPathToAPK() + sketch.getName().toLowerCase() + "_release_signed_aligned."+fileExt); String[] args = { zipAlign.getAbsolutePath(), "-v", "-f", "4", @@ -841,7 +920,24 @@ private void copyCodeFolder(final File libsFolder) throws IOException { } } } - } + } + + private void renameAAB() { + String suffix = target.equals("release") ? "release" : "debug"; + String aabName = getPathToAAB() + module + "-" + suffix + ".aab"; + final File aabFile = new File(tmpFolder, aabName); + if (aabFile.exists()) { + String suffixNew = target.equals("release") ? "release_unsigned" : "debug"; + String aabNameNew = getPathToAAB() + + sketch.getName().toLowerCase() + "_" + suffixNew + ".aab"; + final File aabFileNew = new File(tmpFolder, aabNameNew); + aabFile.renameTo(aabFileNew); + } + } + + private String getPathToAAB() { + return module + "/build/outputs/bundle/" + target + "/"; + } private void renameAPK() { @@ -1014,4 +1110,4 @@ static private boolean versionCheck(String currentVersion, String minVersion) { return false; } -} \ No newline at end of file +} From e08fa30a1f98d36572bcd4f6b94915237b59d39b Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:56:47 +0530 Subject: [PATCH 3/9] Update KeyStoreManager.java --- .../mode/android/KeyStoreManager.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/mode/src/processing/mode/android/KeyStoreManager.java b/mode/src/processing/mode/android/KeyStoreManager.java index d32f42e0..298e90ce 100644 --- a/mode/src/processing/mode/android/KeyStoreManager.java +++ b/mode/src/processing/mode/android/KeyStoreManager.java @@ -64,14 +64,14 @@ public class KeyStoreManager extends JFrame { JTextField country; JTextField stateName; - public KeyStoreManager(final AndroidEditor editor) { + public KeyStoreManager(final AndroidEditor editor, boolean forBundle) { super("Android keystore manager"); this.editor = editor; - createLayout(); + createLayout(forBundle); } - private void createLayout() { + private void createLayout( boolean forBundleExport) { Container outer = getContentPane(); outer.removeAll(); @@ -105,13 +105,21 @@ public void actionPerformed(ActionEvent e) { localityName.getText(), stateName.getText(), country.getText()); setVisible(false); - editor.startExportPackage(new String(passwordField.getPassword())); + if(forBundleExport){ + editor.startExportBundle(new String(passwordField.getPassword())); + }else { + editor.startExportPackage(new String(passwordField.getPassword())); + } } catch (Exception e1) { e1.printStackTrace(); } } else { setVisible(false); - editor.startExportPackage(new String(passwordField.getPassword())); + if(forBundleExport){ + editor.startExportBundle(new String(passwordField.getPassword())); + }else { + editor.startExportPackage(new String(passwordField.getPassword())); + } } } } @@ -147,7 +155,7 @@ public void actionPerformed(ActionEvent e) { setVisible(true); } else { keyStore = null; - createLayout(); + createLayout(forBundleExport); } } } From be0c5f0071711659653d1fe99e679b0789b3ed47 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:58:47 +0530 Subject: [PATCH 4/9] Resolves Unknown Char Error --- mode/src/processing/mode/android/Manifest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/src/processing/mode/android/Manifest.java b/mode/src/processing/mode/android/Manifest.java index c50075fe..58aef8fe 100644 --- a/mode/src/processing/mode/android/Manifest.java +++ b/mode/src/processing/mode/android/Manifest.java @@ -48,7 +48,7 @@ public class Manifest { static final String MANIFEST_ERROR_MESSAGE = "Errors occurred while reading or writing " + MANIFEST_XML + ",\n" + "which means lots of things are likely to stop working properly.\n" + - "To prevent losing any data, it's recommended that you use “Save As”\n" + + "To prevent losing any data, it's recommended that you use Save As\n" + "to save a separate copy of your sketch, and then restart Processing."; static private final String[] MANIFEST_TEMPLATE = { From d7df73bdebd5565d44fa9d973003e31350a447c4 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:59:38 +0530 Subject: [PATCH 5/9] Update AndroidMode.java --- .../processing/mode/android/AndroidMode.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/mode/src/processing/mode/android/AndroidMode.java b/mode/src/processing/mode/android/AndroidMode.java index 574bf1c7..b7d12c26 100644 --- a/mode/src/processing/mode/android/AndroidMode.java +++ b/mode/src/processing/mode/android/AndroidMode.java @@ -326,21 +326,26 @@ public void handleStop(RunnerListener listener) { } - public boolean checkPackageName(Sketch sketch, int comp) { + public boolean checkPackageName(Sketch sketch, int comp, boolean forBundle) { Manifest manifest = new Manifest(sketch, comp, getFolder(), false); String defName = Manifest.BASE_PACKAGE + "." + sketch.getName().toLowerCase(); String name = manifest.getPackageName(); if (name.toLowerCase().equals(defName.toLowerCase())) { // The user did not set the package name, show error and stop - AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_export_package_title"), - AndroidMode.getTextString("android_mode.dialog.cannot_export_package_body", DISTRIBUTING_APPS_TUT_URL)); + if(forBundle){ + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_export_bundle_title"), + AndroidMode.getTextString("android_mode.dialog.cannot_export_package_body", DISTRIBUTING_APPS_TUT_URL)); + }else { + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_export_package_title"), + AndroidMode.getTextString("android_mode.dialog.cannot_export_package_body", DISTRIBUTING_APPS_TUT_URL)); + } return false; } return true; } - public boolean checkAppIcons(Sketch sketch, int comp) { + public boolean checkAppIcons(Sketch sketch, int comp, boolean forBundle) { File sketchFolder = sketch.getFolder(); File[] launcherIcons = AndroidUtil.getFileList(sketchFolder, AndroidBuild.SKETCH_LAUNCHER_ICONS, @@ -355,9 +360,14 @@ public boolean checkAppIcons(Sketch sketch, int comp) { if (!allFilesExist) { // The user did not set custom icons, show error and stop - AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_title"), - AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_body", DISTRIBUTING_APPS_TUT_URL)); - return false; + if(forBundle){ + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_title_bundle"), + AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_body", DISTRIBUTING_APPS_TUT_URL)); + }else { + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_title"), + AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_body", DISTRIBUTING_APPS_TUT_URL)); + } + return false; } return true; } @@ -419,4 +429,5 @@ static public String getTextString(String key, Object... arguments) { } return String.format(value, arguments); } -} \ No newline at end of file +} + From dd9e732d507c730035bc923b52d5aae093778d68 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 06:00:11 +0530 Subject: [PATCH 6/9] Update AndroidToolbar.java --- mode/src/processing/mode/android/AndroidToolbar.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mode/src/processing/mode/android/AndroidToolbar.java b/mode/src/processing/mode/android/AndroidToolbar.java index 8632f768..cb4c08fa 100644 --- a/mode/src/processing/mode/android/AndroidToolbar.java +++ b/mode/src/processing/mode/android/AndroidToolbar.java @@ -46,6 +46,8 @@ public class AndroidToolbar extends EditorToolbar { static protected final int OPEN = 3; static protected final int SAVE = 4; static protected final int EXPORT = 5; + static protected final int EXPORT_BUNDLE = 6; + private AndroidEditor aEditor; @@ -77,6 +79,7 @@ static public String getTitle(int index, boolean shift) { case SAVE: return "Save"; case EXPORT: return !shift ? AndroidMode.getTextString("menu.file.export_signed_package") : AndroidMode.getTextString("menu.file.export_android_project"); + case EXPORT_BUNDLE: return AndroidMode.getTextString("menu.file.export_signed_bundle"); } return null; } @@ -250,4 +253,5 @@ public void deactivateStep() { stepButton.setSelected(false); repaint(); } -} \ No newline at end of file +} + From 6aff7dfa00ee7d86d3d1ba15f01ce5e985069b11 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 06:02:09 +0530 Subject: [PATCH 7/9] Update mode_ko.properties --- mode/languages/mode_ko.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/languages/mode_ko.properties b/mode/languages/mode_ko.properties index bac8efac..24859b16 100644 --- a/mode/languages/mode_ko.properties +++ b/mode/languages/mode_ko.properties @@ -13,6 +13,7 @@ menu.file.export_signed_package = 서명 된 패키지 내보내기 menu.file.export_android_project = 안드로이드 프로젝트 내보내기 +menu.file.export_signed_bundle = 안드로이드 번들 내보내기 # | File | Edit | Sketch | Android | Tools | Help | # | Android | @@ -27,4 +28,4 @@ menu.android.ar = AR menu.android.devices = 장치들 menu.android.devices.no_connected_devices = 연결된 기기 없음 menu.android.sdk_updater = SDK 업데이터 -menu.android.reset_adb = ADB 재설정 \ No newline at end of file +menu.android.reset_adb = ADB 재설정 From 0c08f0e00c0c9f7fa84fde16f7b068254c68cb7b Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Fri, 24 Sep 2021 06:02:59 +0530 Subject: [PATCH 8/9] Update mode.properties --- mode/languages/mode.properties | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mode/languages/mode.properties b/mode/languages/mode.properties index fc025df5..ef7805e2 100644 --- a/mode/languages/mode.properties +++ b/mode/languages/mode.properties @@ -13,6 +13,7 @@ menu.file.export_signed_package = Export Signed Package menu.file.export_android_project = Export Android Project +menu.file.export_signed_bundle = Export Signed Bundle # | File | Edit | Sketch | Android | Tools | Help | # | Android | @@ -61,8 +62,11 @@ android_editor.status.exporting_project = Exporting an Android project of the sk android_editor.status.project_export_completed = Done with project export. android_editor.status.project_export_failed = Error with project export. android_editor.status.exporting_package = Exporting signed package... +android_editor.status.exporting_bundle = Exporting signed bundle... android_editor.status.package_export_completed = Done with package export. +android_editor.status.bundle_export_completed = Done with bundle export. android_editor.status.package_export_failed = Error with package export. +android_editor.status.bundle_export_failed = Error with bundle export. android_editor.error.cannot_create_sketch_properties = Error While creating sketch properties file "%s": %s # --------------------------------------- @@ -84,8 +88,10 @@ android_mode.dialog.wallpaper_installed_body = Processing just built and install android_mode.dialog.watchface_installed_title = Watch face installed! android_mode.dialog.watchface_installed_body = Processing just built and installed your sketch as a watch face on the selected device.

You need to add it as a favourite watch face on the device and then select it from the watch face picker in order to run it. android_mode.dialog.cannot_export_package_title = Cannot export package... +android_mode.dialog.cannot_export_bundle_title = Cannot export bundle... android_mode.dialog.cannot_export_package_body = The sketch still has the default package name. Not good, since this name will uniquely identify your app on the Play store... for ever! Come up with a different package name and write in the AndroidManifest.xml file in the sketch folder, after the "package=" attribute inside the manifest tag, which also contains version code and name. Once you have done that, try exporting the sketch again.

For more info on distributing apps from Processing,
check this online tutorial. android_mode.dialog.cannot_use_default_icons_title = Cannot export package... +android_mode.dialog.cannot_use_default_icons_title_bundle = Cannot export bundle... android_mode.dialog.cannot_use_default_icons_body = The sketch does not include all required app icons. Processing could use its default set of Android icons, which are okay to test the app on your device, but a bad idea to distribute it on the Play store. Create a full set of unique icons for your app, and copy them into the sketch folder. Once you have done that, try exporting the sketch again.

For more info on distributing apps from Processing,
check this online tutorial. android_mode.warn.cannot_load_sdk_title = Bad news... android_mode.warn.cannot_load_sdk_body = The Android SDK could not be loaded.\nThe Android Mode will be disabled. From 26086b087a9931a4037b2e4e2888da41ec12d883 Mon Sep 17 00:00:00 2001 From: Rupesh Kumar <46577873+rupesh-kumar-lpu@users.noreply.github.com> Date: Sat, 25 Sep 2021 02:04:19 +0530 Subject: [PATCH 9/9] Reverting Changes in Manifest.java --- mode/src/processing/mode/android/Manifest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/src/processing/mode/android/Manifest.java b/mode/src/processing/mode/android/Manifest.java index 58aef8fe..c50075fe 100644 --- a/mode/src/processing/mode/android/Manifest.java +++ b/mode/src/processing/mode/android/Manifest.java @@ -48,7 +48,7 @@ public class Manifest { static final String MANIFEST_ERROR_MESSAGE = "Errors occurred while reading or writing " + MANIFEST_XML + ",\n" + "which means lots of things are likely to stop working properly.\n" + - "To prevent losing any data, it's recommended that you use Save As\n" + + "To prevent losing any data, it's recommended that you use “Save As”\n" + "to save a separate copy of your sketch, and then restart Processing."; static private final String[] MANIFEST_TEMPLATE = {