diff --git a/.github/workflows/scripts-android.yml b/.github/workflows/scripts-android.yml index ca97aaf07a..9a7988ed29 100644 --- a/.github/workflows/scripts-android.yml +++ b/.github/workflows/scripts-android.yml @@ -13,6 +13,7 @@ name: Test Android build scripts - 'scripts/android/lib/**/*.java' - 'scripts/android/tests/**/*.java' - 'scripts/device-runner-app/**/*.java' + - 'scripts/hellocodenameone/**' - 'scripts/android/screenshots/**' - '!scripts/android/screenshots/**/*.md' - 'scripts/templates/**' @@ -39,6 +40,7 @@ name: Test Android build scripts - 'scripts/android/lib/**/*.java' - 'scripts/android/tests/**/*.java' - 'scripts/device-runner-app/**/*.java' + - 'scripts/hellocodenameone/**' - 'scripts/android/screenshots/**' - '!scripts/android/screenshots/**/*.md' - 'scripts/templates/**' diff --git a/.github/workflows/scripts-ios.yml b/.github/workflows/scripts-ios.yml index 642379b45a..f9c2343b6b 100644 --- a/.github/workflows/scripts-ios.yml +++ b/.github/workflows/scripts-ios.yml @@ -8,7 +8,7 @@ on: - 'scripts/build-ios-port.sh' - 'scripts/build-ios-app.sh' - 'scripts/run-ios-ui-tests.sh' - - 'scripts/device-runner-app/**/*.java' + - 'scripts/hellocodenameone/**' - 'scripts/ios/tests/**' - 'scripts/ios/screenshots/**' - 'scripts/templates/**' @@ -30,7 +30,7 @@ on: - 'scripts/build-ios-port.sh' - 'scripts/build-ios-app.sh' - 'scripts/run-ios-ui-tests.sh' - - 'scripts/device-runner-app/**/*.java' + - 'scripts/hellocodenameone/**' - 'scripts/ios/tests/**' - 'scripts/ios/screenshots/**' - 'scripts/templates/**' diff --git a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java index e24c30f36e..152bcc5333 100644 --- a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java +++ b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java @@ -8724,6 +8724,23 @@ public void announceForAccessibility(Component cmp, String text) { // should override this method. } + /** + * Returns the stack trace from the exception on the given + * thread. This API isn't supported on all platforms and may + * return a blank string when unavailable. + * + * @param parentThread the thread in which the exception was thrown + * @param t the exception + * @return a stack trace string that might be blank + */ + public String getStackTrace(Thread parentThread, Throwable t) { + System.out.println("CN1SS:ERR:Invoking getStackTrace in CodenameOneImplementation"); + if (parentThread instanceof CodenameOneThread && ((CodenameOneThread) parentThread).hasStackFrame()) { + return ((CodenameOneThread) parentThread).getStack(t); + } + return ""; + } + class RPush implements Runnable { public void run() { final long pushId = Preferences.get("push_id", (long) -1); diff --git a/CodenameOne/src/com/codename1/ui/Display.java b/CodenameOne/src/com/codename1/ui/Display.java index b0025a08ec..74c5015c93 100644 --- a/CodenameOne/src/com/codename1/ui/Display.java +++ b/CodenameOne/src/com/codename1/ui/Display.java @@ -1118,6 +1118,20 @@ void mainEDTLoop() { INSTANCE.edt = null; } + /** + * Returns the stack trace from the exception on the given + * thread. This API isn't supported on all platforms and may + * return a blank string when unavailable. + * + * @param parentThread the thread in which the exception was thrown + * @param t the exception + * @return a stack trace string that might be blank + */ + public String getStackTrace(Thread parentThread, Throwable t) { + System.out.println("CN1SS:ERR:Invoking getStackTrace in Display"); + return impl.getStackTrace(parentThread, t); + } + /** * Implementation of the event dispatch loop content */ diff --git a/CodenameOne/src/com/codename1/ui/Graphics.java b/CodenameOne/src/com/codename1/ui/Graphics.java index 8be38eff81..4fff76a7d1 100644 --- a/CodenameOne/src/com/codename1/ui/Graphics.java +++ b/CodenameOne/src/com/codename1/ui/Graphics.java @@ -383,6 +383,9 @@ public void fillRect(int x, int y, int width, int height) { impl.fillRect(nativeGraphics, xTranslate + x, yTranslate + y, width, height); } + /** + * @deprecated this method should have been internals + */ public void drawShadow(Image img, int x, int y, int offsetX, int offsetY, int blurRadius, int spreadRadius, int color, float opacity) { impl.drawShadow(nativeGraphics, img.getImage(), xTranslate + x, yTranslate + y, offsetX, offsetY, blurRadius, spreadRadius, color, opacity); } diff --git a/Ports/Android/src/com/codename1/impl/android/AndroidGraphics.java b/Ports/Android/src/com/codename1/impl/android/AndroidGraphics.java index 550da69ec3..a50fa22e22 100644 --- a/Ports/Android/src/com/codename1/impl/android/AndroidGraphics.java +++ b/Ports/Android/src/com/codename1/impl/android/AndroidGraphics.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 Pader-Sync Ltd. & Co. KG. All Rights Reserved. + * Copyright 2009 Pader-Sync Ltd. & Co. KG. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -144,18 +144,18 @@ public void drawImage(Object img, int x, int y) { } public void drawShadow(Object image, int x, int y, int offsetX, int offsetY, int blurRadius, int spreadRadius, int color, float opacity) { - if (image == null) return; + if (image == null || canvas == null) { + return; + } Bitmap bmp = (Bitmap)image; float bmpW = bmp.getWidth(); float bmpH = bmp.getHeight(); - if (bmpW == 0 || bmpH == 0) return; - - + if (bmpW == 0 || bmpH == 0) { + return; + } Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth() + 2 * spreadRadius, bmp.getHeight() + 2 * spreadRadius, false); - Paint alphaPaint = new Paint(); - alphaPaint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL)); int[] offsetXY = new int[2]; Bitmap bmAlpha = scaledBmp.extractAlpha(alphaPaint, offsetXY); @@ -164,13 +164,11 @@ public void drawShadow(Object image, int x, int y, int offsetX, int offsetY, int applyTransform(); Paint shadowPaint = new Paint(); int alpha = (int)Math.floor(opacity * 255); - shadowPaint.setColor((0xff000000 | color)); - shadowPaint.setAlpha((int)Math.floor(opacity * 255)); - + shadowPaint.setColor(0xff000000 | color); + shadowPaint.setAlpha(alpha); canvas.drawBitmap(bmAlpha, x + offsetXY[0] - spreadRadius + offsetX, y + offsetXY[1] - spreadRadius + offsetY, shadowPaint); bmAlpha.recycle(); - unapplyTransform(); canvas.restore(); } diff --git a/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java b/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java index e7040dae0a..d110efb0a9 100644 --- a/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java +++ b/Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java @@ -39,9 +39,9 @@ import android.webkit.CookieSyncManager; import android.content.*; -import android.content.pm.*; -import android.content.res.AssetFileDescriptor; -import android.content.res.Configuration; +import android.content.pm.*; +import android.content.res.AssetFileDescriptor; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -544,7 +544,15 @@ private static Map splitQuery(String urlencodeQueryString) { return out; } - + public String getStackTrace(Thread parentThread, Throwable t) { + System.out.println("CN1SS:ERR:Invoking getStackTrace in AndroidImplementation"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintWriter w = new PrintWriter(bos); + t.printStackTrace(w); + w.close(); + System.out.println("CN1SS:ERR:AndroidImplementation getStackTrace completed"); + return bos.toString(); + } public static void initPushContent(String message, String image, String messageType, String category, Context context) { com.codename1.push.PushContent.reset(); @@ -3604,70 +3612,70 @@ public Media createMedia(final String uri, boolean isVideo, final Runnable onCom if (getActivity() == null) { return null; } - if (uri.startsWith("file://")) { - return createMedia(removeFilePrefix(uri), isVideo, onCompletion); - } - File file = null; - if (uri.indexOf(':') < 0) { - // use a file object to play to try and workaround this issue: - // http://code.google.com/p/android/issues/detail?id=4124 - file = new File(uri); - } - - Uri parsedUri = null; - boolean isContentUri = false; - if (file == null) { - parsedUri = Uri.parse(uri); - isContentUri = parsedUri != null && "content".equalsIgnoreCase(parsedUri.getScheme()); - } - - // The document picker grants temporary permissions for content URIs. Requesting - // READ_EXTERNAL_STORAGE again would surface a redundant prompt on Android 13+, so we only - // ask for classic file paths that require the legacy permission. MediaStore URIs still - // require an explicit permission grant, so they remain subject to the legacy check even - // though they also use the content:// scheme. - boolean requiresLegacyPermission = !uri.startsWith(FileSystemStorage.getInstance().getAppHomePath()); - if (isContentUri && parsedUri != null) { - String authority = parsedUri.getAuthority(); - if (authority != null) { - authority = authority.toLowerCase(); - if (!"media".equals(authority) && !authority.startsWith("media.")) { - if (!"com.android.providers.media.documents".equals(authority)) { - requiresLegacyPermission = false; - } - } - } else { - requiresLegacyPermission = false; - } - } - - if(requiresLegacyPermission) { - if(!PermissionsHelper.checkForPermission(isVideo ? DevicePermission.PERMISSION_READ_VIDEO : DevicePermission.PERMISSION_READ_AUDIO, "This is required to play media")){ - return null; - } - } - - Media retVal; - - if (isVideo) { - final AndroidImplementation.Video[] video = new AndroidImplementation.Video[1]; - final boolean[] flag = new boolean[1]; - final File f = file; - final Uri videoUri = parsedUri; - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - VideoView v = new VideoView(getActivity()); - v.setZOrderMediaOverlay(true); - if (f != null) { - v.setVideoURI(Uri.fromFile(f)); - } else { - v.setVideoURI(videoUri != null ? videoUri : Uri.parse(uri)); - } - video[0] = new AndroidImplementation.Video(v, getActivity(), onCompletion); - flag[0] = true; - synchronized (flag) { - flag.notify(); + if (uri.startsWith("file://")) { + return createMedia(removeFilePrefix(uri), isVideo, onCompletion); + } + File file = null; + if (uri.indexOf(':') < 0) { + // use a file object to play to try and workaround this issue: + // http://code.google.com/p/android/issues/detail?id=4124 + file = new File(uri); + } + + Uri parsedUri = null; + boolean isContentUri = false; + if (file == null) { + parsedUri = Uri.parse(uri); + isContentUri = parsedUri != null && "content".equalsIgnoreCase(parsedUri.getScheme()); + } + + // The document picker grants temporary permissions for content URIs. Requesting + // READ_EXTERNAL_STORAGE again would surface a redundant prompt on Android 13+, so we only + // ask for classic file paths that require the legacy permission. MediaStore URIs still + // require an explicit permission grant, so they remain subject to the legacy check even + // though they also use the content:// scheme. + boolean requiresLegacyPermission = !uri.startsWith(FileSystemStorage.getInstance().getAppHomePath()); + if (isContentUri && parsedUri != null) { + String authority = parsedUri.getAuthority(); + if (authority != null) { + authority = authority.toLowerCase(); + if (!"media".equals(authority) && !authority.startsWith("media.")) { + if (!"com.android.providers.media.documents".equals(authority)) { + requiresLegacyPermission = false; + } + } + } else { + requiresLegacyPermission = false; + } + } + + if(requiresLegacyPermission) { + if(!PermissionsHelper.checkForPermission(isVideo ? DevicePermission.PERMISSION_READ_VIDEO : DevicePermission.PERMISSION_READ_AUDIO, "This is required to play media")){ + return null; + } + } + + Media retVal; + + if (isVideo) { + final AndroidImplementation.Video[] video = new AndroidImplementation.Video[1]; + final boolean[] flag = new boolean[1]; + final File f = file; + final Uri videoUri = parsedUri; + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + VideoView v = new VideoView(getActivity()); + v.setZOrderMediaOverlay(true); + if (f != null) { + v.setVideoURI(Uri.fromFile(f)); + } else { + v.setVideoURI(videoUri != null ? videoUri : Uri.parse(uri)); + } + video[0] = new AndroidImplementation.Video(v, getActivity(), onCompletion); + flag[0] = true; + synchronized (flag) { + flag.notify(); } } }); @@ -3682,47 +3690,47 @@ public void run() { return video[0]; } else { MediaPlayer player; - if (file != null) { - FileInputStream is = new FileInputStream(file); - player = new MediaPlayer(); - player.setDataSource(is.getFD()); - player.prepare(); - } else { - player = MediaPlayer.create(getActivity(), parsedUri != null ? parsedUri : Uri.parse(uri)); - if (player == null && isContentUri) { - // Android 13+ introduces stricter access rules for content:// URIs returned - // from the system document picker. The picker grants our activity a - // persistable read permission, but some OEM builds still reject the URI when it - // is passed directly to MediaPlayer. Opening the descriptor ourselves keeps the - // same permission grant while avoiding the OEM bug. - ContentResolver resolver = getContext().getContentResolver(); - if (resolver != null && parsedUri != null) { - AssetFileDescriptor afd = null; - try { - afd = resolver.openAssetFileDescriptor(parsedUri, "r"); - if (afd != null) { - player = new MediaPlayer(); - player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); - player.prepare(); - } - } finally { - if (afd != null) { - try { - afd.close(); - } catch (IOException ignore) { - } - } - } - } - } - } - if (player == null) { - throw new IOException("Unable to create media player for uri " + uri); - } - retVal = new Audio(getActivity(), player, null, onCompletion); - } - return retVal; - } + if (file != null) { + FileInputStream is = new FileInputStream(file); + player = new MediaPlayer(); + player.setDataSource(is.getFD()); + player.prepare(); + } else { + player = MediaPlayer.create(getActivity(), parsedUri != null ? parsedUri : Uri.parse(uri)); + if (player == null && isContentUri) { + // Android 13+ introduces stricter access rules for content:// URIs returned + // from the system document picker. The picker grants our activity a + // persistable read permission, but some OEM builds still reject the URI when it + // is passed directly to MediaPlayer. Opening the descriptor ourselves keeps the + // same permission grant while avoiding the OEM bug. + ContentResolver resolver = getContext().getContentResolver(); + if (resolver != null && parsedUri != null) { + AssetFileDescriptor afd = null; + try { + afd = resolver.openAssetFileDescriptor(parsedUri, "r"); + if (afd != null) { + player = new MediaPlayer(); + player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + player.prepare(); + } + } finally { + if (afd != null) { + try { + afd.close(); + } catch (IOException ignore) { + } + } + } + } + } + } + if (player == null) { + throw new IOException("Unable to create media player for uri " + uri); + } + retVal = new Audio(getActivity(), player, null, onCompletion); + } + return retVal; + } @Override public void addCompletionHandler(Media media, Runnable onCompletion) { @@ -8131,17 +8139,17 @@ private String getImageFilePath(Uri uri) { @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { - if (requestCode == ZOOZ_PAYMENT) { - ((IntentResultListener) pur).onActivityResult(requestCode, resultCode, intent); - return; - } - - takePersistablePermissionsFromIntent(intent); - - if (requestCode == REQUEST_SELECT_FILE || requestCode == FILECHOOSER_RESULTCODE) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (requestCode == REQUEST_SELECT_FILE) { - if (uploadMessage == null) return; + if (requestCode == ZOOZ_PAYMENT) { + ((IntentResultListener) pur).onActivityResult(requestCode, resultCode, intent); + return; + } + + takePersistablePermissionsFromIntent(intent); + + if (requestCode == REQUEST_SELECT_FILE || requestCode == FILECHOOSER_RESULTCODE) { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (requestCode == REQUEST_SELECT_FILE) { + if (uploadMessage == null) return; Uri[] results = null; // Check that the response is a good one @@ -8679,92 +8687,92 @@ public void openGallery(final ActionListener response, int type){ callback = new EventDispatcher(); callback.addListener(response); - Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI); - galleryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (multi) { - galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } + Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI); + galleryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (multi) { + galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } if(type == Display.GALLERY_VIDEO){ galleryIntent.setType("video/*"); }else if(type == Display.GALLERY_IMAGE){ galleryIntent.setType("image/*"); }else if(type == Display.GALLERY_ALL){ galleryIntent.setType("image/* video/*"); - }else if (type == -9999) { - galleryIntent = new Intent(); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - galleryIntent.setAction(Intent.ACTION_OPEN_DOCUMENT); - } else { - galleryIntent.setAction(Intent.ACTION_GET_CONTENT); - } - galleryIntent.addCategory(Intent.CATEGORY_OPENABLE); - galleryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - galleryIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); - } - - // set MIME type for image - galleryIntent.setType("*/*"); - galleryIntent.putExtra(Intent.EXTRA_MIME_TYPES, Display.getInstance().getProperty("android.openGallery.accept", "*/*").split(",")); - }else{ + }else if (type == -9999) { + galleryIntent = new Intent(); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + galleryIntent.setAction(Intent.ACTION_OPEN_DOCUMENT); + } else { + galleryIntent.setAction(Intent.ACTION_GET_CONTENT); + } + galleryIntent.addCategory(Intent.CATEGORY_OPENABLE); + galleryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + galleryIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + } + + // set MIME type for image + galleryIntent.setType("*/*"); + galleryIntent.putExtra(Intent.EXTRA_MIME_TYPES, Display.getInstance().getProperty("android.openGallery.accept", "*/*").split(",")); + }else{ galleryIntent.setType("*/*"); } this.getActivity().startActivityForResult(galleryIntent, multi ? OPEN_GALLERY_MULTI: OPEN_GALLERY); } - class NativeImage extends Image { - - public NativeImage(Bitmap nativeImage) { - super(nativeImage); - } - } - - /** - * Persist read permissions that were granted by an activity result so that media playback can - * continue after {@link Activity#onActivityResult(int, int, Intent)} returns. - * - *

Android 13 and newer revoke temporary grants immediately after the callback unless the - * app calls {@link ContentResolver#takePersistableUriPermission(Uri, int)}. Without this call - * {@link #createMedia(String, boolean, Runnable)} loses access to the {@code content://} URI - * provided by the system picker and playback fails on Android 15.

- */ - private void takePersistablePermissionsFromIntent(Intent intent) { - if (intent == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - return; - } - int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (takeFlags == 0) { - return; - } - ContentResolver resolver = getContext().getContentResolver(); - if (resolver == null) { - return; - } - ClipData clip = intent.getClipData(); - if (clip != null) { - for (int i = 0; i < clip.getItemCount(); i++) { - Uri uri = clip.getItemAt(i).getUri(); - if (uri != null) { - try { - resolver.takePersistableUriPermission(uri, takeFlags); - } catch (SecurityException ignored) { - } - } - } - } - Uri dataUri = intent.getData(); - if (dataUri != null) { - try { - resolver.takePersistableUriPermission(dataUri, takeFlags); - } catch (SecurityException ignored) { - } - } - } - - /** - * Create a File for saving an image or video - */ - private File getOutputMediaFile(boolean isVideo) { + class NativeImage extends Image { + + public NativeImage(Bitmap nativeImage) { + super(nativeImage); + } + } + + /** + * Persist read permissions that were granted by an activity result so that media playback can + * continue after {@link Activity#onActivityResult(int, int, Intent)} returns. + * + *

Android 13 and newer revoke temporary grants immediately after the callback unless the + * app calls {@link ContentResolver#takePersistableUriPermission(Uri, int)}. Without this call + * {@link #createMedia(String, boolean, Runnable)} loses access to the {@code content://} URI + * provided by the system picker and playback fails on Android 15.

+ */ + private void takePersistablePermissionsFromIntent(Intent intent) { + if (intent == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + if (takeFlags == 0) { + return; + } + ContentResolver resolver = getContext().getContentResolver(); + if (resolver == null) { + return; + } + ClipData clip = intent.getClipData(); + if (clip != null) { + for (int i = 0; i < clip.getItemCount(); i++) { + Uri uri = clip.getItemAt(i).getUri(); + if (uri != null) { + try { + resolver.takePersistableUriPermission(uri, takeFlags); + } catch (SecurityException ignored) { + } + } + } + } + Uri dataUri = intent.getData(); + if (dataUri != null) { + try { + resolver.takePersistableUriPermission(dataUri, takeFlags); + } catch (SecurityException ignored) { + } + } + } + + /** + * Create a File for saving an image or video + */ + private File getOutputMediaFile(boolean isVideo) { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. if (getActivity() != null) { diff --git a/scripts/android/screenshots/GraphicsPipeline.png b/scripts/android/screenshots/GraphicsPipeline.png new file mode 100644 index 0000000000..9e8cc9736c Binary files /dev/null and b/scripts/android/screenshots/GraphicsPipeline.png differ diff --git a/scripts/android/screenshots/GraphicsShapesAndGradients.png b/scripts/android/screenshots/GraphicsShapesAndGradients.png new file mode 100644 index 0000000000..7d9eb277a4 Binary files /dev/null and b/scripts/android/screenshots/GraphicsShapesAndGradients.png differ diff --git a/scripts/android/screenshots/GraphicsStateAndText.png b/scripts/android/screenshots/GraphicsStateAndText.png new file mode 100644 index 0000000000..248055fb78 Binary files /dev/null and b/scripts/android/screenshots/GraphicsStateAndText.png differ diff --git a/scripts/android/screenshots/GraphicsTransformations.png b/scripts/android/screenshots/GraphicsTransformations.png new file mode 100644 index 0000000000..f7003a4a9b Binary files /dev/null and b/scripts/android/screenshots/GraphicsTransformations.png differ diff --git a/scripts/android/screenshots/MediaPlayback.png b/scripts/android/screenshots/MediaPlayback.png new file mode 100644 index 0000000000..a7519da333 Binary files /dev/null and b/scripts/android/screenshots/MediaPlayback.png differ diff --git a/scripts/android/tests/Cn1ssChunkTools.java b/scripts/android/tests/Cn1ssChunkTools.java index 7fd03c0ab8..fff7de4b69 100644 --- a/scripts/android/tests/Cn1ssChunkTools.java +++ b/scripts/android/tests/Cn1ssChunkTools.java @@ -26,6 +26,7 @@ public static void main(String[] args) throws Exception { case "count" -> runCount(slice(args, 1)); case "extract" -> runExtract(slice(args, 1)); case "tests" -> runTests(slice(args, 1)); + case "check" -> runCheck(slice(args, 1)); default -> { usage(); System.exit(2); @@ -33,6 +34,23 @@ public static void main(String[] args) throws Exception { } } + private static void runCheck(String[] args) throws IOException { + boolean error = false; + Path path = Path.of(args[0]); + String text = Files.readString(path, StandardCharsets.UTF_8); + String[] lines = text.split("\r?\n"); + for(String line : lines) { + Matcher matcher = CHUNK_PATTERN.matcher(line); + if(line.contains("CN1SS:") && !matcher.find()) { + error = error || line.indexOf(":ERR:") > -1; + System.out.println(line); + } + } + if(error) { + System.exit(1); + } + } + private static void runCount(String[] args) throws IOException { Path path = null; String test = null; @@ -168,7 +186,7 @@ private static Iterable iterateChunks(Path path, Optional testFil private static void usage() { System.err.println("Usage: java Cn1ssChunkTools.java [options]"); - System.err.println("Commands: count, extract, tests"); + System.err.println("Commands: count, extract, tests, check"); } private static String[] slice(String[] args, int from) { diff --git a/scripts/device-runner-app/androidTest/DeviceRunnerInstrumentationTest.java b/scripts/device-runner-app/androidTest/DeviceRunnerInstrumentationTest.java index b884e7f64a..e510d7975f 100644 --- a/scripts/device-runner-app/androidTest/DeviceRunnerInstrumentationTest.java +++ b/scripts/device-runner-app/androidTest/DeviceRunnerInstrumentationTest.java @@ -38,7 +38,7 @@ public void launchMainActivityAndWaitForDeviceRunner() throws Exception { } private boolean waitForDeviceRunner() throws Exception { - final long timeoutMs = 300_000L; + final long timeoutMs = 900_000L; final String endMarker = "CN1SS:SUITE:FINISHED"; long deadline = System.currentTimeMillis() + timeoutMs; diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/HelloCodenameOne.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/HelloCodenameOne.java index 6a85771ef8..82cf0b004e 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/HelloCodenameOne.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/HelloCodenameOne.java @@ -31,7 +31,7 @@ public void start() { Form c = CN.getCurrentForm(); if (!deviceRunnerExecuted || c == null) { deviceRunnerExecuted = true; - CN.callSerially(() -> new Cn1ssDeviceRunner().runSuite()); + new Thread(() -> new Cn1ssDeviceRunner().runSuite()).start(); return; } c.show(); diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/AbstractGraphicsScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/AbstractGraphicsScreenshotTest.java index 597d378b09..24fd13c419 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/AbstractGraphicsScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/AbstractGraphicsScreenshotTest.java @@ -10,10 +10,10 @@ abstract class AbstractGraphicsScreenshotTest extends BaseTest { protected abstract String screenshotName(); @Override - public boolean runTest() throws Exception { + public boolean runTest() { Form form = createForm("Graphics", new BorderLayout(), screenshotName()); form.add(BorderLayout.CENTER, createContent()); form.show(); - return waitForDone(); + return true; } } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BaseTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BaseTest.java index d8e4b76ca9..e92ae7707a 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BaseTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BaseTest.java @@ -7,7 +7,7 @@ import com.codename1.testing.TestUtils; public abstract class BaseTest extends AbstractTest { - private boolean done; + private volatile boolean done; protected Form createForm(String title, Layout layout, final String imageName) { return new Form(title, layout) { @@ -15,7 +15,7 @@ protected Form createForm(String title, Layout layout, final String imageName) { protected void onShowCompleted() { registerReadyCallback(this, () -> { Cn1ssDeviceRunnerHelper.emitCurrentFormScreenshot(imageName); - done = true; + done(); }); } }; @@ -26,17 +26,11 @@ protected void registerReadyCallback(Form parent, Runnable run) { UITimer.timer(1500, false, parent, run); } - protected boolean waitForDone() { - int timeout = 100; - while(!done) { - TestUtils.waitFor(20); - timeout--; - if(timeout == 0) { - return false; - } - } - // give the test a few additional milliseconds for the screenshot emission - TestUtils.waitFor(100); - return true; + protected synchronized void done() { + this.done = true; + } + + public synchronized boolean isDone() { + return done; } } \ No newline at end of file diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BrowserComponentScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BrowserComponentScreenshotTest.java index 01c4d722a9..cacb198197 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BrowserComponentScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/BrowserComponentScreenshotTest.java @@ -1,17 +1,17 @@ package com.codenameone.examples.hellocodenameone.tests; -import com.codename1.testing.AbstractTest; -import com.codename1.testing.TestUtils; import com.codename1.ui.BrowserComponent; import com.codename1.ui.Form; import com.codename1.ui.CN; import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.util.UITimer; public class BrowserComponentScreenshotTest extends BaseTest { private BrowserComponent browser; @Override public boolean runTest() throws Exception { if (!BrowserComponent.isNativeBrowserSupported()) { + done(); return true; } Form form = createForm("Browser Test", new BorderLayout(), "BrowserComponent"); @@ -19,11 +19,12 @@ public boolean runTest() throws Exception { browser.setPage(buildHtml(), null); form.add(BorderLayout.CENTER, browser); form.show(); - return waitForDone(); + return true; } protected void registerReadyCallback(Form parent, final Runnable run) { - browser.addWebEventListener(BrowserComponent.onLoad, evt -> CN.callSerially(run)); + browser.addWebEventListener(BrowserComponent.onLoad, evt -> + UITimer.timer(200, false, parent, run)); } private static String buildHtml() { diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index e68dcff811..05971ef04c 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -1,13 +1,20 @@ package com.codenameone.examples.hellocodenameone.tests; +import com.codename1.impl.CodenameOneThread; +import com.codename1.io.Log; +import com.codename1.io.Util; import com.codename1.testing.DeviceRunner; import com.codename1.testing.TestReporting; +import com.codename1.ui.CN; import com.codename1.ui.Display; import com.codename1.ui.Form; import com.codename1.testing.AbstractTest; +import com.codename1.util.StringUtil; + +import java.util.List; public final class Cn1ssDeviceRunner extends DeviceRunner { - private static final AbstractTest[] TEST_CLASSES = new AbstractTest[] { + private static final BaseTest[] TEST_CLASSES = new BaseTest[] { new MainScreenScreenshotTest(), new GraphicsPipelineScreenshotTest(), new GraphicsShapesAndGradientsScreenshotTest(), @@ -18,17 +25,40 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { }; public void runSuite() { - for (AbstractTest testClass : TEST_CLASSES) { - log("CN1SS:INFO:suite starting test=" + testClass); - try { - testClass.prepare(); - testClass.runTest(); - testClass.cleanup(); - log("CN1SS:INFO:suite finished test=" + testClass); - } catch (Throwable t) { - log("CN1SS:ERR:suite test=" + testClass + " failed=" + t); - t.printStackTrace(); + CN.callSerially(() -> { + Display.getInstance().addEdtErrorHandler(e -> { + log("CN1SS:ERR:exception caught in EDT " + e.getSource()); + String stack = Display.getInstance().getStackTrace(Thread.currentThread(), (Throwable)e.getSource()); + log("CN1SS:ERR:exception stack of length: " + stack.length()); + for(String s : StringUtil.tokenize(stack, '\n')) { + if(s.length() > 200) { + s = s.substring(0, 200); + } + log("CN1SS:ERR:Stack:" + s); + } + }); + }); + for (BaseTest testClass : TEST_CLASSES) { + CN.callSerially(() -> { + log("CN1SS:INFO:suite starting test=" + testClass); + try { + testClass.prepare(); + testClass.runTest(); + } catch (Throwable t) { + log("CN1SS:ERR:suite test=" + testClass + " failed=" + t); + t.printStackTrace(); + } + }); + int timeout = 9000; + while (!testClass.isDone() && timeout > 0) { + Util.sleep(3); + timeout--; + } + testClass.cleanup(); + if(timeout == 0) { + log("CN1SS:ERR:suite test=" + testClass + " failed due to timeout waiting for DONE"); } + log("CN1SS:INFO:suite finished test=" + testClass); } log("CN1SS:SUITE:FINISHED"); TestReporting.getInstance().testExecutionFinished(getClass().getName()); @@ -48,7 +78,6 @@ protected void startApplicationInstance() { new Form().show(); } }); - Cn1ssDeviceRunnerHelper.waitForMillis(200); } @Override @@ -60,6 +89,5 @@ protected void stopApplicationInstance() { current.revalidate(); } }); - Cn1ssDeviceRunnerHelper.waitForMillis(200); } } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunnerHelper.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunnerHelper.java index 409ce0f1fc..54b570843e 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunnerHelper.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunnerHelper.java @@ -29,18 +29,12 @@ static void runOnEdtSync(Runnable runnable) { } } - static void waitForMillis(long millis) { - int duration = (int) Math.max(1, Math.min(Integer.MAX_VALUE, millis)); - Util.sleep(duration); - } - - static boolean emitCurrentFormScreenshot(String testName) { + static void emitCurrentFormScreenshot(String testName) { String safeName = sanitizeTestName(testName); Form current = Display.getInstance().getCurrent(); if (current == null) { println("CN1SS:ERR:test=" + safeName + " message=Current form is null"); println("CN1SS:END:" + safeName); - return false; } int width = Math.max(1, current.getWidth()); int height = Math.max(1, current.getHeight()); @@ -59,7 +53,6 @@ static boolean emitCurrentFormScreenshot(String testName) { if (img[0] == null) { println("CN1SS:ERR:test=" + safeName + " message=Screenshot process timed out"); println("CN1SS:END:" + safeName); - return false; } Image screenshot = img[0]; try { @@ -67,7 +60,6 @@ static boolean emitCurrentFormScreenshot(String testName) { if (io == null || !io.isFormatSupported(ImageIO.FORMAT_PNG)) { println("CN1SS:ERR:test=" + safeName + " message=PNG encoding unavailable"); println("CN1SS:END:" + safeName); - return false; } ByteArrayOutputStream pngOut = new ByteArrayOutputStream(Math.max(1024, width * height / 2)); io.save(screenshot, pngOut, ImageIO.FORMAT_PNG, 1f); @@ -81,12 +73,10 @@ static boolean emitCurrentFormScreenshot(String testName) { } else { println("CN1SS:INFO:test=" + safeName + " preview_jpeg_bytes=0 preview_quality=0"); } - return true; } catch (IOException ex) { println("CN1SS:ERR:test=" + safeName + " message=" + ex); Log.e(ex); println("CN1SS:END:" + safeName); - return false; } finally { screenshot.dispose(); } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsPipelineScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsPipelineScreenshotTest.java index 0969c8207f..aedc948376 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsPipelineScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsPipelineScreenshotTest.java @@ -14,7 +14,7 @@ public boolean runTest() throws Exception { Form form = createForm("Graphics Pipeline", new BorderLayout(), "GraphicsPipeline"); form.add(BorderLayout.CENTER, new GraphicsShowcase()); form.show(); - return waitForDone(); + return true; } private static final class GraphicsShowcase extends Component { diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsStateAndTextScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsStateAndTextScreenshotTest.java index b50946311d..6c599c8de3 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsStateAndTextScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/GraphicsStateAndTextScreenshotTest.java @@ -155,11 +155,11 @@ private void paintTextAndFonts(Graphics g, int x, int y, int w, int h) { g.setColor(0x475569); g.drawRect(x, y, w - 1, h - 1); - Font original = g.getFont(); - g.setFont(original.derive(Font.STYLE_BOLD, original.getHeight() + 2)); + Font nativeFont = Font.createTrueTypeFont("native:MainLight"); + g.setFont(nativeFont.derive(Font.STYLE_BOLD, Font.getDefaultFont().getHeight() + 2)); g.setColor(0x22c55e); g.drawString("drawString", x + 10, y + 10, Style.TEXT_DECORATION_UNDERLINE); - g.setFont(original); + g.setFont(nativeFont); g.setColor(0xf97316); g.drawStringBaseline("baseline", x + 10, y + 42); diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MainScreenScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MainScreenScreenshotTest.java index 29cf44dc40..5ff782ebc1 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MainScreenScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MainScreenScreenshotTest.java @@ -30,6 +30,6 @@ public boolean runTest() throws Exception { form.add(BorderLayout.CENTER, content); form.show(); - return waitForDone(); + return true; } } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MediaPlaybackScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MediaPlaybackScreenshotTest.java index 477a0cb61f..717e67cdd6 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MediaPlaybackScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/MediaPlaybackScreenshotTest.java @@ -11,6 +11,7 @@ import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -23,15 +24,11 @@ public class MediaPlaybackScreenshotTest extends BaseTest { @Override public boolean runTest() throws Exception { Form form = createForm("Media Playback", new BorderLayout(), "MediaPlayback"); - String tonePath = writeToneWav(); final Label statusLabel = new Label("Preparing media sample…"); - if (tonePath == null) { - updateStatus(statusLabel, form, "Failed to generate tone file"); + Media media = MediaManager.createMedia(new ByteArrayInputStream(buildToneWav()), "audio/wav"); + if (media == null) { + updateStatus(statusLabel, form, "Media creation returned null"); } else { - Media media = MediaManager.createMedia(tonePath, false); - if (media == null) { - updateStatus(statusLabel, form, "Media creation returned null"); - } media.setTime(0); media.play(); statusLabel.setText("Starting playback…"); @@ -45,8 +42,7 @@ public boolean runTest() throws Exception { form.show(); - Cn1ssDeviceRunnerHelper.waitForMillis(800); - return waitForDone(); + return true; } private static void updateStatus(Label label, Form form, String message) { @@ -58,38 +54,6 @@ private static void updateStatus(Label label, Form form, String message) { }); } - private static void cleanupMedia(Media media) { - if (media == null) { - return; - } - Cn1ssDeviceRunnerHelper.runOnEdtSync(media::cleanup); - } - - private static String writeToneWav() { - byte[] wav = buildToneWav(); - if (wav == null || wav.length == 0) { - return null; - } - String path = FileSystemStorage.getInstance().getAppHomePath() + "media-playback-test.wav"; - FileSystemStorage.getInstance().delete(path); - OutputStream out = null; - try { - out = FileSystemStorage.getInstance().openOutputStream(path); - out.write(wav); - return path; - } catch (IOException ex) { - TestUtils.log("Unable to write tone wav: " + ex.getMessage()); - return null; - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException ignored) { - } - } - } - } - private static byte[] buildToneWav() { int totalSamples = (int) (SAMPLE_RATE * TONE_DURATION_SECONDS); int bytesPerSample = 2; diff --git a/scripts/hellocodenameone/pom.xml b/scripts/hellocodenameone/pom.xml index 2c9bc5d48a..04b3e490c5 100644 --- a/scripts/hellocodenameone/pom.xml +++ b/scripts/hellocodenameone/pom.xml @@ -19,7 +19,7 @@ common - 7.0.212 + 8.0-SNAPSHOT 8.0-SNAPSHOT UTF-8 1.8 diff --git a/scripts/ios/screenshots/BrowserComponent.png b/scripts/ios/screenshots/BrowserComponent.png index 7e8a58a6c1..a93abafe11 100644 Binary files a/scripts/ios/screenshots/BrowserComponent.png and b/scripts/ios/screenshots/BrowserComponent.png differ diff --git a/scripts/ios/screenshots/GraphicsPipeline.png b/scripts/ios/screenshots/GraphicsPipeline.png new file mode 100644 index 0000000000..ed0c2411cc Binary files /dev/null and b/scripts/ios/screenshots/GraphicsPipeline.png differ diff --git a/scripts/ios/screenshots/GraphicsShapesAndGradients.png b/scripts/ios/screenshots/GraphicsShapesAndGradients.png new file mode 100644 index 0000000000..b18bb997fc Binary files /dev/null and b/scripts/ios/screenshots/GraphicsShapesAndGradients.png differ diff --git a/scripts/ios/screenshots/GraphicsStateAndText.png b/scripts/ios/screenshots/GraphicsStateAndText.png new file mode 100644 index 0000000000..3eee731d81 Binary files /dev/null and b/scripts/ios/screenshots/GraphicsStateAndText.png differ diff --git a/scripts/ios/screenshots/GraphicsTransformations.png b/scripts/ios/screenshots/GraphicsTransformations.png new file mode 100644 index 0000000000..2106a05e75 Binary files /dev/null and b/scripts/ios/screenshots/GraphicsTransformations.png differ diff --git a/scripts/ios/screenshots/MainActivity.png b/scripts/ios/screenshots/MainActivity.png index f3d0396afa..2dfaed76b2 100644 Binary files a/scripts/ios/screenshots/MainActivity.png and b/scripts/ios/screenshots/MainActivity.png differ diff --git a/scripts/ios/screenshots/MediaPlayback.png b/scripts/ios/screenshots/MediaPlayback.png new file mode 100644 index 0000000000..07f3da3ef8 Binary files /dev/null and b/scripts/ios/screenshots/MediaPlayback.png differ diff --git a/scripts/lib/cn1ss.sh b/scripts/lib/cn1ss.sh index ca68390dee..f1e9e99881 100644 --- a/scripts/lib/cn1ss.sh +++ b/scripts/lib/cn1ss.sh @@ -135,7 +135,7 @@ cn1ss_count_chunks() { if [ -n "$channel" ]; then args+=("--channel" "$channel") fi - cn1ss_java_run "$CN1SS_MAIN_CLASS" "${args[@]}" 2>/dev/null || echo 0 + cn1ss_java_run "$CN1SS_MAIN_CLASS" "${args[@]}" } cn1ss_extract_base64() { @@ -180,6 +180,14 @@ cn1ss_list_tests() { cn1ss_java_run "$CN1SS_MAIN_CLASS" tests "$file" } +cn1ss_print_log() { + local file="$1" + if [ -z "$file" ] || [ ! -r "$file" ]; then + return 1 + fi + cn1ss_java_run "$CN1SS_MAIN_CLASS" check "$file" +} + cn1ss_verify_png() { local file="$1" [ -s "$file" ] || return 1 diff --git a/scripts/run-android-instrumentation-tests.sh b/scripts/run-android-instrumentation-tests.sh index 800d896ed5..c6a278163d 100755 --- a/scripts/run-android-instrumentation-tests.sh +++ b/scripts/run-android-instrumentation-tests.sh @@ -10,7 +10,6 @@ ensure_dir() { mkdir -p "$1" 2>/dev/null || true; } # CN1SS helpers are implemented in Java for easier maintenance CN1SS_MAIN_CLASS="Cn1ssChunkTools" -POST_COMMENT_CLASS="PostPrComment" PROCESS_SCREENSHOTS_CLASS="ProcessScreenshots" RENDER_SCREENSHOT_REPORT_CLASS="RenderScreenshotReport" @@ -113,7 +112,7 @@ LOGCAT_PID=$! sleep 2 GRADLEW="./gradlew" -GRADLE_CMD=("$GRADLEW" --no-daemon connectedDebugAndroidTest) +GRADLE_CMD=("$GRADLEW" --stacktrace --info --no-daemon connectedDebugAndroidTest) ra_log "Executing connectedDebugAndroidTest via Gradle" if ! ( @@ -191,6 +190,8 @@ declare -A PREVIEW_OUTPUTS=() ensure_dir "$SCREENSHOT_PREVIEW_DIR" +cn1ss_print_log "$TEST_LOG" + for test in "${TEST_NAMES[@]}"; do dest="$SCREENSHOT_TMP_DIR/${test}.png" if source_label="$(cn1ss_decode_test_png "$test" "$dest" "${CN1SS_SOURCES[@]}")"; then diff --git a/scripts/setup-workspace.sh b/scripts/setup-workspace.sh index bdb2a10a8b..d0384a0465 100755 --- a/scripts/setup-workspace.sh +++ b/scripts/setup-workspace.sh @@ -228,6 +228,12 @@ fi log "Building Codename One core modules" "$MAVEN_HOME/bin/mvn" -f maven/pom.xml -DskipTests -Djava.awt.headless=true -Dcn1.binaries="$CN1_BINARIES" -Dcodename1.platform=javase -P local-dev-javase,compile-android install "$@" +log "Building Codename One Maven plugin" +"$MAVEN_HOME/bin/mvn" -f maven/pom.xml \ + -pl codenameone-maven-plugin -am \ + -DskipTests -Djava.awt.headless=true \ + install "$@" + BUILD_CLIENT="$HOME/.codenameone/CodeNameOneBuildClient.jar" log "Ensuring CodeNameOneBuildClient.jar is installed" if [ ! -f "$BUILD_CLIENT" ]; then