How to debug a JavaFX webview?
I find two ways,
one is to attach external chrome debugger to JavaFX webview.
Another is to inject firebug javascript.
Method#1 seems to use a bit of hacks like set access to private fields etc.
Method#2 depends on firebuglite which seems to be obsolete now.
How should this be done in 2022. Are there any other methods, or better methods?
I am trying firebug lite approach as of now, and have made the javascript part of the maven project and trying to inject it as a string versus an external URL, because firebug seems to be properly public hosted nowhere now.
Refering to https://stackoverflow.com/a/18396900/2448015
This is where I am right now :
package xyz.jphil.internal_browser;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
/**
*
* #author Ivan
*/
public class WebkitDebugUtils {
private static int resourcesPort = 0;
public static void createHttpServerForLocalFiles() {
int portStart = 64321, endPort = 65534;
resourcesPort = portStart + (int) ((endPort * 1d - portStart * 1d) * Math.random());
System.out.println("Starting internal server on " + resourcesPort);
try {
HttpServer server = HttpServer.create(new InetSocketAddress(resourcesPort), 0);
HttpContext context = server.createContext("/");
context.setHandler((req) -> {
try (req) {
var p = req.getRequestURI();
System.out.println("Serving # localhost:"+resourcesPort+" "+p);
var s = WebkitDebugUtils.class.getResourceAsStream(p.toString()).readAllBytes();
req.sendResponseHeaders(200, s.length);
req.getResponseBody().write(s);
}catch(Exception a){
a.printStackTrace();
}
});
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class JavaBridge {
public void log(String text) {
System.out.println(text);
}
}
// Maintain a strong reference to prevent garbage collection:
// https://bugs.openjdk.java.net/browse/JDK-8154127
private final static JavaBridge bridge = new JavaBridge();
public static void enableDebug(final WebEngine e) {
JSObject window = (JSObject) e.executeScript("window");
window.setMember("java", bridge);
e.executeScript("""
console.log = function(message)
{
java.log(message);
};""");
e.executeScript("console.log('console.log enabled');");
}
public static void enableFirebug(final WebEngine e) {
var firebugurl = "http://localhost:"+resourcesPort+"/firebug-lite/firebug-lite.js";
//https://raw.githubusercontent.com/stefanXO/firebug-lite/master/firebug-lite.js
//https://lupatec.eu/getfirebug/firebug-lite-compressed.js
var script= """
console.log("Inside script block");
if (!document.getElementById("FirebugLite")) {
E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
E = E ?
document["createElement" + "NS"](E, "script") :
document["createElement"]("script");
E["setAttribute"]("id", "FirebugLite");
E["setAttribute"]("src", "{$firebugurl}#startOpened");
E["setAttribute"]("FirebugLite", "4");
(
document["getElementsByTagName"]("head")[0]
||
document["getElementsByTagName"]("body")[0]
).appendChild(E);
E = new Image();
E["setAttribute"]("src", "{$firebugurl}#startOpened");
}
console.log("executed script block");
""".replace("{$firebugurl}", firebugurl);
System.out.println("+++firebug thing+++");
System.out.println(script);
System.out.println("---firebug thing---");
e.executeScript(script);
}
}
Main method of the application
//...
public static void main(String[]args){
WebkitDebugUtils.createHttpServerForLocalFiles();
launch(args);
}
//...
In the JavaFx Stage initializing code section
#Override
public void start(final Stage primaryStage) throws Exception {
// ....
wv = new WebView();
// ....
javafx.scene.web.WebEngine e = wv.getEngine();
// ....
e.getLoadWorker().stateProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableDebug(e));
e.documentProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableFirebug(e));
// ....
}
I am serving the files for firebug from the resources folder of the jar
Snippter of the custom firebug.js file
console.log("inside firebug-lite");
(function(){
//...
//firebug lite 1.4.0 code
//..
})();
console.log("outside firebug-lite");
Output
Starting internal server on 65148
console.log enabled
console.log enabled
// some application specific output
+++firebug thing+++
if (!document.getElementById("FirebugLite")) {
E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
E = E ?
document["createElement" + "NS"](E, "script") :
document["createElement"]("script");
E["setAttribute"]("id", "FirebugLite");
E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
E["setAttribute"]("FirebugLite", "4");
(
document["getElementsByTagName"]("head")[0]
||
document["getElementsByTagName"]("body")[0]
).appendChild(E);
E = new Image();
E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
}
---firebug thing---
Inside script block
executed script block
Serving # localhost:65148 /firebug-lite/firebug-lite.js
// some application specific output
console.log enabled
Summary:
nothing is happening, nothing is showing up in the console, I don't know the error, firebug is not even opening.
Update2:
I think I am using the wrong version of firebug 1.5 or something
and the old version I don't see any HTML file and thus it is dependent on the default website which is down at the moment.
You can simply inject firebug-lite after loading your webpage.
I've tested on windows 10 with correto 18.0.2 JDK.
Here's an example: (Page can take some time to load)
package com.example.demo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.scene.web.WebEngine;
import javafx.concurrent.Worker.State;
public class HelloApplication extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage myStage) {
System.out.println("Demo for firebugLite");
WebView myWebView = new WebView();
myWebView.minHeight(1050);
myWebView.prefWidth(1950);
myWebView.prefHeight(1070);
myWebView.minWidth(1050);
final WebEngine myWebEngine = myWebView.getEngine();
myWebEngine.getLoadWorker().stateProperty()
.addListener((obs, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
System.out.println("finished loading");
myWebEngine.executeScript("var firebug=document.createElement('script');firebug.setAttribute('src','https://lupatec.eu/getfirebug/firebug-lite-compressed.js');document.body.appendChild(firebug);(function(){if(window.firebug.version){firebug.init();}else{setTimeout(arguments.callee);}})();void(firebug);");
}
});
myWebEngine.load("https://lupatec.eu/getfirebug/");
VBox myBox = new VBox(myWebView);
Scene myScene = new Scene(myBox, 1050, 600);
myStage.setScene(myScene);
myStage.show();
}
}
Result:
Related
I'm having an encoding issue with JavaFX's WebView. When loading a UTF-8 encoded file, special characters are displayed incorrectly (e.g. ’ is displayed instead of ’). Here's an SSCCE:
WebViewTest.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
WebView webView = new WebView();
webView.getEngine().load(getClass().getResource("/test.html").toExternalForm());
Scene scene = new Scene(webView, 500, 500);
stage.setScene(scene);
stage.setTitle("WebView Test");
stage.show();
}
}
test.html
<!DOCTYPE html>
<html>
<body>
<p>RIGHT SINGLE QUOTATION MARK: ’</p>
</body>
</html>
Output of file -bi test.html
src:$ file -bi test.html
text/plain; charset=utf-8
Result:
The same thing happens in Windows using Java 17 and the latest JavaFX (I used Linux and Java 8 for the demonstration).
I've tried:
Declaring the charset in the HTML: <meta charset="UTF-8">
(works, but I'm making an editor program, so I don't have control over the HTML)
Using the JVM argument -Dfile.encoding=UTF-8 (doesn't work)
Setting the charset using reflection (doesn't work, and throws an exception in newer Java versions):
System.setProperty("file.encoding","UTF-8");
Field charset = Charset.class.getDeclaredField("defaultCharset");
charset.setAccessible(true);
charset.set(null,null);
Declaring the charset after the page loads using the DOM API (doesn't work):
webView.getEngine().getLoadWorker().stateProperty().addListener((o, oldState, newState) -> {
if(newState == Worker.State.SUCCEEDED) {
Document document = webView.getEngine().getDocument();
Element meta = document.createElement("meta");
meta.setAttribute("charset", "UTF-8");
document.getElementsByTagName("html").item(0).appendChild(meta);
}
});
Using WebEngine.loadContent(String) instead of load(String) (wouldn't work; relative links would be broken)
It appears that WebView ignores file encodings, and uses ISO-8859-1 unless a charset is specified in the HTML.
WebView determines the encoding from either the HTML file or the HTTP header. This is as per the w3c specification, for information see:
Declaring character encodings in HTML
As you already noted in your question, you can declare the character encoding in the head element within the HTML document and the WebView will pick it up:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
...
But, you also note in your question that you don't have control over the input HTML files and whether it includes the necessary header for declaring the charset.
You can also have the HTTP protocol specify the encoding of the file using an appropriate header.
Content-Type: text/html; charset=UTF-8
If you do that, the HTML file content will be correctly UTF-8 decoded by the WebView, even if the input file does not include a charset header.
Here is an example:
import com.sun.net.httpserver.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
public class WebViewTest extends Application {
private static final String TEST_HTML = "test.html";
private HttpServer server;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void init() throws Exception {
server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
#Override
public void start(Stage stage) {
WebView webView = new WebView();
webView.getEngine().load("http://localhost:8000/" + TEST_HTML);
Scene scene = new Scene(webView, 500, 500);
stage.setScene(scene);
stage.setTitle("WebView Test");
stage.show();
}
#Override
public void stop() throws Exception {
server.stop(0);
}
static class MyHandler implements HttpHandler {
public void handle(HttpExchange httpExchange) {
try {
String path = httpExchange.getRequestURI().getPath().substring(1); // strips leading slash from path, so resource lookup will be relative to this class, not the root.
String testString = resourceAsString(path);
System.out.println("testString = " + testString);
if (testString != null) {
httpExchange.getResponseHeaders().put("Content-Type", List.of("text/html; charset=UTF-8"));
httpExchange.sendResponseHeaders(200, testString.getBytes(StandardCharsets.UTF_8).length);
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(httpExchange.getResponseBody()))) {
writer.write(testString);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("Unable to find resource: " + path);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String resourceAsString(String fileName) throws IOException {
try (InputStream is = WebViewTest.class.getResourceAsStream(fileName)) {
if (is == null) return null;
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
}
}
}
}
For this example to work, place the HTML test file from your question in the same location as your compiled WebViewTest.class, so that it can be loaded from there as a resource.
To run the example as a modular app, add the following to your module-info.java (in addition to your javafx module requirements and any other app requirements you need):
requires jdk.httpserver;
While writing the question, I found a hacky solution:
webView.getEngine().getLoadWorker().stateProperty().addListener((o, oldState, newState) -> {
if(newState == Worker.State.SUCCEEDED) {
try {
String newContent = new String(Files.readAllBytes(Paths.get(new URI(getClass().getResource("/test.html").toExternalForm()))), "UTF-8");
webView.getEngine().executeScript("document.documentElement.innerHTML = '" + newContent.replace("'", "\\'").replace("\n", "\\n") + "'");
} catch(Exception e) {
e.printStackTrace();
}
}
});
I found another simple solution using Spark Java:
WebViewTest.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import spark.Spark;
import spark.staticfiles.StaticFilesConfiguration;
public class WebViewTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
Spark.port(8000);
StaticFilesConfiguration staticHandler = new StaticFilesConfiguration();
staticHandler.configure("/");
Spark.before((req, res) -> {
if(req.url().endsWith(".html")) staticHandler.putCustomHeader("Content-Type", "text/html; charset=UTF-8");
else staticHandler.putCustomHeader("Content-Type", null);
staticHandler.consume(req.raw(), res.raw());
});
Spark.init();
WebView webView = new WebView();
webView.getEngine().load("http://localhost:8000/test.html");
Scene scene = new Scene(webView, 500, 500);
stage.setScene(scene);
stage.setTitle("WebView Test");
stage.show();
}
}
test.html
<!DOCTYPE html>
<html>
<body>
<p>RIGHT SINGLE QUOTATION MARK: ’</p>
<p>Image:</p>
<img src="image.png">
</body>
</html>
image.png
Result:
Summary: In the url below, please click the reply button. Then, a popup will appear and hide the reply button. When you click a blank area or on the paragraph containing 'post id:', the popup disappears and you can see the reply button again. I want to do these things using the code below.
Problem: None of the last 3 method calls in main make the popup disappear. What is wrong with my code and how do I make it work ?
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
public class Temp {
private static final WebDriver browser = new ChromeDriver();
private static String reply_btn_xpath = "//button[contains(concat(\" \", normalize-space(#class), \" \"), \" "
+ "reply_button" + " \")]";
private static By reply_btn_loc = By.xpath(reply_btn_xpath);
private static String url = "https://sfbay.craigslist.org/pen/apa/5764613878.html";
public static void main(String[] args) {
browser.get(url);
WebElement reply = browser.findElement(reply_btn_loc);
reply.click();
WebElement post_id = browser.findElement(By.xpath("//p[contains(., 'post id:')]"));
post_id.click();
click_with_actions(post_id);
click_with_js(post_id);
}
public static void click_with_actions(WebElement element) {
Actions actions = new Actions(browser);
actions.moveToElement(element).click().perform();
}
public static void click_with_js(WebElement element) {
((JavascriptExecutor) browser).executeScript("arguments[0].click();", element);
}
}
Apparently, it is advised not to use java.awt.Robot in VMs or CI to do the clicks because they don't have real keyboards. Java awt.Robot not working inside a virtual machine?
I tried Robot also in the above code and it did not work.
public static void click_with_robot(WebElement element, int x, int y) {
Robot bot = null;
try {
bot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
Point location = element.getLocation();
int x_loc = location.getX() + x;
int y_loc = location.getY() + y;
bot.mouseMove(x_loc, y_loc);
bot.mousePress(InputEvent.BUTTON1_MASK);
bot.mouseRelease(InputEvent.BUTTON1_MASK);
}
I created an eclipse plugin that will hook into the save action to create a minified javascript file with the goolge closure compiler. See files below.
That worked until eclipse 3.7.2. Unfortunately now in eclipse 4.2.1 it seems that this creates an endless loop sometimes. The job "compile .min.js" (line 64 in ResourceChangedListener.java) seems the be the cause. It results in the case that the workspaced starts to build over and over. I guess this is because that job creates or changes a file triggering the workspace build again, which again triggers the job which triggers the build and so on.
But I can not figure out how to prevent this.
// Activator.java
package closure_compiler_save;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class Activator extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "closure-compiler-save"; //$NON-NLS-1$
// The shared instance
private static Activator plugin;
/**
* The constructor
*/
public Activator() {
}
#Override
public void start(BundleContext context) throws Exception {
super.start(context);
Activator.plugin = this;
ResourceChangedListener listener = new ResourceChangedListener();
ResourcesPlugin.getWorkspace().addResourceChangeListener(listener);
}
#Override
public void stop(BundleContext context) throws Exception {
Activator.plugin = null;
super.stop(context);
}
/**
* Returns the shared instance
*
* #return the shared instance
*/
public static Activator getDefault() {
return plugin;
}
}
// ResourceChangedListener.java
package closure_compiler_save;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
public class ResourceChangedListener implements IResourceChangeListener {
public void resourceChanged(IResourceChangeEvent event) {
if (event.getType() != IResourceChangeEvent.POST_CHANGE)
return;
IResourceDelta delta = event.getDelta();
try {
processDelta(delta);
} catch (CoreException e) {
e.printStackTrace();
}
}
// find out which class files were just built
private void processDelta(IResourceDelta delta) throws CoreException {
IResourceDelta[] kids = delta.getAffectedChildren();
for (IResourceDelta delta2 : kids) {
if (delta2.getAffectedChildren().length == 0) {
if (delta.getKind() != IResourceDelta.CHANGED)
return;
IResource res = delta2.getResource();
if (res.getType() == IResource.FILE && "js".equalsIgnoreCase(res.getFileExtension())) {
if (res.getName().contains("min"))
return;
compile(res);
}
}
processDelta(delta2);
}
}
private void compile(final IResource res) throws CoreException {
final IPath fullPath = res.getFullPath();
final IPath fullLocation = res.getLocation();
final String fileName = fullPath.lastSegment().toString();
final String outputFilename = fileName.substring(0, fileName.lastIndexOf(".")).concat(".min.js");
final String outputPath = fullPath.removeFirstSegments(1).removeLastSegments(1).toString();
final IProject project = res.getProject();
final IFile newFile = project.getFile(outputPath.concat("/".concat(outputFilename)));
Job compileJob = new Job("Compile .min.js") {
public IStatus run(IProgressMonitor monitor) {
byte[] bytes = null;
try {
bytes = CallCompiler.compile(fullLocation.toString(), CallCompiler.SIMPLE_OPTIMIZATION).getBytes();
InputStream source = new ByteArrayInputStream(bytes);
if (!newFile.exists()) {
newFile.create(source, IResource.NONE, null);
} else {
newFile.setContents(source, IResource.NONE, null);
}
} catch (IOException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
}
return Status.OK_STATUS;
}
};
compileJob.setRule(newFile.getProject()); // this will ensure that no two jobs are writing simultaneously on the same file
compileJob.schedule();
}
}
After I setup a blank eclipse classic environment, started a new eclipse plugin project there and recreated all files it works again partly.
In this environment starting a debug session I can save .js files and .min.js files are created automatically.
So far so good!
But when I install the plugin to my real developing eclipse environment automatic saving does not work.
At least one step further!
Step 2:
There were some files not included in the build obviously needed, like manifest. No idea why they were deselected.
Anyway it seems just setting up a blank eclipse 4 classic and going through the eclipse plugin wizard fixed my original problem. I still would love to know what was the actual problem...
I am trying to implement OAuth 2.0 implicit grant from java desktop client application using SkyDrive REST API. I use the following code:
Desktop.getDesktop().browse(new URL(st.toString()).toURI());
JOptionPane.showMessageDialog(null, "Press ok to continue once you have authenticated.");
my code opens web browser and ask users to signin and then SkyDrive sends access token to the browser url in the following form:
https://login.live.com/oauth20_desktop.srf?lc=1033#access_token=EwAwAq1DBAAUlbRW.....
What I want to do is to read this access token from my java program.
I tried to read httpconnection from console:
HttpURLConnection con = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader( new InputStreamReader(url.openStream()));
while(reader.readLine()!=null){
System.out.println(reader.readLine());
but it seems that java httpurlconnection does not handle javascript response. It replies:
<html dir="..... Windows Live ID requires JavaScript to sign in. This web browser either does not support JavaScript, or scripts are being blocked......<body onload="evt_LoginHostMobile_onload(event);">
So, Is there any way to retrieve the access token directly from java?
I had the same problem. After hours of brainstorming, I have finally found a solution. I use the JavaFX library to create a WebView. Then you can intercept location change.
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Authenticate extends Application {
static final String APP_ID = "...";
static final String REDIRECT_URL = "https://login.live.com/oauth20_desktop.srf";
static final String RESPONSE_TYPE = "token";
static final String SCOPE = "wl.signin%20wl.offline_access";
private Scene scene;
#Override
public void start(final Stage stage) throws Exception {
final String url = "https://login.live.com/oauth20_authorize.srf?client_id="+APP_ID
+"&scope="+SCOPE+"&response_type="+RESPONSE_TYPE+"&oauth_callback=oob&redirect_uri="+REDIRECT_URL;
BorderPane borderPane = new BorderPane();
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.load(url);
borderPane.setCenter(browser);
webEngine.setOnStatusChanged(new EventHandler<WebEvent<String>>() {
public void handle(WebEvent<String> event) {
if (event.getSource() instanceof WebEngine) {
WebEngine we = (WebEngine) event.getSource();
String location = we.getLocation();
if (location.startsWith(REDIRECT_URL) && location.contains("access_token")) {
try {
URL url = new URL(location);
String[] params = url.getRef().split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
System.out.println("The access token: "+map.get("access_token"));
stage.hide();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
}
});
// create scene
stage.setTitle("Skydrive");
scene = new Scene(borderPane, 750, 500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I'm trying to create a splash screen using LWUIT. I want a form to load and display text and an imagefor 5 seconds then continue to the next form. I have a code but fails to show the image. The class and the image are stored together int he same package. Instead, it shows an error.
java.io.IOException
What could be the problem? This is the code
package tungPackage;
import com.sun.lwuit.Display;
import com.sun.lwuit.Form;
import com.sun.lwuit.Image;
import com.sun.lwuit.Label;
import javax.microedition.midlet.MIDlet;
public class photoMidlet extends MIDlet {
public void startApp() {
Display.init(this);
try {
Form splashscreen = new Form();
// Label splashText = new Label("Baldy");
Image image = Image.createImage("/splash.png");
Label pictureLabel = new Label(image);
splashscreen.addComponent(pictureLabel);
splashscreen.show();
} catch (Exception ex) {
Form x = new Form("ERROR");
String y = ex.toString();
Label g = new Label(y);
x.addComponent(g);
x.show();
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
Open your JAR file using a ZIP utility (e.g. 7-zip) and look in the root of the file. If splash.png isn't in the root of the jar that's your problem!
Place splash.png so it is in the root of the jar.