Está en la página 1de 29

Navigating the file system on a mobile device

Skill Level: Introductory


John Muchow
Author
09 May 2005
This tutorial goes through the steps to build a simple file and directory explorer
application. It includes moving through a directory hierarchy, viewing file permissions,
and opening and reading a file's contents.
Section 1. Before you start
About this tutorial
The Java 2 Platform, Micro Edition (J2ME) Mobile Device Information Profile
(MIDP) does not provide out-of-the-box support for accessing a file system. The
optional package that provides this capability is defined by Java Specification
Request (JSR) 75. Two packages are included in JSR 75: the Personal Information
Management (PIM) package and the FileConnection package. This tutorial focuses
on the FileConnection package, with an emphasis on learning how to navigate a
directory tree and access files.
This tutorial takes you through the steps to build a simple file and directory explorer
application. It includes moving through a directory hierarchy, viewing file
permissions, and opening and reading a file's contents.
This tutorial begins by having you download and install the necessary software. It
takes you through becoming familiar with the FileConnection API, including how to
verify that the API is available on a device. Then, you'll create a simple file system
on your development machine for testing the MIDlet's you will write as you progress
through the tutorial. This entails configuring both the Wireless Toolkit (WTK), as well
as building a file hierarchy on your hard drive.
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 1 of 29
Next, you'll write a MIDlet that navigates the custom file system. This same MIDlet
will also provide support for viewing the attributes of any files you encounter in the
hierarchy. Finally, you'll build upon the previous MIDlet, extending the application to
open, read, and display file contents.
You'll start by downloading and installing the necessary software.
Software prerequisites
You'll need two software tools to complete this tutorial:
The Java Development Kit (JDK): You need to download and install the
JDK. The development kit will allow the applications to compile and
package the J2ME code. Download version 1.4 or greater. Download JDK
version 1.4.2.
The Wireless Toolkit (WTK): The Sun Microsystems Wireless Toolkit is
the integrated tool in this tutorial for building MIDlets. The tool includes
basic project support, as well as compiling, building, packaging, and
running of J2ME applications. Download J2ME Wireless Toolkit 2.2.
Install the software
The Java Development Kit (JDK)
Use the JDK documentation to install the JDK. You can choose either the default
directory or specify another directory. If you choose to specify a directory, make a
note of where you install the JDK. During the installation process for the Wireless
Toolkit, the software attempts to locate the Java Virtual Machine (JVM); if it cannot
locate the JVM, you are prompted for the JDK installation path.
The Wireless Toolkit (WTK)
If you are new to the WTK, you might be interested in the developerWorks tutorial
"MIDlet Development with the Wireless Toolkit" (see Resources), which explains the
basics of creating MIDlets with the toolkit. This tutorial is an excellent starting point if
you are new to the Wireless Toolkit.
The Wireless Toolkit is contained within a single executable file. Run this file to
begin the installation process. It is recommended that you use the default installation
directory. However, if you do not use the default directory, make sure the path you
select does not include any spaces.
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 2 of 29
Section 2. FileConnection API
Check for API existence
One of the first steps a MIDlet needs to perform upon application startup is to check
for the existence of the FileConnection API. This is accomplished by a call to
System.getProperty and passing in the key of
microedition.io.file.FileConnection.version.
In the next few sections I'll walk through the steps to create a MIDlet within the WTK,
demonstrating along the way how to verify the existence of the API.
Create the MIDlet
The basic steps to create and run a MIDlet within the WTK are:
1. Create the project.
2. Write the source code.
3. Compile and preverify the code.
4. Run the MIDlet.
In the next section you'll begin to create a new project.
Create the project
1. Select New Project.
2. Enter the project name and MIDlet class name, as shown in Figure 1.
3. Click Create Project.
Figure 1. Create MobileXplorer project
Setting the project preferences
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 3 of 29
After you enter the project name and select Create Project, the window shown in
Figure 2 appears. It's important to make sure that you enable support for the PDA
profile for J2ME (JSR 75). Select the checkbox as shown in Figure 2 and click OK to
finish the project creation.
Figure 2. Project preferences
Enter the Java source code
There is just one Java source file for this MIDlet: MobileXplorer.java. Copy and
paste the following code into a text editor:
/*****************************************************************************
* MobileXplorer
*****************************************************************************/
import javax.microedition.midlet.*;
/*--------------------------------------------------
* MIDlet definition
*-------------------------------------------------*/
public class MobileXplorer extends MIDlet
{
/*--------------------------------------------------
* Check for FileConnection API, print out separator
*-------------------------------------------------*/
public MobileXplorer()
{
String str = System.getProperty("microedition.io.file.FileConnection.version");
if (str == null)
System.out.println("FileConnection API not available");
else
{
System.out.println("FileConnection API available is version: " + str);
// Printout the type of separator (e.g. \ or / )
System.out.println("The file separator is: " +
System.getProperty("file.separator"));
}
destroyApp(false);
notifyDestroyed();
}
public void startApp()
{
}
public void pauseApp()
{
}
public void destroyApp(boolean cond)
{
}
}
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 4 of 29
Save the source code as MobileXplorer.java in the \src directory within the
MobileXplorer project in the WTK. For instance, if you installed Version 2.2 of the
WTK onto drive C:, your full path might look like:
C:\wtk22\apps\MobileXplorer\src
Save, compile, and preverify
Select Build within the WTK to compile, preverify, and package the MIDlet.
Click Run to start the Application Manager, and select Launch to start the MIDlet.
This MIDlet is quite trivial in that it has no interaction with the device display. Rather,
there is simply a message written to the console indicating the existence of the
FileConnection API.
Figure 3. MIDlet output
In the next section I'll review the source code for this MIDlet.
Code review
The following code retrieves the system property for the FileConnection API. Based
on the results returned, you either print a message stating the API is not available, or
you print a message indicating the version number installed and the file separator
character (which will most likely vary depending on the device operating system).
String str = System.getProperty("microedition.io.file.FileConnection.version");
if (str == null)
System.out.println("FileConnection API not available");
else
{
System.out.println("FileConnection API available is version: " + str);
// Printout the type of separator (e.g. \ or / )
System.out.println("The file separator is: " +
System.getProperty("file.separator"));
}
Now that you've gotten an introduction to building a MIDlet within the WTK and had
a chance to write a short application to verify the existence of the FileConnection
API, let's move on to the next topic, building a custom file system.
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 5 of 29
Section 3. Building a custom file system
Overview
I need to segue to another topic before building the next MIDlet. Let's look a little
deeper at the file system; namely, the correlation between the hard drive, the
wireless toolkit, and the MIDlet.
Because the WTK provides an emulation environment, any MIDlet built with the
WTK will also be emulating access to files and directories. In that regard, the file
system available while using the WTK is your hard drive. Rather than offer up your
entire file structure, the WTK provides access to all files located off the WTK
installation directory, specifically \wtk22\appdb\.
The next section describes the two options that determine which files and directories
are made available to your MIDlet.
WTK default file system, Part 1
The first option as to which files or directories are accessible to your MIDlet is based
on which emulator you choose to run from within the WTK. For instance, if you ran
the first MIDlet you wrote using the default color device, the WTK creates the
directory structure as shown in Figure 4.
Figure 4. Default color device
The filesystem directory and its subdirectory root1 were created by the WTK.
WTK default file system, Part 2
If you run the same MIDlet using the default gray-scale device, the WTK creates the
directory structure as shown in Figure 5.
Figure 5. Default gray device
What this implies is that any directory structure that you create (file and directories)
in these locations is available to the specified device emulator. However, this
approach has one drawback. Let's move to the next section to learn more.
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 6 of 29
File system limitation
Although the WTK creates separate directory structures for you depending on the
emulator you choose, that's double-edged sword. The downside of this approach is
that any files and directories you choose to add must be duplicated in every device
emulator directory structure.
Obviously, this is only a problem if you plan to test your MIDlets with more than one
emulator. However, given that this is a highly recommended practice during the
development cycle, in the next section I'll look at a configuration option that will offer
a solution to this dilemma.
Custom file system
The solution to your problem of having a duplicate file system for each device is
solved by changing a configuration setting within the WTK. Begin by selecting Edit >
Preferences, as shown in Figure 6.
Figure 6. Preferences
Within the preferences window, change the settings within the Storage tab as
displayed in Figure 7.
Figure 7. Storage preferences
The next section disusses the file system created for you.
New directory structure
What you have accomplished through setting your preferences in the WTK is to
specify a root directory where the WTK will look for files and directories, regardless
of which device emulator you choose when running your MIDlets. Figure 8 shows
the actual directories created by the WTK based on the preferences you set.
Figure 8. New directory
Adding to the directory
Before you move on to the next MIDlet, you must add a few more directories to the
new file system you have in place. Create the directories on your hard drive as
shown in Figure 9.
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 7 of 29
Figure 9. Adding new directories
Creating a new file
In the final MIDlet of this tutorial you'll see how to open, read, and display a file. It
seems like a logical point to create the file and write it to your new file system, after
all. Create a text file called sample.txt and save it into the directory hierarchy as
shown in Figure 10.
Figure 10. Text file
You can choose whatever text you like inside the file. However, I suggest you keep it
relatively small to limit the memory requirements of your MIDlet. The text I choose to
place in my file is below:
sample.txt
The path to this file is:
/root3/3b/sample.txt
Now, it's time to move on to creating your next MIDlet, one that will let you navigate
this new directory hierarchy as well as view file properties.
Section 4. Accessing file properties
Overview
This is the first section in which you'll build a MIDlet with any substance. The focus
here is to learn more about the FileConnection API as it pertains to accessing file
property information, including file read/write permissions, file size, and date/time
information.
You'll also build a basic file system browser within this section. The MIDlet will
provide a way to visually move about the file system, including navigation up and
down through a directory tree. You'll tie in the file property information by providing
the option to view file details as you move about the tree.
Create the project
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 8 of 29
Begin by creating a new project as shown in Figure 11:
1. Select New Project.
2. Enter the project name and MIDlet class name, as shown in Figure 11.
3. Click Create Project.
Figure 11. Create MobileXplorer2 project
Setting the project preferences
As you did for your first MIDlet, you need to enable access to the FileConnection API
(JSR 75), as shown in Figure 12.
Figure 12. Project preferences
Enter the Java source code
Continue by copying and pasting the following code into a text editor. After you have
the code up and running you'll come back to review the important details as to how
this MIDlet works.
/*****************************************************************************
* MobileXplorer2
*
* Simple file and directory explorer. Also displays file properties.
*
*****************************************************************************/
import java.util.*;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.file.*;
import javax.microedition.midlet.*;
/*--------------------------------------------------
* Mobile File Explorer MIDlet
*-------------------------------------------------*/
public class MobileXplorer2 extends MIDlet implements CommandListener
{
// For our file system, this will be our root
private final static String ROOT = "/";
// Definitions for directories
private final static String DIRECTORY_INDICATOR = "/";
private final static String UP_DIRECTORY_INDICATOR = "..";
// Holds the full path to the current directory.
// Point to the root at app startup
private String fullPath = ROOT;
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 9 of 29
// Our main display object
// List of files/directories in the current directory
List lstDirectory = null;
// Icons for directory, file, and move-up-one directory
Image imgDirectory = null, imgFile = null, imgUpDirectory = null;
private Command cmExit; // Command to exit
private Command cmSelect; // Command to select dir or file
private Command cmBack; // Command to "go back" one "screen"
/*--------------------------------------------------
* It all starts here. Check for FileConnection API,
* get images, create commands and allocate List
*-------------------------------------------------*/
public MobileXplorer2()
{
if (System.getProperty("microedition.io.file.FileConnection.version") == null)
{
System.out.println("FileConnection API not available");
destroyApp(false);
notifyDestroyed();
}
else
{
// Store references to our images for files and directories
try
{
imgFile = Image.createImage("/file.png");
imgDirectory = Image.createImage("/directory.png");
imgUpDirectory = Image.createImage("/up_directory.png");
}
catch(IOException e)
{ }
// Allocate the List that will hold directory contents
lstDirectory = new List(fullPath, List.IMPLICIT);
// Add commands and listen for events
cmSelect = new Command("Select", Command.ITEM, 1);
cmExit = new Command("Exit", Command.EXIT, 2);
cmBack = new Command("cmBack", Command.BACK, 3);
lstDirectory.addCommand(cmExit);
lstDirectory.addCommand(cmSelect);
lstDirectory.setCommandListener(this);
// Default command when selecting an entry in the List
lstDirectory.setSelectCommand(cmSelect);
}
}
/*--------------------------------------------------
* Get list of roots and display the List object
*-------------------------------------------------*/
public void startApp()
{
// Create a list of root directories
getRootDirectories();
// The List is our main displayable
Display.getDisplay(this).setCurrent(lstDirectory);
}
public void pauseApp()
{
}
public void destroyApp(boolean cond)
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 10 of 29
{
notifyDestroyed();
}
/*--------------------------------------------------
* Create a list of the valid root directories
*-------------------------------------------------*/
private void getRootDirectories()
{
// Get roots
Enumeration enum = FileSystemRegistry.listRoots();
// Clear out the existing List contents
lstDirectory.deleteAll();
// Store entries in vector
while(enum.hasMoreElements())
{
String root = (String) enum.nextElement();
lstDirectory.append(root, imgDirectory);
}
}
/*--------------------------------------------------
* Build list of files in the specified directory
*-------------------------------------------------*/
private void buildFileList(String dir)
{
String fname;
Enumeration enum;
FileConnection fc = null;
try
{
// Open connection to the specified directory
fc = (FileConnection) Connector.open("file://" + dir);
// Enumerate the list of returned files/directories
enum = fc.list("*", true);
// Clear out the existing List contents
lstDirectory.deleteAll();
// Show image that represents going up one directory level
lstDirectory.append("..", imgUpDirectory);
// Loop through all entries, building a List
while(enum.hasMoreElements())
{
fname = (String) enum.nextElement();
// Open connection
fc = (FileConnection) Connector.open("file://" + dir + "/" + fname);
// Append the name, along with an indicator to the List,
// specifying if the entry is a file or directory
lstDirectory.append(fname, fc.isDirectory() ? imgDirectory : imgFile);
}
fc.close();
}
catch (Exception e)
{ }
}
/*--------------------------------------------------
* Directory selected, change to the directory
*-------------------------------------------------*/
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 11 of 29
void changeToDirectory(String dirname)
{
// Selected ".." directory, move up the tree
if (dirname.equals(UP_DIRECTORY_INDICATOR))
{
// Locate the next to last separator so we can remove the path
char separator = fullPath.charAt(0);
int x = fullPath.lastIndexOf(separator, (fullPath.length() - 2));
// Remove the last path entry, as we are moving up the tree
fullPath = fullPath.substring(0, x + 1);
}
else // Drilling down the directory tree
{
// Update variable that holds the full directory path
fullPath += dirname;
}
// We worked our way up the tree back to the root,
// build list of roots
if (fullPath.length() == 1)
getRootDirectories();
else
// Build a list of files/dir given the new path
buildFileList(fullPath);
// Show the new List
Display.getDisplay(this).setCurrent(lstDirectory);
}
/*--------------------------------------------------
* File selected, show its properties
*-------------------------------------------------*/
void displayFileProperties(String fullPath, String filename)
{
try
{
FileConnection fc = (FileConnection) Connector.open(fullPath);
// Build an alert to show file properties
Alert fileinfo = new Alert(filename,
"Last Modified " + new Date(fc.lastModified()) + "\n" +
"Write access: " + (fc.canWrite() ? "yes" : "no") + "\n" +
"Read access: " + (fc.canRead() ? "yes" : "no") + "\n" +
"File size: " + fc.fileSize(),
null, AlertType.INFO);
// Wait for user acknowledgement
fileinfo.setTimeout(Alert.FOREVER);
// Show the alert
Display.getDisplay(this).setCurrent(fileinfo);
fc.close();
}
catch (IOException ioe)
{ }
}
/*--------------------------------------------------
* Manage commands
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
if (c == cmSelect)
{
// Get a reference to the selected List entry
String str = lstDirectory.getString(lstDirectory.getSelectedIndex());
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 12 of 29
// Depending on whether a file or directory was selected...
// Valid directories are "/" or ".."
if(str.endsWith(DIRECTORY_INDICATOR) || str.equals(UP_DIRECTORY_INDICATOR))
changeToDirectory(str);
else
{
// Pass in the full path (including the selected file name)
// as well as the filename itself
displayFileProperties("file://" + fullPath + str, str);
}
}
else if (c == cmExit)
{
destroyApp(false);
notifyDestroyed();
}
}
}
Save the Java source as MobileXplorer2.java in the \src directory within the
MobileXplorer2 project in the WTK. You'll compile and preverify the code in the next
panel.
Save, compile, and preverify
Select Build, and then select Run to compile, preverify, and start the Application
Manager. Select Launch to start the MIDlet.
After this MIDlet is active, you'll see output similar to that shown in Figure 13.
Figure 13. MIDlet file system
File properties
Within the MIDlet, continue to navigate down the directory tree as shown in Figure
14.
Figure 14. Navigate file system
When you navigate to the /root3/3b directory, highlight the sample.txt file and click
Select on the device emulator. An alert window opens and displays various
attributes of the sample.txt file.
Figure 15. File properties
The next few sections show how to build the directory navigation system, as well as
how to acquire and display the file attributes.
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 13 of 29
Code review - variable definition
As with most applications, you begin by defining the necessary data structures and
variables. Look over the definitions below.
// Holds the full path to the current directory. Point to the root at
// app startup
private String fullPath = ROOT;
// Our main display object
// List of files/directories in the current directory
List lstDirectory = null;
// Icons for directory, file, and move-up-one directory
Image imgDirectory = null, imgFile = null, imgUpDirectory = null;
You'll keep a reference to the complete path as you move about a file system;
fullPath stores this value. lstDirectory is a MIDP List object that contains the
directory and a list of the files for the current location in a file system. This same list
is what you display on the device to let the user navigate the file system.
Code review - initialization
Start with initialization of the MIDlet as shown below.
public MobileXplorer2()
{
if (System.getProperty("microedition.io.file.FileConnection.version") == null)
{
System.out.println("FileConnection API not available");
destroyApp(false);
notifyDestroyed();
}
else
{
// Store references to our images for files and directories
try
{
imgFile = Image.createImage("/file.png");
imgDirectory = Image.createImage("/directory.png");
imgUpDirectory = Image.createImage("/up_directory.png");
}
catch(IOException e)
{ }
// Allocate the List that will hold directory contents
lstDirectory = new List(fullPath, List.IMPLICIT);
// Add commands and listen for events
cmSelect = new Command("Select", Command.ITEM, 1);
cmExit = new Command("Exit", Command.EXIT, 2);
cmBack = new Command("cmBack", Command.BACK, 3);
lstDirectory.addCommand(cmExit);
lstDirectory.addCommand(cmSelect);
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 14 of 29
lstDirectory.setCommandListener(this);
// Default command when selecting an entry in the List
lstDirectory.setSelectCommand(cmSelect);
}
}
As you've done previously, you start by checking for the existence of the
FileConnection API. Next, you allocate a MIDP List object to store references to the
file and directories. You add Command objects to your List to assist with selecting
directories and files, as well as providing a means to exit the MIDlet. The final step is
to set the "select" Command as the default, which will provide the default action
when selecting an option in your list of files and directories.
Note: One other point worth mentioning is that I have opted to use image files to
create a better user experience for navigating the file system. This is an optional
parameter to the List object, so you can comment out the three lines above if you
choose not to use image files.
Code review - startup
Starting the MIDlet is nothing more than building a list of the root directories and
displaying the List object, which contains the same.
public void startApp()
{
// Create a list of root directories
getRootDirectories();
// The List is our main displayable
Display.getDisplay(this).setCurrent(lstDirectory);
}
The following code creates the root directory list. The first thing to notice is the call to
FileSystemRegistry.listRoots(), which returns an enumeration of the roots.
You clear out any existing entries in the List and walk through the enumeration to
rebuild its contents.
/*--------------------------------------------------
* Create a list of the valid root directories
*-------------------------------------------------*/
private void getRootDirectories()
{
// Get roots
Enumeration enum = FileSystemRegistry.listRoots();
// Clear out the existing List contents
lstDirectory.deleteAll();
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 15 of 29
// Store entries in vector
while(enum.hasMoreElements())
{
String root = (String) enum.nextElement();
lstDirectory.append(root, imgDirectory);
}
}
Code review - command processing
With the application up and running, you now need to think about how to process
user input.
public void commandAction(Command c, Displayable s)
{
if (c == cmSelect)
{
// Get a reference to the selected List entry
String str = lstDirectory.getString(lstDirectory.getSelectedIndex());
// Depending on whether a file or directory was selected...
// Valid directories are "/" or ".."
if(str.endsWith(DIRECTORY_INDICATOR) || str.equals(UP_DIRECTORY_INDICATOR))
changeToDirectory(str);
else
{
// Pass in the full path (including the selected file name)
// as well as the filename itself
displayFileProperties("file://" + fullPath + str, str);
}
}
else if (c == cmExit)
{
destroyApp(false);
notifyDestroyed();
}
}
Other than a request to exit the MIDlet, there is only one other option of concern,
and that is what to do after a user chooses the "select" command. The first test is to
see if the selected entry is a directory. If so, call the changeToDirectory()
method, passing in the selected entry. If the selected entry is a file, you want to
display its properties, so you call displayFileProperties() and pass the full
path to the selected object, as well as the selected entry. Later in this tutorial, you'll
see how these values are used.
Code review - changing directories
After a request to change directories, you end up here. Your first check is to see if
the user would like to move up the tree. If so, remove the last entry on the path to
build what is now the new path. If a request was made to move down the tree, add
the selected entry to the existing path to create the new full path.
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 16 of 29
void changeToDirectory(String dirname)
{
// Selected ".." directory, move up the tree
if (dirname.equals(UP_DIRECTORY_INDICATOR))
{
// Locate the next-to-last separator so you can remove the path
char separator = fullPath.charAt(0);
int x = fullPath.lastIndexOf(separator, (fullPath.length() - 2));
// Remove the last path entry, as we are moving up the tree
fullPath = fullPath.substring(0, x + 1);
}
else // Drilling down the directory tree
{
// Update variable that holds the full directory path
fullPath += dirname;
}
// We worked our way up the tree back to the root,
// build list of roots
if (fullPath.length() == 1)
getRootDirectories();
else
// Build a list of files/dir given the new path
buildFileList(fullPath);
// Show the new List
Display.getDisplay(this).setCurrent(lstDirectory);
}
With the newly constructed full path, first check to see if the user has worked their
way back to the root. If so, rebuild the root directories as you did at application
startup. Otherwise, create a new list of the files and directories based on the value of
the new full path. The next section looks at the buildFileList() method.
Code review - build file list
To build a list of files and directories, start with a call to Connector.open(),
passing in the directory (path) that you'd like to open. The result is an enumeration,
so as before, clear the List object and work your way through the enumeration,
adding the files and directories as you go.
private void buildFileList(String dir)
{
String fname;
Enumeration enum;
FileConnection fc = null;
try
{
// Open connection to the specified directory
fc = (FileConnection) Connector.open("file://" + dir);
// Enumerate the list of returned files/directories
enum = fc.list("*", true);
// Clear out the existing List contents
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 17 of 29
lstDirectory.deleteAll();
// Show image that represents going up one directory level
lstDirectory.append("..", imgUpDirectory);
// Loop through all entries, building a List
while(enum.hasMoreElements())
{
fname = (String) enum.nextElement();
// Open connection
fc = (FileConnection) Connector.open("file://" + dir + "/" + fname);
// Append the name, along with an indicator to the List,
// specifying if the entry is a file or directory
lstDirectory.append(fname, fc.isDirectory() ? imgDirectory : imgFile);
}
fc.close();
}
catch (Exception e)
{ }
}
A couple of points to clarify. Notice that you always include a reference to moving up
the directory tree using "..". Also, depending on whether the current enumeration
entry is a file or directory, the appropriate image is appended along with the name of
the entry.
Code review - file properties
The last stop in the code review is to display the file properties whenever a file is
selected.
void displayFileProperties(String fullPath, String filename)
{
try
{
FileConnection fc = (FileConnection) Connector.open(fullPath);
// Build an alert to show file properties
Alert fileinfo = new Alert(filename,
"Last Modified " + new Date(fc.lastModified()) + "\n" +
"Write access: " + (fc.canWrite() ? "yes" : "no") + "\n" +
"Read access: " + (fc.canRead() ? "yes" : "no") + "\n" +
"File size: " + fc.fileSize(),
null, AlertType.INFO);
// Wait for user acknowledgement
fileinfo.setTimeout(Alert.FOREVER);
// Show the alert
Display.getDisplay(this).setCurrent(fileinfo);
fc.close();
}
catch (IOException ioe)
{ }
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 18 of 29
}
After opening a connection to the file (notice you must pass in the full path), create
an Alert window to hold the property information. Populate the window, set the type
to modal, which will require acknowledgement from the user before dismissing the
window, and display the Alert.
Read-only files
As a sanity check, change the file attributes for the sample.txt file to read-only. This
gives you a chance to verify that your code is working as advertised. Navigate to the
sample.txt file in Windows Explorer and right-click on the file. Change the attribute
to read-only as shown in Figure 16.
Figure 16. Change file attribute
Verify file property
After changing the file attribute, restart the MIDlet and navigate back down to the
sample.txt file. Notice that the write-access flag is set to "no," indicating that the
attribute has been changed, and no longer allows write access to the file.
Figure 17. File attribute
In the next section, in addition to showing file properties, you'll add support for
opening and displaying file contents.
Section 5. Opening and reading a file
Overview
Your last MIDlet builds upon the concepts you've covered so far to add one last
feature -- namely, the option to open a file and display its contents. To keep things
simple, you'll create a new project for the final MIDlet. From within the WTK, create a
new project with the name MobileXplorer3, as shown in Figure 18.
Figure 18. MobileXplorer3
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 19 of 29
Setting the project preferences
Ensure that the project preferences include support for the PDA profile by selecting
the checkbox as shown in Figure 19 and clicking OK to create the project.
Figure 19. Project preferences
Enter the Java source code
Copy and paste the following code into a text editor:
/*****************************************************************************
* MobileXplorer3
*
* Simple file and directory explorer. Displays file properties as well as
* file contents.
*
*****************************************************************************/
import java.util.*;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.file.*;
import javax.microedition.midlet.*;
/*--------------------------------------------------
* Mobile File Explorer MIDlet
*-------------------------------------------------*/
public class MobileXplorer3 extends MIDlet implements CommandListener
{
// For our file system, this will be our root
private final static String ROOT = "/";
// Definitions for directories
private final static String DIRECTORY_INDICATOR = "/";
private final static String UP_DIRECTORY_INDICATOR = "..";
// Holds the full path to the current directory. Point to the root at
// app startup
private String fullPath = ROOT;
// Our main display object.
// List of files/directories in the current directory
List lstDirectory = null;
// Icons for directory, file, and move-up-one directory
Image imgDirectory = null, imgFile = null, imgUpDirectory = null;
private Command cmExit; // Exit app
private Command cmSelect; // View file properties
private Command cmRead; // View file contents
private Command cmBack; // Exit textbox showing file contents
/*--------------------------------------------------
* It all starts here. Check for FileConnection API
* get images, create commands and allocate List
*-------------------------------------------------*/
public MobileXplorer3()
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 20 of 29
{
if (System.getProperty("microedition.io.file.FileConnection.version") == null)
{
System.out.println("FileConnection API not available");
destroyApp(false);
notifyDestroyed();
}
else
{
// Store references to our images for files and directories
try
{
imgFile = Image.createImage("/file.png");
imgDirectory = Image.createImage("/directory.png");
imgUpDirectory = Image.createImage("/up_directory.png");
}
catch(IOException e)
{ }
}
// Allocate the List that will hold directory contents
lstDirectory = new List(fullPath, List.IMPLICIT);
// Add commands and listen for events
cmExit = new Command("Exit", Command.EXIT, 1);
cmSelect = new Command("Select", Command.ITEM, 1);
cmRead = new Command("Read", Command.ITEM, 2);
cmBack = new Command("Back", Command.BACK, 1);
lstDirectory.addCommand(cmExit);
lstDirectory.addCommand(cmSelect);
lstDirectory.addCommand(cmRead);
lstDirectory.setCommandListener(this);
// Default command when selecting an entry in the List
lstDirectory.setSelectCommand(cmSelect);
}
/*--------------------------------------------------
* Get list of roots and display the List object
*-------------------------------------------------*/
public void startApp()
{
// Create a list of root directories
getRootDirectories();
// The List is our main displayable
Display.getDisplay(this).setCurrent(lstDirectory);
}
public void pauseApp()
{
}
public void destroyApp(boolean cond)
{
notifyDestroyed();
}
/*--------------------------------------------------
* Create a list of the valid root directories
*-------------------------------------------------*/
private void getRootDirectories()
{
// Get roots
Enumeration enum = FileSystemRegistry.listRoots();
// Clear out the existing List contents
lstDirectory.deleteAll();
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 21 of 29
// Store entries in vector
while(enum.hasMoreElements())
{
String root = (String) enum.nextElement();
lstDirectory.append(root, imgDirectory);
}
}
/*--------------------------------------------------
* Build list of files in the specified directory
*-------------------------------------------------*/
private void buildFileList(String dir)
{
String fname;
Enumeration enum;
FileConnection fc = null;
try
{
// Open connection to the specified directory
fc = (FileConnection) Connector.open("file://" + dir);
// Enumerate the list of returned files/directories
enum = fc.list("*", true);
// Clear out the existing List contents
lstDirectory.deleteAll();
// Show image that represents going up one directory level
lstDirectory.append("..", imgUpDirectory);
// Loop through all entries, building a List
while(enum.hasMoreElements())
{
fname = (String) enum.nextElement();
// Open connection
fc = (FileConnection) Connector.open("file://" + dir + "/" + fname);
// Append the name, along with an indicator to the List,
// specifying if the entry is a file or directory
lstDirectory.append(fname, fc.isDirectory() ? imgDirectory : imgFile);
}
fc.close();
}
catch (Exception e)
{ }
}
/*--------------------------------------------------
* Directory selected, change to the directory
*-------------------------------------------------*/
void changeToDirectory(String dirname)
{
// Selected ".." directory, move up the tree
if (dirname.equals(UP_DIRECTORY_INDICATOR))
{
// Locate the next to last separator so we can remove the path
char separator = fullPath.charAt(0);
int x = fullPath.lastIndexOf(separator, (fullPath.length() - 2));
// Remove the last path entry, as we are moving up the tree
fullPath = fullPath.substring(0, x + 1);
}
else // Drilling down the directory tree
{
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 22 of 29
// Update variable that holds the full directory path
fullPath += dirname;
}
// We worked our way up the tree back to the root,
// build list of roots
if (fullPath.length() == 1)
getRootDirectories();
else
// Build a list of files/dir given the new path
buildFileList(fullPath);
// Show the new List
Display.getDisplay(this).setCurrent(lstDirectory);
}
/*--------------------------------------------------
* File selected, show its properties
*-------------------------------------------------*/
void displayFileProperties(String fullPath, String filename)
{
try
{
FileConnection fc = (FileConnection) Connector.open(fullPath);
// Build an alert to show file properties
Alert fileinfo = new Alert(filename,
"Last Modified " + new Date(fc.lastModified()) + "\n" +
"Write access: " + (fc.canWrite() ? "yes" : "no") + "\n" +
"Read access: " + (fc.canRead() ? "yes" : "no") + "\n" +
"File size: " + fc.fileSize(),
null, AlertType.INFO);
// Wait for user acknowledgement
fileinfo.setTimeout(Alert.FOREVER);
// Show the alert
Display.getDisplay(this).setCurrent(fileinfo);
fc.close();
}
catch (IOException ioe)
{ }
}
/*--------------------------------------------------
* File selected, show its contents
* We assume the selected file contains pure text
*-------------------------------------------------*/
void displayFileContents(String fullPath, String filename)
{
// Textbox to hold the file contents, set it to read-only
TextBox tbx = new TextBox(filename, null, 2048,
TextField.UNEDITABLE | TextField.ANY);
// Create a byte array to hold file contents
byte[] b = new byte[2048];
// Connection and input stream
FileConnection fc = null;
InputStream is = null;
try
{
// Open file and stream
fc = (FileConnection) Connector.open(fullPath);
is = fc.openInputStream();
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 23 of 29
// Read no more than 2048 bytes
int len = is.read(b, 0, 2048);
// Here's how we get back from the file viewer textbox
tbx.addCommand(cmBack);
tbx.setCommandListener(this);
// Set textbox contents based on file read results...
if (len > 0)
{
// Place contents into the textbox
tbx.setString(new String(b, 0, len));
}
else
tbx.setString("Unable to read file!");
// Display the textbox and the file contents
Display.getDisplay(this).setCurrent(tbx);
is.close();
fc.close();
}
catch(Exception e)
{ }
}
/*--------------------------------------------------
* Manage commands
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
if (c == cmExit)
{
destroyApp(false);
notifyDestroyed();
}
else if (c == cmBack)
{
// Selected from the textbox showing a files contents,
// go back to our main list of files/directories
Display.getDisplay(this).setCurrent(lstDirectory);
}
else
{
// Get a reference to the selected List entry
String str = lstDirectory.getString(lstDirectory.getSelectedIndex());
// Select command...
if (c == cmSelect)
{
// Depending on whether a file or directory was selected...
// Valid directories are "/" or ".."
if(str.endsWith(DIRECTORY_INDICATOR) || str.equals(UP_DIRECTORY_INDICATOR))
changeToDirectory(str);
else
{
// Pass in the full path (including the selected file name)
// as well as the filename itself
displayFileProperties("file://" + fullPath + str, str);
}
}
else // Read file contents...
{
System.out.println("read file...");
displayFileContents("file://" + fullPath + str, str);
}
}
}
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 24 of 29
}
Save the source code as MobileXplorer3.java in the \src directory within the
MobileXplorer3 project in the WTK.
Save, compile, and preverify
Select Build within the WTK to compile, preverify, and package the MIDlet.
Click Run to start the Application Manager, and choose Launch to start the MIDlet.
Navigate file system
Navigate your way down the directory tree as shown in Figure 20.
Figure 20. Navigate file system
View file contents
The final step is to select Menu in the device emulator (see the left screenshot in
Figure 21).
Figure 21. View file contents
After you select the read option in the menu, the MIDlet opens the file, reads its
contents, and displays the results as shown to the right in Figure 21.
Code review - new command
Much of the code for the MIDlet is identical to the previous MIDlet. However, there
are a few minor additions to walk through. The first change is the addition of one
more command (cmRead) to request the MIDlet to open, read, and display a file.
private Command cmExit; // Exit app
private Command cmSelect; // View file properties
private Command cmRead; // View file contents
private Command cmBack; // Exit textbox showing file contents
Code review - command processing
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 25 of 29
The next change you must make is to add support for handling the new command.
public void commandAction(Command c, Displayable s)
{
if (c == cmExit)
{
destroyApp(false);
notifyDestroyed();
}
else if (c == cmBack)
{
// Selected from the textbox showing a file's contents,
// go back to our main list of files/directories
Display.getDisplay(this).setCurrent(lstDirectory);
}
else
{
// Get a reference to the selected List entry
String str = lstDirectory.getString(lstDirectory.getSelectedIndex());
// Select command...
if (c == cmSelect)
{
// Depending on whether a file or directory was selected...
// Valid directories are "/" or ".."
if(str.endsWith(DIRECTORY_INDICATOR) || str.equals(UP_DIRECTORY_INDICATOR))
changeToDirectory(str);
else
{
// Pass in the full path (including the selected file name)
// as well as the filename itself
displayFileProperties("file://" + fullPath + str, str);
}
}
else // Read file contents...
{
System.out.println("read file...");
displayFileContents("file://" + fullPath + str, str);
}
}
}
Much of the code is the same as before, except for a minor adjustment to add a call
to displayFileContents() when requested by the user. The next section
discusses the details of this method.
Code review - display file
The first parameter passed to this method is the full path of the file selected. You'll
use this as the title of the TextBox that will hold the file contents. The second
parameter is the file name to open.
After creating the new TextBox, create a byte array that will store what you read
from the file. You define a FileConnection and an InputStream as the way to read
the file contents.
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 26 of 29
void displayFileContents(String fullPath, String filename)
{
// Textbox to hold the file contents, set it to read-only
TextBox tbx = new TextBox(filename, null, 2048, TextField.UNEDITABLE | TextField.ANY);
// Create a byte array to hold file contents
byte[] b = new byte[2048];
// Connection and input stream
FileConnection fc = null;
InputStream is = null;
try
{
// Open file and stream
fc = (FileConnection) Connector.open(fullPath);
is = fc.openInputStream();
// Read no more than 2048 bytes
int len = is.read(b, 0, 2048);
// Here's how we get back from the file viewer textbox
tbx.addCommand(cmBack);
tbx.setCommandListener(this);
// Set textbox contents based on file read results...
if (len > 0)
{
// Place contents into the textbox
tbx.setString(new String(b, 0, len));
}
else
tbx.setString("Unable to read file!");
// Display the textbox and the file contents
Display.getDisplay(this).setCurrent(tbx);
is.close();
fc.close();
}
catch(Exception e)
{ }
}
After the file is open, you read up to 2048 bytes (a limitation set to reduce the
memory requirements of the MIDlet). A command is added to the TextBox to go
back to the previous screen, and, finally, you populate the textbox with the file
contents. The last few steps are to request the TextBox be displayed and to clean up
the connections.
Now you have a MIDlet that can navigate a file system, show file properties, and
open, read, and display file contents. Not bad for just over 300 lines of code!
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 27 of 29
Section 6. Summary
Summary
I've covered a great deal of ground in this tutorial. I began with an introduction to the
FileConnection API, the foundation of JSR 75. In this same section, you also wrote
your first MIDlet to verify the existence of the API on the device/emulator.
The second task was to build a custom file system that could be accessed by all
emulators within the WTK. You created a directory hierarchy and also wrote a small
text file that you would put to use in the additional MIDlets you wrote.
With the file system in place, you wrote your second MIDlet to both navigate the file
system and to display file properties. This gave you an opportunity to see first-hand
how to open a file connection and obtain attribute information for a file. To round out
the discussion on working with file systems, you wrote one last application that
would open, read, and display the contents of a file.
At this point you have a good foundation for further development using the
FileConnection API. Although by no means was your final MIDlet a full-fledged file
explorer, it should be a sufficient starting point for understanding the nuances of the
API and serve as a catalyst for more comprehensive file system applications.
developerWorks ibm.com/developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 28 of 29
Resources
Learn
The Java Specification Request for PDA Optional Packages JSR 75 can be
found on the Java Community Process Web site.
The Java Community Process page provides a list of all JSRs.
"MIDlet development with the Wireless Toolkit" (developerWorks, March 2003)
guides you through the basic steps for MIDlet development with J2ME.
Stay current with developerWorks technical events and Webcasts.
Get products and technologies
You can download the Java Development Kit and the J2ME Wireless Toolkit
from the Sun Developer Network.
Build your next development project with IBM trial software, available for
download directly from developerWorks.
Discuss
Participate in developerWorks blogs and get involved in the developerWorks
community.
About the author
John Muchow
John Muchow is the author of Core J2ME Technology and MIDP, a
popular J2ME book that has been translated into several foreign
languages.
ibm.com/developerWorks developerWorks
Navigating the file system on a mobile device Trademarks
Copyright IBM Corporation 2005. All rights reserved. Page 29 of 29

También podría gustarte