Wednesday, September 2, 2009

Books: a new study case

Updated on 31/May/2010. New features on version 1.4

I had some TXT books and wanted to read them on my cell phone.
So I made this application and shared it.

Features:


  • Fullscreen landscape reading with biggest font available.

  • Recent files list, so you do not need to browse for files every time you open the application.

  • Resume reading from the same line you were (with progress bar since version 1.1) - no key pressing just to find the point you were before.

  • Press and hold to fast forward or rewind with increasing speed (since 1.1).

  • Languages: English, Español, Français, Português (since 1.2).

  • Read UTF-8 encoded files (since 1.3).

  • Color verification to guarantee scroll bar is always visible (since 1.3).

  • Presenting error dialog when an unexpected situation occurs (since 1.3).

  • Checking file size before trying to open it (since 1.4).

  • Line number presented on footer during read (since 1.4).

  • Better touch support: go to previous pages (since 1.4).



You can open files using JSR 75 - FileConnection.
When you enter "Open File" screen your cell phone might ask you to grant read access to the application.
With Sony Ericsson I had to grant it only once, at the first execution.
With Nokia it was necessary to grant every folder the application was trying to read.
If some unexpected situation occurs an error dialog will be presented with details.
You can send me these details by email.

The content is shown in landscape/fullscreen mode thanks to Sprite transforms, so MIDP 2 is required.

To use the maximum available space just for text I did not set title nor Commands on main Canvas and treated keyPressed to identify softkeys.

Each file read is kept on a "Recents" screen.
A List where the top most items are the most recent files opened by the application.
I used a simple RecordStore to keep this list of files and where in each file we were reading.

Give it a try and, if you like it, write a nice review. ;)

Related topics:

Sunday, August 23, 2009

Fullscreen Landscape

Most handset screens are portrait: higher than wider. What if you want to show content in landscape mode? Since MIDP 2.0 we can use an Sprite to rotate an image in 90 degrees.


First thing we need is an Image of the correct size. Following code could be inside the constructor of a class that extends Canvas - considering an Sprite and an Image attributes:



int width = Math.max(super.getWidth(), super.getHeight());
int height = Math.min(super.getWidth(), super.getHeight());
screen = Image.createImage(width, height);
sprite = new Sprite(screen);
if (super.getWidth() < super.getHeight()) { // portrait screen
sprite.setTransform(Sprite.TRANS_ROT90);
sprite.setPosition(0, 0);
}

When painting your content use the mutable Image Graphics, then update the sprite with the image like bellow.



protected void paint(Graphics g1) {
Graphics g = screen.getGraphics();
// ... do your drawing
this.sprite.setImage(screen, screen.getWidth(), screen.getHeight());
sprite.paint(g1);
}

How can you use the biggest possible area on the handset display?



  1. Do not setTitle on your Canvas

  2. Do not addCommand to your Canvas

  3. Call setFullScreenMode(true) before calling super.getWidth() and super.getHeight()

Monday, August 17, 2009

Open File Dialog

Ok, this is not a Dialog... It is an Implicit List. As we do not have File Dialogs on LCDUI I decided to share my version with you. It did work with Sony Ericson and Motorola iDEN handsets and I use it in my Books application.

import java.io.IOException;
import java.util.Enumeration;
import java.util.Stack;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.io.file.FileSystemRegistry;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.Ticker;


public class OpenFileDialog extends List
implements CommandListener
{
public static final String PREFIX = "file:///";
private static final String UP = "[ .. ]";
private static final String DIR = " + ";
private Stack stack = new Stack();
private OpenFileListener listener;
private CommandListener commandListener;
private String extension;

public OpenFileDialog (OpenFileListener listener,
String title, String extension)
{
super(title, List.IMPLICIT);
super.setCommandListener(this);
this.listener = listener;
this.extension = extension;
addRoots();
}

public void setCommandListener(CommandListener l) {
this.commandListener = l;
}

public void commandAction(Command c, Displayable d) {
if (c == List.SELECT_COMMAND) {
this.changeSelection();
} else {
if (this.commandListener != null) {
this.commandListener.commandAction(c, d);
}
}
}

private String getSelectedText () {
int index = getSelectedIndex();
if (index < 0 || index >= this.size()) {
return "";
}
return this.getString(index);
}

private void changeSelection () {
String target = this.getSelectedText();
String parent = null;
boolean goingUp = false;

if (UP.equals(target)) {
goingUp = true;
stack.pop();
if (stack.isEmpty() == false) {
target = (String) stack.peek();
} else {
super.deleteAll();
addRoots();
this.setTicker(null);
return;
}
} else {
if (stack.isEmpty() == false) {
parent = stack.peek().toString();
}
}

try {
if (target.startsWith(DIR)) {
target = target.substring(3);
}
if (parent != null) {
target = parent + target;
}
this.setTicker(new Ticker(target));
FileConnection fc = (FileConnection)
Connector.open(PREFIX + target, Connector.READ);
if (fc.isDirectory()) {
super.deleteAll();
if (goingUp == false) {
stack.push(target);
}
super.append(UP, null);

Enumeration entries = fc.list();

while (entries.hasMoreElements()) {
String entry = (String) entries.nextElement();
FileConnection fc2 = (FileConnection)
Connector.open(PREFIX + target + entry,
Connector.READ);
if (fc2.isDirectory()) {
super.append(DIR + entry, null);
} else if (entry.toLowerCase().endsWith(extension)) {
super.append(entry, null);
}
fc2.close();
}
fc.close();
} else {
this.listener.fileSelected(fc);
}
} catch (IOException e) {
e.printStackTrace();
}

}

private void addRoots() {
Enumeration roots = FileSystemRegistry.listRoots();
while (roots.hasMoreElements()) {
super.append(DIR + roots.nextElement().toString(), null);
}
}

}

Related topics: