SlideShare a Scribd company logo
1 of 22
Download to read offline
Hacking the Source - Part III
So we ran the Kitchen Sink as a mobile app using the Simulator and that's great. Before going to Android and iOS lets try running the app as a desktop app. This will be
a relatively simple exercise that can help us understand the more nuanced Android/iOS processes.
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
As I mentioned before we don't have a real main class. We only have the bootstrap Codename One main class which we can't run as a standalone app. So we will need
to create a new main class which I will place under a directory called stubs. The term "stub" in Codename One is one we use internally to refer to the OS bootstrap/glue
code. In this case I place the stub under stubs/desktop/src/com/codename1/demos/kitchen.

There is a bit of code here in the stub so I'm going to go over the various lines. It's basically a Swing app that embeds Codename One so you can enhance the stub with
everything available under JavaSE.

I included import statements because they refer to classes in JavaSE and that might be confusing. In the stub it makes sense to import the implementation code since
this code has no portability to other platforms so I’m importing the JavaSEPort directly
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
For simplicity I hardcoded the app dimensions but it's pretty easy to create more elaborate heuristics and window positioning using Swing
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
If your theme doesn't derive from native this doesn't matter. However, if it does I made it use the iOS 7 theme which is the default iOS theme. This will make the app feel
a bit like an iPad app
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
The JavaSEPort is used for the simulator too so things like network monitor and other simulator features might still be on if this is a developer machine. So here we
disable all of those flags
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
This is the location for the storage file under the user home directory. In the simulator this is always .cn1 but it makes more sense to use your app name for real world
apps
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
The simulator hides the full filesystem and shows a fake one under the .cn1 hierarchy. This disables that behavior so getRoots() will return the filesystems in your
machine. The simulator does that by default to make it feel more like a real device
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
When running on a desktop you would prefer showing a tablet UI rather than a phone UI. This means that isTablet & isDesktop would both return true
package com.codename1.demos.kitchen;
import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Display;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class KitchenSinkStub implements Runnable {
private static JFrame frm;
private KitchenSink mainApp;
private static final int APP_WIDTH = 800;
private static final int APP_HEIGHT = 600;
public static void main(String[] args) {
JavaSEPort.setNativeTheme("/iOS7Theme.res");
JavaSEPort.blockMonitors();
JavaSEPort.setShowEDTViolationStacks(false);
JavaSEPort.setShowEDTWarnings(false);
JavaSEPort.setAppHomeDir(".KitchenSink");
JavaSEPort.setExposeFilesystem(true);
JavaSEPort.setTablet(true);
JavaSEPort.setUseNativeInput(true);
JavaSEPort.setDefaultPixelMilliRatio(new Double(10));
SwingUtilities.invokeLater(new KitchenSinkStub());
}
KitchenSinkStub (Desktop)
Swing is also single threaded... This invokes the run() method on the Swing thread so we can construct the Swing UI
SwingUtilities.invokeLater(new KitchenSinkStub());
}
@Override
public void run() {
frm = new JFrame("Kitchen Sink");
Display.init(frm.getContentPane());
Display.getInstance().setProperty("package_name", "com.codename1.demos.kitchen");
Display.getInstance().setProperty("AppName", "KitchenSink");
Display.getInstance().setProperty("AppVersion", "2.0");
Display.getInstance().setProperty("Platform", System.getProperty("os.name"));
Display.getInstance().setProperty("OSVer", System.getProperty("os.version"));
frm.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
java.awt.Dimension d = new java.awt.Dimension(APP_WIDTH, APP_HEIGHT);
frm.getContentPane().setPreferredSize(d);
frm.getContentPane().setMinimumSize(d);
frm.getContentPane().setMaximumSize(d);
frm.pack();
frm.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
KitchenSinkStub (Desktop)
We create a Swing JFrame and then initialize Codename One with the ContentPane of the frame
SwingUtilities.invokeLater(new KitchenSinkStub());
}
@Override
public void run() {
frm = new JFrame("Kitchen Sink");
Display.init(frm.getContentPane());
Display.getInstance().setProperty("package_name", "com.codename1.demos.kitchen");
Display.getInstance().setProperty("AppName", "KitchenSink");
Display.getInstance().setProperty("AppVersion", "2.0");
Display.getInstance().setProperty("Platform", System.getProperty("os.name"));
Display.getInstance().setProperty("OSVer", System.getProperty("os.version"));
frm.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
java.awt.Dimension d = new java.awt.Dimension(APP_WIDTH, APP_HEIGHT);
frm.getContentPane().setPreferredSize(d);
frm.getContentPane().setMinimumSize(d);
frm.getContentPane().setMaximumSize(d);
frm.pack();
frm.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
KitchenSinkStub (Desktop)
These are display properties we rely on in code, they aren't essential but it's good practice to have them
frm.pack();
frm.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
mainApp.stop();
mainApp.destroy();
Display.getInstance().exitApplication();
}
});
}
} );
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
if(Display.getInstance().isEdt()) {
mainApp = new com.codename1.demos.kitchen.KitchenSink();
mainApp.init(this);
mainApp.start();
SwingUtilities.invokeLater(this);
} else {
frm.setVisible(true);
KitchenSinkStub (Desktop)
The frame is sized and positioned. We bind a window listener for the app close event. We make sure to call stop() and destroy() on the main class before exiting. Notice
we invoke these methods on the Codename One thread!
@Override
public void run() {
mainApp.stop();
mainApp.destroy();
Display.getInstance().exitApplication();
}
});
}
} );
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
if(Display.getInstance().isEdt()) {
mainApp = new com.codename1.demos.kitchen.KitchenSink();
mainApp.init(this);
mainApp.start();
SwingUtilities.invokeLater(this);
} else {
frm.setVisible(true);
}
}
});
}
}
KitchenSinkStub (Desktop)
We go into the Codename One thread to invoke start() & init(). Once those are done we call this method again on the Swing thread and show the frame itself. I took a bit
of a shortcut by reusing the same Runnable instance for both callbacks.

