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 5f458c33e0..197fbb6126 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 @@ -68,6 +68,9 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new TransformCamera(), new BrowserComponentScreenshotTest(), new MediaPlaybackScreenshotTest(), + new ContactsTest(), + new VideoPlaybackTest(), + new SocketTest(), new OrientationLockScreenshotTest(), new InPlaceEditViewTest(), new AccessibilityTest())); diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ContactsTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ContactsTest.java new file mode 100644 index 0000000000..05111a915a --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ContactsTest.java @@ -0,0 +1,63 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.contacts.Contact; +import com.codename1.contacts.ContactsManager; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.util.UITimer; + +public class ContactsTest extends BaseTest { + @Override + public boolean shouldTakeScreenshot() { + return false; + } + + @Override + public boolean runTest() throws Exception { + Form form = new Form("Contacts", new BorderLayout()); + Label status = new Label("Reading contacts..."); + form.add(BorderLayout.CENTER, status); + form.show(); + + // Attempt to get contacts. + // On Android, this might trigger a blocking permission dialog that we cannot interact with. + // We run this in a background thread and use a safety timer to ensure the test doesn't hang the suite. + Thread t = new Thread(() -> { + try { + // Requesting only IDs to be minimal and reduce permission friction where possible + Contact[] contacts = ContactsManager.getContacts(false, false, false, false, false, false); + Display.getInstance().callSerially(() -> { + if (contacts == null) { + status.setText("Contacts returned null (access denied?)"); + } else { + status.setText("Found " + contacts.length + " contact IDs"); + } + form.revalidate(); + done(); + }); + } catch (Exception e) { + Display.getInstance().callSerially(() -> { + status.setText("Exception: " + e.getMessage()); + form.revalidate(); + done(); + }); + e.printStackTrace(); + } + }); + t.start(); + + // Safety timeout: if the thread is blocked (e.g. by a permission dialog) for too long, + // we finish the test gracefully so the suite continues. + UITimer.timer(5000, false, form, () -> { + if (!isDone()) { + status.setText("Timeout waiting for contacts (likely blocking permission dialog)."); + form.revalidate(); + done(); + } + }); + + return true; + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SocketTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SocketTest.java new file mode 100644 index 0000000000..53209b1e96 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SocketTest.java @@ -0,0 +1,77 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.io.Socket; +import com.codename1.io.SocketConnection; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import java.io.InputStream; +import java.io.OutputStream; + +public class SocketTest extends BaseTest { + + @Override + public boolean shouldTakeScreenshot() { + return false; + } + + @Override + public boolean runTest() throws Exception { + // We do not use createForm() because it registers a timer that calls done(). + // We want to control done() ourselves after socket interaction. + Form form = new Form("Sockets", new BorderLayout()); + Label status = new Label("Connecting..."); + form.add(BorderLayout.CENTER, status); + form.show(); + + if (!Socket.isSupported()) { + status.setText("Sockets not supported"); + done(); + return true; + } + + Socket.connect("google.com", 80, new SocketConnection() { + @Override + public void connectionEstablished(InputStream is, OutputStream os) { + Display.getInstance().callSerially(() -> { + status.setText("Connected. Sending request..."); + form.revalidate(); + }); + try { + os.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n".getBytes()); + os.flush(); + + // Simple read to verify basic connectivity + byte[] buffer = new byte[128]; + int read = is.read(buffer); + if (read > 0) { + Display.getInstance().callSerially(() -> { + status.setText("Data received: " + read + " bytes. Success."); + form.revalidate(); + done(); + }); + } else { + Display.getInstance().callSerially(() -> { + fail("Read 0 or -1 bytes from socket."); + }); + } + + } catch (Exception e) { + Display.getInstance().callSerially(() -> { + fail("Write/Read failed: " + e.getMessage()); + }); + } + } + + @Override + public void connectionError(int errorCode, String message) { + Display.getInstance().callSerially(() -> { + fail("Connection error: " + message); + }); + } + }); + + return true; + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/VideoPlaybackTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/VideoPlaybackTest.java new file mode 100644 index 0000000000..45ae863c6f --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/VideoPlaybackTest.java @@ -0,0 +1,65 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.media.Media; +import com.codename1.media.MediaManager; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.util.UITimer; +import com.codename1.ui.layouts.BorderLayout; + +public class VideoPlaybackTest extends BaseTest { + // A small, public domain video sample (HTTPS to avoid cleartext issues) + private static final String VIDEO_URL = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Small.mp4"; + + @Override + public boolean shouldTakeScreenshot() { + return false; + } + + @Override + public boolean runTest() throws Exception { + Form form = new Form("Video Playback", new BorderLayout()); + Label status = new Label("Initializing video..."); + form.add(BorderLayout.NORTH, status); + form.show(); + + // Use createMediaAsync to avoid blocking the EDT on network access. + try { + MediaManager.createMediaAsync(VIDEO_URL, true, () -> { + // On completion of playback + }).ready(media -> { + if (media != null) { + media.setNativePlayerMode(false); // Try to embed if possible + form.add(BorderLayout.CENTER, media.getVideoComponent()); + status.setText("Playing video..."); + form.revalidate(); + media.play(); + } else { + status.setText("Failed to create media (null)."); + form.revalidate(); + } + }).except(err -> { + status.setText("Error creating media: " + err.getMessage()); + form.revalidate(); + }); + } catch (Exception e) { + status.setText("Error starting media creation: " + e.getMessage()); + e.printStackTrace(); + } + + // Safety timeout: Ensure the test finishes even if callbacks are never invoked (e.g. network hang) + // We allow plenty of time (10s) for buffering, but ensure we eventually call done(). + UITimer.timer(10000, false, form, () -> { + if (!isDone()) { + if (status.getText().equals("Initializing video...")) { + status.setText("Video initialization timed out."); + form.revalidate(); + } + done(); + } + }); + + return true; + } +}