Wednesday, July 8, 2009

Getting Started with BlackBerry J2ME Development

Check out my article on java.net on blackberry application development with J2ME.

Friday, February 20, 2009

J2ME: Browsing File System on Mobile Device using FileConnection APIs

There can be application where we need to browse through different files on our mobile phone, File Viewer, File Uploader are few to name. Here I’m going to provide a complete program that helps you browse through your file on mobile device and therefore helps you develop more sophisticated applications using it. Its based on J2ME FileConnection APIs (JSR 75) which is a optional package within MIDP. Though the midlet code provided is quite simple and straightforward and needn’t much explanation, however, please consult JavaDoc for FileConnection API for details which is available here. The midlet allows the user to browse through file system on the Java Enabled devices.
There are some other ways to open file connection but they open connection to specific folders e.g. System.getProperty("fileconn.dir.photos") which retruns the path to ‘photos’ folder to which connection can be opened. Moreover this method might not be supported by the some of the devices. In my case, I tried this approach for SE K800i and Nokia 6630 where it worked but not for Motorola V3. In that case, the approach outlined by the code snippet here is more suitable.

Note: FileConnection APIs (JSR 75) being an optional package of MIDP might not be available on the all the devices. So you’ll have to check if the particular device has support for it using system property System.getProperty("microedition.io.file.FileConnection.version").


/**
*
* @author Atifs
*/
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;

public class BrowserLauncher extends MIDlet implements CommandListener {

private Display display;
private final Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
FileSysBrowser fsb;

public BrowserLauncher() {
display = Display.getDisplay(this);
}

protected void startApp() {
fsb = new FileSysBrowser(this);
fsb.showFiles();
}

public Display getDisplay() {
return display;
}

protected void destroyApp(boolean unconditional) {
}

protected void exitApp() {
destroyApp(false);
notifyDestroyed();
}

protected void pauseApp() {
}

public void commandAction(Command c, Displayable d) {
if (c == CMD_EXIT) {
exitApp();
}
}
}


/**
*
* @author Atifs
*/
import java.io.IOException;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import java.util.*;