Now that this is out of the way we are nearly done!
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
We just need to compile that code and link it with the JavaSEPort code. Since we already have an ant build.xml file this is probably the easiest way to do that. I added a
new target to the build.xml that looks like this.

I want to make sure we are working with a clean slate when running this build
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
There are two important things to notice in this statement. I don't use the bootclasspath since the app is now a standard JavaSE app. The classpath points at JavaSE.jar
because I'm compiling against an implementation not an API
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
Notice that the stubs/desktop directory is added to the compilation process so that would get bundled in too
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
We copy the theme from the Codename One repo so the native theme will look like iOS
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
The final step is the executable jar generation. For a jar to be executable we need the main class in the manifest which I define here
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
We need 3 things in the jar. First we need the contents of JavaSEPort which is already available in the cn1 project. Notice I exclude the skin file from there as we don't
need the simulator and it will just increase the jar size
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
The next thing we need are the compiled sources of the KitchenSink project and KitchenSinkStub
<target name="desktop" depends="init">
<delete dir="build/tmp" />
<mkdir dir="build/tmp" />
<javac destdir="build/tmp"
encoding="${source.encoding}"
source="1.8"
target="1.8"
classpath="JavaSE.jar">
<src path="${src.dir}:stubs/desktop"/>
</javac>
<copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" />
<jar jarfile="kitchesink-desktop.jar">
<manifest> <5>
<attribute name="Main-Class"
value="com.codename1.demos.kitchen.KitchenSinkStub"/>
</manifest>
<fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" />
<fileset dir="build/tmp" />
<fileset dir="src" excludes="*.java" />
</jar>
</target>
build.xml
And finally we need the resources defined within the Kitchen Sink project so I package them directly from the src directory making sure to exclude the Java source files
java -jar kitchesink-desktop.jar
Run
After going through all of this I now have a desktop app that I can run using this. 

Notice that this is a self contained fat Jar with no external dependencies which means it should work well for tools such as JavaPackager etc.

More Related Content

