project hydrazine 2 years ago
Saturday, May 31, 2008
LWUIT - guest post by Chen Fishbein
project hydrazine 2 years ago
Guest bloggers
Thursday, May 29, 2008
Very busy week running around with LWUIT
Comverse have been working with us on one of the first commercial applications outside of Sun that leverage LWUIT. They seem to share the excitement we have towards this project and Amit of Comverse went on stage with us at Java tech day. I hope to post some shots and a video of their really cool LWUIT application, its a visual voice mail application which they developer well before the iPhone was even planned (at least 3 years ago). They had a Java version for years but they couldn't get the UI to look "just right", it looks great in LWUIT and they are really helping us iron out the kinks in LWUIT.
Hopefully I will be able to get guys from Comverse to blog in the future about their LWUIT experiences.
I was also at another company which I won't name for now since it wasn't yet announced, they too will have a very widely deployed LWUIT application out within a ridiculously short amount of time. Hopefully once its made public I can blog more about their product which is also really cool.
This brings me to the point, people are using LWUIT for production already even before we released the code! There is real excitement and the amount of interest is just phenomenal!
I can't talk yet about most of the leads and directions because I'm working in a corporate environment, here you always assume everything is secrete (which it usually is). Expect allot of LWUIT related announcements in the coming months...
I have also spent allot of time on S60 performance with the help of some kind soles within Nokia who profiled the LWUIT Demo on devices and gave me some pointers. I tried to make simple and wide sweeping changes that might actually improve performance on all devices across the board.
Note: Updates by Shai to include image of Comverse Visual Voicemail application taken from here.
Sunday, May 25, 2008
Saturday, May 24, 2008
Licensing Terms of LWUIT
LWUIT is NOT GPL!
It is GPL+CPE (Classpath Exception) which allows you to make changes to the source without a problem. You will need to publish all changes you made to LWUIT, but since the CPE still applies you can still package your proprietary application with the binary and ship this without publishing the source code for your own application!
Disclaimer: I Am Not A Lawyer, however I know these issues reasonably well.
The main idea: change LWUIT as you want but give it back, feel free to make money off it using whatever business model you choose.
So why do we have another license on the page, the SLA?
Simplicity, it is often hard to talk in some corporations about open source. There is general GPL phobia in some cases and you might not be able to get past some of those lawyer types.
Sun wants to help you by offering another licensing option that might be easier to use in a corporate environment rather than dealing with OSS license complexities. Obviously this license can only be applied to the binary release to prevent potential harmful forking of the code.
How can Sun publish under the SLA and not only under the GPL+CPE?
Sun maintains full copyright to the code and as the copyright owner can license the binary & source as Sun pleases. This is a privilage of Sun as the owner of the rights to the code, you yourself can't just relicese and resell the code. However, you can sell derivative work as long as you comply with the appropriate license. e.g.:
When you haven't changed the code you can pick any license terms SLA or GPL+CPE.
When you changed the code you must comply with GPL+CPE and cannot use the SLA license.
Hopefully this makes sense, one thing I don't like about the GPL is that it makes programmers talk like lawyers... Hopefully we provided enough licensing leverage to render these issues mute.
Friday, May 23, 2008
LWUITDemo in only 123kb JAR!
What I did today was just that, I refactored all usage of resources and made them downloadable on startup. If the demo can't find the resources locally then it goes to a download URL and fetches them to RMS. I have some odd deployment problems with my server but the jad/jar seem correct since I was able to beam the jar to my Nokia for testing.
This is the jad url and this is the jar url.
This is the exact same full blown LWUIT demo in 123kb, the size was so small it even surprised me!
BTW I also added a couple of features and fixed some bugs since this is based on a newer LWUIT snapshot. Notice that I added a feature suggested on the list for applying the list cell renderer (in the renderer demo) to the menu. The screenshot in this post shows the menu with a fisheye cell renderer installed, very cool effect!
Update by Shai: Fixed the JAR URL which was broken.
Thursday, May 22, 2008
When Will We See LWUIT Matisse Support
Pretty much everything supported by the standard Matisse is supported (even baseline alignment is supported!).
So why isn't this out yet?
Matisse is HUGE, I could never have built this without the tremendous help from Tomas Pavek and Jan Stola. Their help made this stage possible, however to release something like this we need even further effort to verify that this tool doesn't break when you are actually trying to use it.
Problem is that there are dosens of priorities and tasks related to LWUIT and Matisse isn't at a high position in our team compared to this at the moment.
How can you help?
Write to lwuit_comments at sun.com or the users list, open RFE's both for LWUIT and for Netbeans (both Matisse and Mobility). Don't spam and be nice about it, the point being to show to both our management here and the NetBeans team how worthwhile it is to invest in this tool.
If its one person asking and writing I doubt that will help much, so if you really want it and think it warrants sacrificing other features/efforts to get this please let us know.
Wednesday, May 21, 2008
What Do You Want To Know About LWUIT?
Feel free to reply to this post and I will try to answer in future blog posts.
InvokeAndBlock, Foxtrot Inspired Threading In LWUIT
When people talk about dialog modality they often mean two separate things, the first indicates that the dialog intercepts all input and blocks the background form/window which is the true definition of modality. However, there is another aspect often associated with modality that is really important from a programmers perspective and simplified our code considerably:
public void actionPerformed(ActionEvent ev) {Notice that the dialog show method will block the calling thread until the user clicks OK or Not OK...
// will return true if the user clicks "OK"
if(!Dialog.show("Question", "How Are You", "OK", "Not OK")) {
// ask what went wrong...
}
}
If you read a bit about LWUIT you would notice that we are blocking the EDT (Event Dispatch Thread) which is also responsible for painting, how does the dialog paint itself or handle events?
The secret is invokeAndBlock, it allows us to "block" the EDT and resume it while keeping a "nested" EDT functioning. The semantics of this logic are a bit hairy so I won't try to explain them further, this functionality is also available in Swing which has the exact same modality feature however Swing doesn't expose the "engine" to developers. Foxtrot, exposes this undocumented engine to Swing developers, in LWUIT we chose to expose the ability to block the EDT (without "really" blocking it) as a simple API: invokeAndBlock.
The best way to explain this is by example:
public void actionPerformed(ActionEvent ev) {Notice that the behavior here is similar to the modal dialog, invokeAndBlock "blocked" the current thread despite the fact that it is the EDT and performed the run() method in a separate thread. When run() completes the EDT is resumed. All the while repaints and events occur as usual, you can have invokeAndBlock calls occurring while another invokeAndBlock is still pending there are no limitations here.
label.setText("Initiating IO, please wait...");
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
// perform IO operation...
}
});
label.setText("IO completed!");
// update UI...
}
As you can see this is a very simple approach for thread programming in UI, you don't need to block your flow and track the UI thread. You can just program in a way that seems sequential (top to bottom) but really uses multi-threading correctly without blocking the EDT.
Text Input In LWUIT Now And In The Future
Chen and myself got a question during our LWUIT session in J1 which in retrospect we probably didn't answer correctly. Text input in LWUIT today goes to the system native text box and returns when the user approves the change to the text.
One of our biggest users asked us in the past for a "lightweight" text widget, this would allow fast input for numbers or search fields but won't support predicitive text (T9). Such a future development won't replace the option to go to the native text box but would supplant it mostly for the simpler input types (e.g. numbers) and for input types where T9 is less important (e.g. search where mostly names are typed).
So when we got a question of how input is implemented in LWUIT we made the mistake of discussing the future support for lightweight input rather than the already existing support for standard text box input.
BTW for lightweight numeric input we already have a sample in the developer guide which implements a numeric text field with a blinking cursor so this should be a feasible component.How Portable Is LWUIT Part 2 - The PSP
A cool thing about J1 is meeting people who are working on great ideas which I wasn't expecting, I met Max Mu before but we hadn't talked too much... Then in J1 he flashed his PSP running a port of Phone ME which was also the subject of his BoF, getting LWUIT running on it was just a matter of beaming the jar over into his device using BT. It just worked, very little needed doing. The attached video shows our reaction to the feat including sideline commentary about people stealing our food (goes to show you what people like Max and myself are up to while drinking in a bar party).
Max did an amazing job with this port just the fact that LWUIT worked without a hitch using the exact same jar as any other device on the first try is a testament of that. The VM is purely interpreted which is why LWUIT is so slow (the screen is very large making LWUIT even slower without a JIT).
This was not the only surprise I had at J1 regarding devices, all over the show people approached me and showed me the UI demo on their pet device and in many cases it was devices we haven't seen before. Had LWUIT been a public product this would have been commendable, but these people all came to us from the very limited set of people who had access to our pre-releases...
Tuesday, May 20, 2008
Progress Indicator & Threads In LWUIT
This is a simple example of how to create a custom component in LWUIT in this specific case a progress indicator that supports both drawing itself (as a filled round rectangle and as a couple of images overlayed one on top of the other. The progress indicator component is fully themeable and customizable and will accept all L&F settings seamlessly.
The screenshots show both an image based indicator (its ugliness is just a testament to my bad drawing skills) and an indicator drawn in graphics primitives (fill/drawRoundRect). These are the images used to draw the image progress:
As part of this I also wanted to create something else familiar to Swing developers, the SwingWorker. Generally I prefer the foxtrot approach implemented in LWUIT as invokeAndBlock in Display, however lots of people like the SwingWorker approach so I used it here as part of the explanations.
First lets create the Progress indicator component:
/**
* Simple progress indicator component that fills out the progress made.
* Progress is assumed to always be horizontal in this widget
*
* @author Shai Almog
*/
public class Progress extends Component {
private byte percent;
private Image unfilled;
private Image filled;
/**
* The default constructor uses internal rendering to draw the progress
*/
public Progress() {
setFocusable(false);
}
/**
* Allows indicating the progress using a filled/unfilled images.
* The unfilled image is always drawn and the filled image is drawn on top with
* clipping to indicate the amount of progress made.
*
* @param unfilled an image containing the progress bar without any of its
* content being filled (with the progress color)
* @param filled an image identicall to unfilled in every way except that progress
* is completed in this bar.
*/
public Progress(Image unfilled, Image filled) {
this();
this.unfilled = unfilled;
this.filled = filled;
}
/**
* Indicate to LWUIT the component name for theming in this case "Progress"
*/
public String getUIID() {
return "Progress";
}
/**
* Indicates the percent of progress made
*/
public byte getProgress() {
return percent;
}
/**
* Indicates the percent of progress made, this method is thread safe and
* can be invoked from any thread although discression should still be kept
* so one thread doesn't regress progress made by another thread...
*/
public void setProgress(byte percent) {
this.percent = percent;
repaint();
}
/**
* Return the size we would generally like for the component
*/
protected Dimension calcPreferredSize() {
if(filled != null) {
return new Dimension(filled.getWidth(), filled.getHeight());
} else {
// we don't really need to be in the font height but this provides
// a generally good indication for size expectations
return new Dimension(Display.getInstance().getDisplayWidth(),
Font.getDefaultFont().getHeight());
}
}
/**
* Paint the progress indicator
*/
public void paint(Graphics g) {
int width = (int)((((float)percent) / 100.0f) * getWidth());
if(filled != null) {
if(filled.getWidth() != getWidth()) {
filled = filled.scaled(getWidth(), getHeight());
unfilled = unfilled.scaled(getWidth(), getHeight());
}
// draw based on two user supplied images
g.drawImage(unfilled, getX(), getY());
g.clipRect(getX(), getY(), width, getHeight());
g.drawImage(filled, getX(), getY());
} else {
// draw based on simple graphics primitives
Style s = getStyle();
g.setColor(s.getBgColor());
int curve = getHeight() / 2 - 1;
g.fillRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
g.setColor(s.getFgColor());
g.drawRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
g.clipRect(getX(), getY(), width - 1, getHeight() - 1);
g.setColor(s.getBgSelectionColor());
g.fillRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
}
}
}
This code seems to me to be simple but obviously I'm not objective, if something is not clear or you think it might not be clear to others please let me know in the comments.
BackgroundTask is my equivalent to SwingWorker, its much simpler than SwingWorker:
/**
* A tool allowing to respond to an event in the background possibly with
* progress indication inspired by Swings "SwingWorker" tool. This class
* should be used from event dispatching code to prevent the UI from blocking.
* State can be stored in this class the separate thread and it can be used by
* the finish method which will be invoked after running.
*
* @author Shai Almog
*/
public abstract class BackgroundTask {
/**
* Start this task
*/
public final void start() {
if(Display.getInstance().isEdt()) {
taskStarted();
} else {
Display.getInstance().callSeriallyAndWait(new Runnable() {
public void run() {
taskStarted();
}
});
}
new Thread(new Runnable() {
public void run() {
if(Display.getInstance().isEdt()) {
taskFinished();
} else {
performTask();
Display.getInstance().callSerially(this);
}
}
}).start();
}
/**
* Invoked on the LWUIT EDT before spawning the background thread, this allows
* the developer to perform initialization easily.
*/
public void taskStarted() {
}
/**
* Invoked on a separate thread in the background, this task should not alter
* UI except to indicate progress.
*/
public abstract void performTask();
/**
* Invoked on the LWUIT EDT after the background thread completed its
* execution.
*/
public void taskFinished() {
}
}
And this is the code to display these two progress bars:
Form progressForm = new Form("Progress");
progressForm.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
Progress p1 = new Progress();
progressForm.addComponent(new Label("Drawn"));
progressForm.addComponent(p1);
Progress p2 = new Progress(Image.createImage("/unfilled.png"), Image.createImage("/filled.png"));
p2.getStyle().setBgTransparency(0);
progressForm.addComponent(new Label("Image Based"));
progressForm.addComponent(p2);
progressForm.show();
class ProgressCommand extends Command {
private Progress p;
public ProgressCommand(String name, Progress p) {
super(name);
this.p = p;
}
public void actionPerformed(ActionEvent ev) {
new BackgroundTask() {
public void performTask() {
for(byte b = 0 ; b <= 100 ; b++) {
try {
p.setProgress(b);
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}.start();
}
}
progressForm.addCommand(new ProgressCommand("Drawn", p1));
progressForm.addCommand(new ProgressCommand("Images", p2));