public class FileSysBrowser extends List implements CommandListener {
// 'DATHHEAD' holds the root, please see javadoc for details
// This may vary for different devices.
String DATHHEAD = "file:///";
private Command CMD_SUBMIT = new Command("Open", Command.ITEM, 0);
private Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
private Command CMD_BACK = new Command("Back", Command.BACK, 1);
private BrowserLauncher fl;
private String path = null;
private String selectedItem = null;
FileConnection srcconn = null;

public FileSysBrowser(BrowserLauncher fl) {
super("File List", List.IMPLICIT);
addCommand(CMD_SUBMIT);
addCommand(CMD_EXIT);
addCommand(CMD_BACK);
setCommandListener(this);
this.fl = fl;
}

protected void showFiles() {
if (path == null) {
Enumeration e = FileSystemRegistry.listRoots();
path = DATHHEAD;
setTitle(path);
while (e.hasMoreElements()) {
String root = (String) e.nextElement();
// you can pass an 'image' object instead of 'null' to
// append method
append(root, null);
}
fl.getDisplay().setCurrent(this);
} else {
if (selectedItem != null) {
setTitle(path + selectedItem);
} else {
setTitle(path);
}
try {
// works when users opens a directory
if (selectedItem != null) {
srcconn = (FileConnection) Connector.open(path + selectedItem, Connector.READ);
} else // works when presses 'Back' to go one level above/up
{
srcconn = (FileConnection) Connector.open(path, Connector.READ);
}
// Check if the selected item is a directory
if (srcconn.isDirectory()) {
if (selectedItem != null) {
path = path + selectedItem;
selectedItem = null;
}
Enumeration files = srcconn.list();
while (files.hasMoreElements()) {
String file = (String) files.nextElement();
append(file, null);
}
//
fl.getDisplay().setCurrent(this);
try {
if (srcconn != null) {
srcconn.close();
srcconn = null;
}
} catch (IOException ex) {
ex.printStackTrace();
}
}//if (srcconn.isDirectory())
else {
// 'else' is unnecessary here, i just out here to signify the
// fact that you handle the selected item if it is a 'file'
// instead of a directory. For example you can display a file
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

protected String getDirectoryName() {
int select = getSelectedIndex();
return getString(select);
}

public void commandAction(Command c, Displayable d) {
if (c == CMD_SUBMIT) {
selectedItem = getDirectoryName();
deleteAll();
showFiles();
}
if (c == CMD_EXIT) {
fl.exitApp();
}
if (c == CMD_BACK) {
if (!path.equals(DATHHEAD)) {
int slashIndex = path.lastIndexOf('/', path.length() - 2);
path = path.substring(0, slashIndex + 1);
if (path.equals(DATHHEAD)) {
path = null;
}
deleteAll();
showFiles();
}
}
}
}


Disclaimer: The code provided has been written for exploratory purposes, not following the official guideline of Software Engineering and therefore has not been tested thoroughly. However, you are FREE to use the code for any purpose and be aware to test it before using for production uses!!!

Wednesday, February 4, 2009

J2ME: Thumbnails Extraction of JPEG (Exif) Images made with Mobile Phone Camera

....continuation
Following are the code snippets for extracting the thumbnail from JPEG image/picture created from mobile phone camera. I have put comments inside the code where necessary, I guess they should be enough to explain the code, however, I would strongly recommend to study this link for understanding the Exif format. This will really help you understand the code. Please the read the comments carefully!!

Disclaimer: The code provided has been written for exploratory purposes (that’s why there are so many console output and debug statements), not following the official guideline of Software Engineering and therefore has not been tested thoroughly but it does provide a good foundation and good starting point if you intend to work on the topic. However, you are FREE to use the code for any purpose and be aware to test it before using for production uses!!!

To start with you need an object of ‘InputStream’ opened to the JPEG file. (We will discuss at some other time how to open a connection to a file). Pass this object to the extractThumbnail(). Rest of stuff is pretty much clear from the comments so I leave you here with it!!!

/*
This method is the starting point, it takes an 'InputStream' object as an
argument to the JPEG file
*/
public int extractThumbnail(InputStream in) throws Exception {
int current;
if (in == null) {
return 0;
}
current = read16Bytes(in);
if (current != 0xFFD8) { //SOI
error("Not a JPEG file");
return 0;
}
current = read16Bytes(in);

// Reading until 'APP' marker is reached
while (!(current == 0xFFE0 || current == 0xFFE1 || current == 0xFFE2 ||
current == 0xFFE3 || current == 0xFFE4 || current == 0xFFE5 ||
current == 0xFFE6 || current == 0xFFE7 || current == 0xFFE8 ||
current == 0xFFE9 || current == 0xFFEA || current == 0xFFEB ||
current == 0xFFEC || current == 0xFFED || current == 0xFFEE ||
current == 0xFFEF)) {
current = read16Bytes(in);
if (current == 0xFFD9) { // End of Image
error("ERROR: File Malformed");
return 0;
}
}
return readAppMarker(in);
}

// Utility Method to throw exceptions
private void error(String message) throws Exception {
throw new Exception(message);
}

// Utility Method to read 2 bytes from the file*/
private final int read16Bytes(InputStream in) throws Exception {
int temp;
try {
temp = in.read();
temp <<= 8;
return temp | in.read();
} catch (IOException e) {
error("read16Bytes() read error: " + e.toString());
return -1;
}
}

// Utility Method to read 1 bytes from the file
private final int read8Bytes(InputStream in) throws Exception {
try {
return in.read();
} catch (IOException e) {
error("read8Bytes() read error: " + e.toString());
return -1;
}
}

// Method that reads the App Marker from the file to the point where
// thumbnail exists...
private int readAppMarker(InputStream in) throws Exception {
boolean debug = false;
// 'Lp' holds the lenght of the marker
int Lp;
// 'content' contains the bytes read from the file
int content = -1;
//'count' for the bytes read from the file
int count = 0;
int offset = 0;
int entries = 0;
// Readin the lenght for marker
Lp = read16Bytes(in);
count += 2;
System.out.println("Length of App1 marker: " + Lp);
if (debug)
System.out.println("Reading App1 identifier\nIdentifier: ");

// Reading the identifier i.e.6 bytes
int iden = 0;
String id = null;
content = read8Bytes(in); //1st byte read of the identifier
count++;
id = Integer.toHexString(content); // This byte should be '45' for Exif or '4a' for JFIF
if (!id.equals("45")) {
while (iden < 5) { // Reading the remaining 5 bytes which are 78, 69, 66, 00, 00
content = read8Bytes(in);
count++; // Incrementing each time a byte is read
iden++;
if (debug)
System.out.print(Integer.toHexString(content));
}
} else {
// Not an exif image
return 0;
}

/* If the image is found to be ''Exif', remaining
* bytes will be following 'TIFF' image format
*/

if (debug) {
System.out.println("\nBytes read for identifier: " + (count - 2));
System.out.println("\n\nReading TIFF Header");
}
// 'tiffCounter' contains the bytes read from the file
// Though i could have used 'count'.....
int tiffCounter = 0;
// 'intelAlign' used as a flag indiacte byte alignment
// either intel (little endian) or
// non-intel(motorola-big endian)
boolean intelAlign = false;

// Reading first 8 bytes for TIFF header
while (tiffCounter < 8) {
if (tiffCounter == 0) {
content = read16Bytes(in);
// count+=2;
tiffCounter += 2;
if (Integer.toHexString(content).equals("4949")) {
intelAlign = true;
}
if (debug) {
System.out.println("Byte Align: " + Integer.toHexString(content));
}
} else {
if (tiffCounter == 2) {
content = read16Bytes(in);
// count += 2;
tiffCounter += 2;
if (debug) {
System.out.println("Tag mark: " + Integer.toHexString(content));
}
} else if (tiffCounter == 4) {
// Reading offset for IFD0 which conatins
// offset for IFD1 which in turn conatins the
// thumbnail
content = read16Bytes(in);
content <<= 16;
content |= read16Bytes(in);
if (intelAlign) {
content = convertIntegerToBigEndian(content);
}
offset = content;
// count += 4;
tiffCounter += 4;
if (debug) {
System.out.println("IFD0 Offset: " + Integer.toHexString(offset));
}
}
}
}
if (debug) {
System.out.println("Bytes read for header: " + tiffCounter);
System.out.println("\n\nBytes to be read for IFD0: " + (offset - tiffCounter));
}

// Reading bytes until reached IFD0
while (offset - tiffCounter != 0) {
content = read8Bytes(in);
tiffCounter++;
if (debug) {
System.out.println("Bytes to be read for IFD0: " + (offset - tiffCounter));
}

}

// Reading IFD0 .......to get the offset for FD01 which
// contains Thumbnail
content = 0;
// reading the number of entries in IFD0
content = read16Bytes(in);
if (intelAlign) {
content = convertShortToBigEndian(content);
}

tiffCounter += 2;
entries = content;
int bytesReadSoFar = tiffCounter;
if (debug) {
System.out.println("\n\nNo. of entries in IFD0: " + entries);
}

// Reading all the entries in IFD0
// 12 bytes for each entry
// The loop is bit elaborated although i could have just skim
// all those byte for all the entries
// I was just exploring it:)
for (int i = 0; i < entries; i++) {

int entryCounter = tiffCounter;
if (debug) {
System.out.println("\n\nScaning entry in IFD0 no. : " + (i + 1));
}
content = read16Bytes(in);
tiffCounter += 2;
if (debug) {
System.out.println("Tag no. : " + content);
}

content = read16Bytes(in);
tiffCounter += 2;
if (debug) {
System.out.println("Data Format : " + content);
}

content = read16Bytes(in);
content |= read16Bytes(in);
tiffCounter += 4;
if (debug) {
System.out.println("no. of Components : " + content);
}

content = read16Bytes(in);
content |= read16Bytes(in);
tiffCounter += 4;
if (debug) {
System.out.println("Either data value or offset to data value : " + content);
System.out.println("Bytes read for entry : " + (tiffCounter - entryCounter));
}
}

if (debug) {
System.out.println("Bytes read for IFD0 : " + (tiffCounter - bytesReadSoFar));
}

// Reading offset for IFD1
content = 0;
content = read16Bytes(in);
content <<= 16;
content |= read16Bytes(in);
if (intelAlign) {
content = convertIntegerToBigEndian(content);
}
tiffCounter += 4;
offset = content;
if (debug) {
System.out.println("\n\nBytes to be read for IFD1 " + (offset - tiffCounter));
}

// Reading bytes until reached IFD1
while (offset - tiffCounter != 0) {
content = read8Bytes(in);
tiffCounter++;
if (debug) {
System.out.println("Bytes to be read for IFD1: " + (offset - tiffCounter));
}
}

// Reading number of entries for IFD1
content = read16Bytes(in);
if (intelAlign) {
content = convertShortToBigEndian(content);
}
tiffCounter += 2;
entries = content;
bytesReadSoFar = tiffCounter;
if (debug) {
System.out.println("\n\nNo. of entries in IFD1: " + entries);
}

// 'data' array contains the info about the thumbnail
int data[] = new int[3];
// Reading the entried for IFD1
// 12 bytes for each entry
for (int i = 0; i < entries; i++) {
int desiredTag = -1;
int entryCounter = tiffCounter;
if (debug) {
System.out.println("\n\nScaning entry in IFD1 no. : " + (i + 1));
}
content = read16Bytes(in);
content = convertShortToBigEndian(content);
tiffCounter += 2;
// tag '103' containd about the format of thumbnail i.e. Jpeg compression
// or TIFF format howeve, we are interested in JPEg compression
// JPEG compression has been standardised by DCS so we expect it to
// JPEG compression for mobile phone cameras
if (Integer.toHexString(content).equals("103")) {
desiredTag = 0;
}
// if JPEG compression tag '201' contains the offset to the thumbnail image
if (Integer.toHexString(content).equals("201")) {
desiredTag = 1;
}
// if JPEG compression tag '202' contains the size of the thumbnail
if (Integer.toHexString(content).equals("202")) {
desiredTag = 2;
}

if (debug) {
System.out.println("Tag no. : " + Integer.toHexString(content));
}

content = read16Bytes(in);
content = convertShortToBigEndian(content);
tiffCounter += 2;
if (debug) {
System.out.println("Data Format : " + content);
System.out.println("Data Format Hex : " + Integer.toHexString(content));
}

content = read16Bytes(in);
content <<= 16;
content |= read16Bytes(in);
content = convertIntegerToBigEndian(content);
tiffCounter += 4;
if (debug) {
System.out.println("no. of Components : " + content);
System.out.println("no. of Components : " + Integer.toHexString(content));
}

content = read16Bytes(in);
content <<= 16;
content |= read16Bytes(in);
content = convertIntegerToBigEndian(content);
tiffCounter += 4;
if (desiredTag != -1) {
data[desiredTag] = content;
}
if (debug) {
System.out.println("Either data value or offset to data value : " + content);
System.out.println("Either data value or offset to data value : " + Integer.toHexString(content));
System.out.println("Bytes read for entry : " + (tiffCounter - entryCounter));
}
}
if (debug) {
System.out.println("Bytes read for IFD1 : " + (tiffCounter - bytesReadSoFar));
}

/*
'data[0]' conatins the thumbnail format
'data[1]' contains the offset to the thumbnail
'data[2]' contains the size of the thumbnail in bytes (which we might not need as we'll pass
to a JPEG Decoder which will take care of it...in fact the length is already there in JPEG
thumbnail image!!!)
*/

// we only proceed only if the thumbnail is JPED compressed
// for which data[0] = 6
if (data[0] != 6) {
error("Not a JPEG file");
return 0;
} else {
// reading bytes until reached the thumbnail
while (data[1] - tiffCounter != 0) {
content = read8Bytes(in);
tiffCounter++;
if (debug) {
System.out.println("Bytes to be read for IFD1: " + (data[1] - tiffCounter));
}
}
}
if (debug) {
System.out.println("Total Length for the marker: " + Lp);
System.out.println("Total bytes read so far: " + (tiffCounter + count));
System.out.println("Bytes left for this marker");
}

/*
At this point, we need a JPEG decoder which starts reading the file
* from this point in the file, that decodes the thumbnail. Remember
* thumbnail is reduced version of actual image but still a JPEG image
* if JPEG Compressed!!
*/
return 1;

}

// Utility Method to convert Short (2bytes) from Little Endian to
// Big Endian
int convertShortToBigEndian(int i) {
return ((i >> 8) & 0xff) + ((i << 8) & 0xff00);
}

// Utility Method to convert Integer (4bytes) from Little Endian to
// Big Endian
int convertIntegerToBigEndian(int i) {
return ((i & 0xff) << 24) + ((i & 0xff00) << 8) + ((i & 0xff0000) >> 8) + ((i >> 24) & 0xff);
}



After the execution of extractThumbnail() method you are at the point where thumbnail exists in the file, it should be noted that thumbnail is scaled down version of the actual image, but some of the markers are missing here. You need a JPEG decoder that decodes the thumbnail for display; there is one free JPEG Decoder available here. It also instructs how to incorporate this decoder in your code.
Well, you might be wondering why we are extracting thumbnail if we still need a JPEG decoder!!! Why don’t we just decode the actual image and produced a reduced version of the image using above mentioned decoder?? The answer is, as I experienced it, it takes very long to decode the full image and producing a thumbnail, the situation is even worst while dealing with image of very high dimension e.g. 2048x1680, which is not good from your application point of view. So it’s a good idea to use the already embedded thumbnail which is far quicker to decode for the decoder, moreover, we should use this if already there because that’s the whole purpose of it!!!!!

Wednesday, January 21, 2009

J2ME: Thumbnails Extraction of JPEG (Exif) Images made with Mobile Phone Camera

Mobile Phones Cameras, being digital, make pictures/images in JPEG format particularly ‘Exif’ flavor of JPEG format. Exif has been decided as a standard for Digital Cameras. We are not going to discuss the process involved in the creation of JPEG images i.e. the encoding process of a raw image into compressed JPEG image which essentially involves three steps in order of Discrete Cosine Transform(DCT), Quantization and Entropy (Huffman) Encoding. Here we are concerned about how data is organized that is the ‘FORMAT’ once a JPEG image has been created. However we do need a JPEG decoder for thumbnail extraction, there is one freely available. First we talk a little about JPEG format and its Exif flavor.
Few Words on JPEG format
A JPEG (jpg) file is organized in order of markers along with their contents. Each marker itself takes 2 bytes. The very first marker (0xFFD8) stands for Start of Image (SOI). This declares that this is a JPEG file. The second marker is (APPn) that depends upon the application using JPEG hence the marker contains an identifier in its contents indicating the application. The marker have any value from APP0 (0xFFE0) to APP15 (0xFFEF) both inclusive.
In JPEG format, immediate 2 bytes after each marker contain the length of the marker’s contents including the length field itself, so is the case with APPn marker. APP0 (0xFFE0) belongs to ‘JFIF’ marker while APP1 (0xFFE1) to ‘Exif’ marker (We are interested in APPn marker particularly APP1 (0xFFE1) marker for extracting thumbnail which is already embedded in the file within this marker). In APPn, after length field, following bytes contain the ASCII code equivalent of the identifier name (5 bytes for ‘JFIF’ and 6bytes for ‘Exif’). Please see the NOTE below. The Exif identifier is 45, 78, 69, 66, 00, 00 (6 bytes). From there on, Exif format is same as TIFF image format is. More detail on Exif (and its embedded TIFF) please see here. After reading a specific number of offset bytes when thumbnail offset is reached, it could be in one of three formats JPEG compressed (most commonly used), RGB TIFF or YCbCr TIFF (The number of offset bytes depends upon ‘byte align’ discussed below). If JPEG compressed, it is just like another JPEG image of a cut down scale which is decoded for display. (I have tried on Sony Ericson K800i and Nokia 6630, both of them had the thumbnail in JPEG Compressed format hence our discussion pertains to this only). Another important thing about TIFF header (8 bytes), embedded in Exif format, is that its first 2 byte informs you about the byte align of TIFF data to be followed that is either little endian (used by Intel) or big endian (used by Motorola). So you have got to look for this thing to calculate the offset while reading Exif file in general and TIFF file in particular. JPEG generally uses big endian however Exif allows both of them. Moreover, most of the digital cameras using Exif format follow little endian. Also remember that all the offset in TIFF are calculated from the first byte of the TIFF header.
NOTE
In case of JFIF, last byte out of 5 contains zero while last two bytes in case of ‘Exif’ contain zero, for example, 4A, 46, 49, 46, 00 (5 bytes)for JFIF. The JFIF v1.02 and above have JFIF extension according to which APP0 has extension part which again starts with application marker hence making two APP markers. The thumbnail may be located either under first marker or under second marker For more detail on JFIF , please see here.
...continued

Wednesday, January 14, 2009

JDOM vs DOM4J

Bear in mind, JDOM and DOM4J are java API for XML processing. JDOM is open source pure Java API for XML processing. JDOM is not an extension or wrapper over W3C DOM model. JDOM is an object model in java that stands for XML document that is the same model as of DOM. JDOM does use SAX API with XML parser (Xerces or Crimson or any other) at the back end to establish the in-memory tree based model.
Note: JDOM uses the defaults parser of JAXP as it calls the parser through JAXP, however, it can be changed to any other parser (Please see the previous post).
You can find this description on the following this link. Like DOM and unlike to SAX, JDOM provides random access to contents of the document hence allowing to update it. JDOM is compatible for conversion to DOM Model or SAX Events and vice versa. Please see this. Unlike DOM, JDOM doesn’t work through interfaces rather it has concrete classes for different components of the model i.e. node, element, attributes etc.
DOM4J, on the other hand like DOM, has done it other way round than JDOM. DOM4J is also XML Processing APIs and follow the same tress based object model for XML document. DOM4J works through interfaces rather than concrete classes. That’s the major difference between DOM4J and JDOM. DOM4J makes navigating along the tree easier than JDOM due to this difference. DOM4J also provides some support for XPath (which, unfortunately, I didn’t get a chance to explore).
The purpose of this and the last three posts is to have a clear understanding of what is what in the area of XML Parsing. I haven’t provided any code here because there is huge coding stuff available on the web for this purpose. I’ll get back to each of them with more detail some other time (fingers crossed!!).

Friday, January 2, 2009

JAXP: Another Player in Java-XML Game

JAXP stands for ‘Java API for XML Processing’. Please, don’t confuse it with other APIs like SAX, DOM, JDOM etc. JAXP is a set of APIs for XML processing making a rather abstract layer in the overall scheme of XML processing. SAX, DOM and JDOM are parsing APIs only, used in parsing of XML document, while JAXP has a broader spectrum of its application, it lets you parse, validate and transform XML documents, here we are concerned with the parsing feature of it however. Remember, JAXP is not parsing APIs nor is it a new way of XML handling in java. As a matter of fact, JAXP provides a convenient way of XML processing. As said before, JAXP exists at the abstract layer; it uses a parser behind the scenes for parsing purposes. JAXP is a bundle of some APIs and a parser. Previously, JAXP was bundled with Crimson parser as the default one; however, JAXP provides the facility to change to some other parser without recompiling the application which has several benefits. (Details can be found on the following link.) These parsers in turn implement SAX, DOM or some other parsing APIs. We are not going into much detail here; please refer to All About JAXP for detail on JAXP along with example code too, this tutorial on JAXP provides quite a comprehensive detail on JAXP highlighting its parsing and validation feature.