Similar to Hacking the Codename One Source Code - Part III - Transcript.pdf

Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
Droidcon Berlin
 
4th semester project report
4th semester project report4th semester project report
4th semester project report
Akash Rajguru
 
HRServicesPOX.classpathHRServicesPOX.project HRSer.docx
HRServicesPOX.classpathHRServicesPOX.project  HRSer.docxHRServicesPOX.classpathHRServicesPOX.project  HRSer.docx
HRServicesPOX.classpathHRServicesPOX.project HRSer.docx
adampcarr67227
 
PhoneGap:你应该知道的12件事
PhoneGap:你应该知道的12件事PhoneGap:你应该知道的12件事
PhoneGap:你应该知道的12件事
longfei.dong
 
I am getting a syntax error. I cant seem to find whats causing t.pdf
I am getting a syntax error. I cant seem to find whats causing t.pdfI am getting a syntax error. I cant seem to find whats causing t.pdf
I am getting a syntax error. I cant seem to find whats causing t.pdf
fashionfolionr
 
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W... 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
Robert Nyman
 

Similar to Hacking the Codename One Source Code - Part III - Transcript.pdf (20)

Appium troubleshooting
Appium troubleshootingAppium troubleshooting
Appium troubleshooting
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
 
From Swing to JavaFX
From Swing to JavaFXFrom Swing to JavaFX
From Swing to JavaFX
 
JavaYDL18
JavaYDL18JavaYDL18
JavaYDL18
 
DBTool
DBToolDBTool
DBTool
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Will it blend? Java agents and OSGi
Will it blend? Java agents and OSGiWill it blend? Java agents and OSGi
Will it blend? Java agents and OSGi
 
What do you mean it needs to be Java based? How jython saved the day.
What do you mean it needs to be Java based? How jython saved the day.What do you mean it needs to be Java based? How jython saved the day.
What do you mean it needs to be Java based? How jython saved the day.
 
Hacking the Codename One Source Code - Part IV - Transcript.pdf
Hacking the Codename One Source Code - Part IV - Transcript.pdfHacking the Codename One Source Code - Part IV - Transcript.pdf
Hacking the Codename One Source Code - Part IV - Transcript.pdf
 
4th semester project report
4th semester project report4th semester project report
4th semester project report
 
HRServicesPOX.classpathHRServicesPOX.project HRSer.docx
HRServicesPOX.classpathHRServicesPOX.project  HRSer.docxHRServicesPOX.classpathHRServicesPOX.project  HRSer.docx
HRServicesPOX.classpathHRServicesPOX.project HRSer.docx
 
Java awt
Java awtJava awt
Java awt
 
Code to DI For - Dependency Injection for Modern Applications
Code to DI For - Dependency Injection for Modern ApplicationsCode to DI For - Dependency Injection for Modern Applications
Code to DI For - Dependency Injection for Modern Applications
 
PhoneGap:你应该知道的12件事
PhoneGap:你应该知道的12件事PhoneGap:你应该知道的12件事
PhoneGap:你应该知道的12件事
 
I am getting a syntax error. I cant seem to find whats causing t.pdf
I am getting a syntax error. I cant seem to find whats causing t.pdfI am getting a syntax error. I cant seem to find whats causing t.pdf
I am getting a syntax error. I cant seem to find whats causing t.pdf
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257
 
Introduction toandroid
Introduction toandroidIntroduction toandroid
Introduction toandroid
 
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W... 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 

More from ShaiAlmog1

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 

Recently uploaded

Recently uploaded (20)

Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 

Hacking the Codename One Source Code - Part III - Transcript.pdf

  • 1. Hacking the Source - Part III So we ran the Kitchen Sink as a mobile app using the Simulator and that's great. Before going to Android and iOS lets try running the app as a desktop app. This will be a relatively simple exercise that can help us understand the more nuanced Android/iOS processes.
  • 2. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) As I mentioned before we don't have a real main class. We only have the bootstrap Codename One main class which we can't run as a standalone app. So we will need to create a new main class which I will place under a directory called stubs. The term "stub" in Codename One is one we use internally to refer to the OS bootstrap/glue code. In this case I place the stub under stubs/desktop/src/com/codename1/demos/kitchen. There is a bit of code here in the stub so I'm going to go over the various lines. It's basically a Swing app that embeds Codename One so you can enhance the stub with everything available under JavaSE. I included import statements because they refer to classes in JavaSE and that might be confusing. In the stub it makes sense to import the implementation code since this code has no portability to other platforms so I’m importing the JavaSEPort directly
  • 3. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) For simplicity I hardcoded the app dimensions but it's pretty easy to create more elaborate heuristics and window positioning using Swing
  • 4. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) If your theme doesn't derive from native this doesn't matter. However, if it does I made it use the iOS 7 theme which is the default iOS theme. This will make the app feel a bit like an iPad app
  • 5. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) The JavaSEPort is used for the simulator too so things like network monitor and other simulator features might still be on if this is a developer machine. So here we disable all of those flags
  • 6. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) This is the location for the storage file under the user home directory. In the simulator this is always .cn1 but it makes more sense to use your app name for real world apps
  • 7. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) The simulator hides the full filesystem and shows a fake one under the .cn1 hierarchy. This disables that behavior so getRoots() will return the filesystems in your machine. The simulator does that by default to make it feel more like a real device
  • 8. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) When running on a desktop you would prefer showing a tablet UI rather than a phone UI. This means that isTablet & isDesktop would both return true
  • 9. package com.codename1.demos.kitchen; import com.codename1.impl.javase.JavaSEPort; import com.codename1.ui.Display; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class KitchenSinkStub implements Runnable { private static JFrame frm; private KitchenSink mainApp; private static final int APP_WIDTH = 800; private static final int APP_HEIGHT = 600; public static void main(String[] args) { JavaSEPort.setNativeTheme("/iOS7Theme.res"); JavaSEPort.blockMonitors(); JavaSEPort.setShowEDTViolationStacks(false); JavaSEPort.setShowEDTWarnings(false); JavaSEPort.setAppHomeDir(".KitchenSink"); JavaSEPort.setExposeFilesystem(true); JavaSEPort.setTablet(true); JavaSEPort.setUseNativeInput(true); JavaSEPort.setDefaultPixelMilliRatio(new Double(10)); SwingUtilities.invokeLater(new KitchenSinkStub()); } KitchenSinkStub (Desktop) Swing is also single threaded... This invokes the run() method on the Swing thread so we can construct the Swing UI
  • 10. SwingUtilities.invokeLater(new KitchenSinkStub()); } @Override public void run() { frm = new JFrame("Kitchen Sink"); Display.init(frm.getContentPane()); Display.getInstance().setProperty("package_name", "com.codename1.demos.kitchen"); Display.getInstance().setProperty("AppName", "KitchenSink"); Display.getInstance().setProperty("AppVersion", "2.0"); Display.getInstance().setProperty("Platform", System.getProperty("os.name")); Display.getInstance().setProperty("OSVer", System.getProperty("os.version")); frm.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); java.awt.Dimension d = new java.awt.Dimension(APP_WIDTH, APP_HEIGHT); frm.getContentPane().setPreferredSize(d); frm.getContentPane().setMinimumSize(d); frm.getContentPane().setMaximumSize(d); frm.pack(); frm.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Display.getInstance().callSerially(new Runnable() { @Override public void run() { KitchenSinkStub (Desktop) We create a Swing JFrame and then initialize Codename One with the ContentPane of the frame
  • 11. SwingUtilities.invokeLater(new KitchenSinkStub()); } @Override public void run() { frm = new JFrame("Kitchen Sink"); Display.init(frm.getContentPane()); Display.getInstance().setProperty("package_name", "com.codename1.demos.kitchen"); Display.getInstance().setProperty("AppName", "KitchenSink"); Display.getInstance().setProperty("AppVersion", "2.0"); Display.getInstance().setProperty("Platform", System.getProperty("os.name")); Display.getInstance().setProperty("OSVer", System.getProperty("os.version")); frm.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); java.awt.Dimension d = new java.awt.Dimension(APP_WIDTH, APP_HEIGHT); frm.getContentPane().setPreferredSize(d); frm.getContentPane().setMinimumSize(d); frm.getContentPane().setMaximumSize(d); frm.pack(); frm.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Display.getInstance().callSerially(new Runnable() { @Override public void run() { KitchenSinkStub (Desktop) These are display properties we rely on in code, they aren't essential but it's good practice to have them
  • 12. frm.pack(); frm.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Display.getInstance().callSerially(new Runnable() { @Override public void run() { mainApp.stop(); mainApp.destroy(); Display.getInstance().exitApplication(); } }); } } ); Display.getInstance().callSerially(new Runnable() { @Override public void run() { if(Display.getInstance().isEdt()) { mainApp = new com.codename1.demos.kitchen.KitchenSink(); mainApp.init(this); mainApp.start(); SwingUtilities.invokeLater(this); } else { frm.setVisible(true); KitchenSinkStub (Desktop) The frame is sized and positioned. We bind a window listener for the app close event. We make sure to call stop() and destroy() on the main class before exiting. Notice we invoke these methods on the Codename One thread!
  • 13. @Override public void run() { mainApp.stop(); mainApp.destroy(); Display.getInstance().exitApplication(); } }); } } ); Display.getInstance().callSerially(new Runnable() { @Override public void run() { if(Display.getInstance().isEdt()) { mainApp = new com.codename1.demos.kitchen.KitchenSink(); mainApp.init(this); mainApp.start(); SwingUtilities.invokeLater(this); } else { frm.setVisible(true); } } }); } } KitchenSinkStub (Desktop) We go into the Codename One thread to invoke start() & init(). Once those are done we call this method again on the Swing thread and show the frame itself. I took a bit of a shortcut by reusing the same Runnable instance for both callbacks.
 Now that this is out of the way we are nearly done!
  • 14. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml We just need to compile that code and link it with the JavaSEPort code. Since we already have an ant build.xml file this is probably the easiest way to do that. I added a new target to the build.xml that looks like this. I want to make sure we are working with a clean slate when running this build
  • 15. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml There are two important things to notice in this statement. I don't use the bootclasspath since the app is now a standard JavaSE app. The classpath points at JavaSE.jar because I'm compiling against an implementation not an API
  • 16. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml Notice that the stubs/desktop directory is added to the compilation process so that would get bundled in too
  • 17. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml We copy the theme from the Codename One repo so the native theme will look like iOS
  • 18. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml The final step is the executable jar generation. For a jar to be executable we need the main class in the manifest which I define here
  • 19. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml We need 3 things in the jar. First we need the contents of JavaSEPort which is already available in the cn1 project. Notice I exclude the skin file from there as we don't need the simulator and it will just increase the jar size
  • 20. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml The next thing we need are the compiled sources of the KitchenSink project and KitchenSinkStub
  • 21. <target name="desktop" depends="init"> <delete dir="build/tmp" /> <mkdir dir="build/tmp" /> <javac destdir="build/tmp" encoding="${source.encoding}" source="1.8" target="1.8" classpath="JavaSE.jar"> <src path="${src.dir}:stubs/desktop"/> </javac> <copy file="../cn1/Themes/iOS7Theme.res" todir="build/tmp" /> <jar jarfile="kitchesink-desktop.jar"> <manifest> <5> <attribute name="Main-Class" value="com.codename1.demos.kitchen.KitchenSinkStub"/> </manifest> <fileset dir="../cn1/Ports/JavaSE/build/classes" excludes="*.skin" /> <fileset dir="build/tmp" /> <fileset dir="src" excludes="*.java" /> </jar> </target> build.xml And finally we need the resources defined within the Kitchen Sink project so I package them directly from the src directory making sure to exclude the Java source files
  • 22. java -jar kitchesink-desktop.jar Run After going through all of this I now have a desktop app that I can run using this. Notice that this is a self contained fat Jar with no external dependencies which means it should work well for tools such as JavaPackager etc.