Implementing the file browser functionality

The FileSelector class is the main file browser class and forms the bulk of the MIDlet. It creates the UI for navigating the device file system and selecting images to view. It also provides support for file management operations such as delete, rename, and directory creation.

The FileSelector checks whether the fileconn.dir.photos system property is specified and, if specified, opens the file browser in that directory. If the system property is not specified, FileSelector displays a list of available roots instead.

To implement the FileSelector class:

  1. Create the FileSelector.java class file.

  2. Import the required packages and classes.

    import java.io.*;
    import java.util.*;
    import javax.microedition.io.*;
    import javax.microedition.io.file.*;
    import javax.microedition.lcdui.*;
  3. Set FileSelector to extend List and implement CommandListener and FileSystemListener . Create the required folder icons, Commands, and other variables.

    // Simple file selector class.
    // It navigates the file system and shows images currently available
    class FileSelector
            extends List
            implements CommandListener, FileSystemListener {
    
        private final static Image ROOT_IMAGE =
                ImageViewerMIDlet.makeImage("/root_1.png");
        private final static Image FOLDER_IMAGE =
                ImageViewerMIDlet.makeImage("/folder1.png");
        private final static Image FILE_IMAGE =
                ImageViewerMIDlet.makeImage("/file1.png");
        private final OperationsQueue queue = new OperationsQueue();
        private final static String FILE_SEPARATOR =
                (System.getProperty("file.separator") != null) ? System.getProperty("file.separator") : "/";
        private final static String UPPER_DIR = "..";
        private final ImageViewerMIDlet midlet;
        private final Command openCommand =
                new Command("Open", Command.ITEM, 1);
        private final Command createDirCommand =
                new Command("Create new directory", Command.SCREEN, 2);
        private final Command deleteCommand =
                new Command("Delete", Command.ITEM, 3);
        private final Command renameCommand =
                new Command("Rename", Command.ITEM, 4);
        private final Command exitCommand =
                new Command("Exit", Command.EXIT, 1);
        private final static int RENAME_OP = 0;
        private final static int MKDIR_OP = 1;
        private final static int INIT_OP = 2;
        private final static int OPEN_OP = 3;
        private final static int DELETE_OP = 4;
        private Vector rootsList = new Vector();
        // Stores the current root, if null we are showing all the roots
        private FileConnection currentRoot = null;
        private Ticker ticker = new Ticker("Image Viewer");
  4. Add the Commands and a CommandListener, and implement the commandAction method.

        FileSelector(ImageViewerMIDlet midlet) {
            super("Image Viewer", List.IMPLICIT);
            setTicker(ticker);
            this.midlet = midlet;
            addCommand(openCommand);
            addCommand(createDirCommand);
            addCommand(deleteCommand);
            addCommand(renameCommand);
            addCommand(exitCommand);
            setSelectCommand(openCommand);
            setCommandListener(this);
        }
    
        // ...
    
        public void commandAction(Command c, Displayable d) {
            if (c == openCommand) {
                queue.enqueueOperation(new ImageViewerOperations(OPEN_OP));
            } else if (c == renameCommand) {
                queue.enqueueOperation(new ImageViewerOperations(RENAME_OP));
            } else if (c == deleteCommand) {
                queue.enqueueOperation(new ImageViewerOperations(DELETE_OP));
            } else if (c == createDirCommand) {
                queue.enqueueOperation(new ImageViewerOperations(MKDIR_OP));
            } else if (c == exitCommand) {
                midlet.fileSelectorExit();
            }
        }
  5. Create a set of methods for initializing and stopping the FileSystemListener.

        void initialize() {
            queue.enqueueOperation(new ImageViewerOperations(INIT_OP));
            FileSystemRegistry.addFileSystemListener(FileSelector.this);
        }
    
        void stop() {
            queue.abort();
            FileSystemRegistry.removeFileSystemListener(this);
        }
    
        void inputReceived(String input, int code) {
            switch (code) {
                case RENAME_OP:
                    queue.enqueueOperation(new ImageViewerOperations(
                            input,
                            RENAME_OP));
                    break;
                case MKDIR_OP:
                    queue.enqueueOperation(new ImageViewerOperations(
                            input,
                            MKDIR_OP));
                    break;
            }
        }
  6. Create methods for listening to changes in the displayed folder and displaying the current root directory.

        // Listen for changes in the roots
        public void rootChanged(int state, String rootName) {
            queue.enqueueOperation(new ImageViewerOperations(INIT_OP));
        }
    
        private void displayAllRoots() {
            ticker.setString("Image Viewer - [Roots]");
            deleteAll();
            Enumeration roots = rootsList.elements();
            while (roots.hasMoreElements()) {
                String root = (String) roots.nextElement();
                root = root.replace('/', FILE_SEPARATOR.charAt(0));
                append(root.substring(1), ROOT_IMAGE);
            }
            currentRoot = null;
        }
    
        // ...
    
        private void loadRoots() {
            if (!rootsList.isEmpty()) {
                rootsList.removeAllElements();
            }
            try {
                Enumeration roots = FileSystemRegistry.listRoots();
                while (roots.hasMoreElements()) {
                    rootsList.addElement("/" +
                            (String) roots.nextElement());
                }
            } catch (Throwable e) {
                midlet.showMsg(e.getMessage());
            }
    
        }
  7. Create methods for handling the creation of new directories.

        private void createNewDir() {
            if (currentRoot == null) {
                midlet.showMsg("Is not possible to create a new root");
            } else {
                midlet.requestInput("New dir name", "", MKDIR_OP);
            }
        }
    
        private void createNewDir(String newDirURL) {
            if (currentRoot != null) {
                try {
                    FileConnection newDir =
                            (FileConnection) Connector.open(
                            currentRoot.getURL() + newDirURL,
                            Connector.WRITE);
                    newDir.mkdir();
                } catch (IOException e) {
                    midlet.showError(e);
                }
                displayCurrentRoot();
            }
        }
  8. Create a method for deleting the currently selected item.

        private void deleteCurrent() {
            if (currentRoot == null) {
                midlet.showMsg("Is not possible to delete a root");
            } else {
                int selectedIndex = getSelectedIndex();
                if (selectedIndex >= 0) {
                    String selectedFile = getString(selectedIndex);
                    if (selectedFile.equals(UPPER_DIR)) {
                        midlet.showMsg("Is not possible to delete an upper dir");
                    } else {
                        try {
                            String tmp = selectedFile.replace(FILE_SEPARATOR.charAt(0), '/');
                            FileConnection fileToDelete =
                                    (FileConnection) Connector.open(
                                    currentRoot.getURL() + tmp,
                                    Connector.READ_WRITE);
                            if (!fileToDelete.exists()) {
                                midlet.showMsg("File " + fileToDelete.getName() + " does not exists");
                            } else {
                                if (getConfirmation("Do you really want to delete " + tmp + "?")) {
                                    fileToDelete.delete();
                                    midlet.showMsg(tmp + " deleted.");
                                } else {
                                    midlet.showMsg("Operation cancelled.");
                                }
                            }
                        } catch (IOException e) {
                            midlet.showError(e);
                        } catch (SecurityException e) {
                            midlet.showError(e);
                        }
                        displayCurrentRoot();
                    }
                }
            }
        }
  9. Create methods for renaming the currently selected item.

        private void renameCurrent() {
            if (currentRoot == null) {
                midlet.showMsg("Is not possible to rename a root");
            } else {
                int selectedIndex = getSelectedIndex();
                if (selectedIndex >= 0) {
                    String selectedFile = getString(selectedIndex);
                    if (selectedFile.equals(UPPER_DIR)) {
                        midlet.showMsg("Is not possible to rename the upper dir");
                    } else {
                        midlet.requestInput("New name", selectedFile, RENAME_OP);
                    }
                }
            }
        }
    
        private void renameCurrent(String newName) {
            if (currentRoot == null) {
                midlet.showMsg("Is not possible to rename a root");
            } else {
                int selectedIndex = getSelectedIndex();
                if (selectedIndex >= 0) {
                    String selectedFile = getString(selectedIndex);
                    if (selectedFile.equals(UPPER_DIR)) {
                        midlet.showMsg("Is not possible to rename the upper dir");
                    } else {
                        try {
                            String tmp = selectedFile.replace(FILE_SEPARATOR.charAt(0), '/');
                            FileConnection fileToRename =
                                    (FileConnection) Connector.open(
                                    currentRoot.getURL() + tmp,
                                    Connector.READ_WRITE);
    
                            if (fileToRename.exists()) {
                            	if(fileToRename.isDirectory())
                            	{
                            		if(newName.indexOf('/')>-1)
                            			newName = newName.substring(0,newName.indexOf('/'));
                            	}	
                            	else
                                {
                            		newName = newName.replace(FILE_SEPARATOR.charAt(0), '/');
                                }
                                fileToRename.rename(newName);
                            } else {
                                midlet.showMsg("File " + fileToRename.getName() + " does not exists");
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            midlet.showError(e);
                        } catch (SecurityException e) {
                            e.printStackTrace();
                            midlet.showError(e);
                        }
                        displayCurrentRoot();
                    }
                }
            }
        }
  10. Create a method for handling the actions that occur when the currently selected item is opened. If the item is a folder, it is made the currently visible folder (defined as currentRoot). If the item is an image file, it is opened in the ImageCanvas .

        private void openSelected() {
    
            int selectedIndex = getSelectedIndex();
            if (selectedIndex >= 0) {
                String selectedFile = getString(selectedIndex);
                if (selectedFile.endsWith(FILE_SEPARATOR)) {
                    try {
                        String tmp = selectedFile.replace(FILE_SEPARATOR.charAt(0), '/');
                        if (currentRoot == null) {
                            currentRoot = (FileConnection) Connector.open(
                                    "file:///" + tmp, Connector.READ);
                        } else {
                            currentRoot.setFileConnection(tmp);
                        }
                        displayCurrentRoot();
                    } catch (IOException e) {
                        midlet.showError(e);
                    } catch (SecurityException e) {
                        midlet.showError(e);
                    }
                } else if (selectedFile.equals(UPPER_DIR)) {
                    if (rootsList.contains(currentRoot.getPath() + currentRoot.getName())) {
                        displayAllRoots();
                    } else {
                        try {
                            currentRoot.setFileConnection(UPPER_DIR);
                            displayCurrentRoot();
                        } catch (IOException e) {
                            midlet.showError(e);
                        }
                    }
                } else {
                    String url = currentRoot.getURL() + selectedFile;
                    midlet.displayImage(url);
                }
            }
        }
  11. Create a method for drawing the file hierarchy in the currently visible folder.

        private void displayCurrentRoot() {
            try {
                ticker.setString("Image Viewer - [" + currentRoot.getURL() + "]");
                // open the root
                deleteAll();
                append(UPPER_DIR, FOLDER_IMAGE);
                // list all dirs
                Enumeration listOfDirs = currentRoot.list("*", false);
                while (listOfDirs.hasMoreElements()) {
    
                    String currentDir = ((String) listOfDirs.nextElement());
    
                    if (currentDir.endsWith("/")) {
                        String tmp = currentDir.replace('/', FILE_SEPARATOR.charAt(0));
                        append(tmp, FOLDER_IMAGE);                    // always display the platform specific seperator to the user
    
                    }
                }
                // list all png files and dont show hidden files
                Enumeration listOfFiles = currentRoot.list("*.png", false);
                while (listOfFiles.hasMoreElements()) {
                    String currentFile = (String) listOfFiles.nextElement();
                    if (currentFile.endsWith(FILE_SEPARATOR)) {
                        append(currentFile, FOLDER_IMAGE);
                    } else {
                        append(currentFile, FILE_IMAGE);
                    }
                }
                listOfFiles = currentRoot.list("*.jpg", false);
                while (listOfFiles.hasMoreElements()) {
                    String currentFile = (String) listOfFiles.nextElement();
                    if (currentFile.endsWith(FILE_SEPARATOR)) {
                        append(currentFile, FOLDER_IMAGE);
                    } else {
                        append(currentFile, FILE_IMAGE);
                    }
                }
                listOfFiles = currentRoot.list("*.bmp", false);
                while (listOfFiles.hasMoreElements()) {
                    String currentFile = (String) listOfFiles.nextElement();
                    if (currentFile.endsWith(FILE_SEPARATOR)) {
                        append(currentFile, FOLDER_IMAGE);
                    } else {
                        append(currentFile, FILE_IMAGE);
                    }
                }
                //Making the top item visible.
                setSelectedIndex(0, true);
            } catch (IOException e) {
                midlet.showError(e);
            } catch (SecurityException e) {
                midlet.showError(e);
            }
        }
  12. Create an inner class for handling the actual user actions in the file manager. When the user selects an action (select, create, rename, or delete), the following class launches the corresponding method using an operations queue.

        private class ImageViewerOperations implements Operation {
    
            private final String parameter;
            private final int operationCode;
    
            ImageViewerOperations(int operationCode) {
                this.parameter = null;
                this.operationCode = operationCode;
            }
    
            ImageViewerOperations(String parameter, int operationCode) {
                this.parameter = parameter;
                this.operationCode = operationCode;
            }
    
            public void execute() {
                switch (operationCode) {
                    case INIT_OP:
                        String initDir = System.getProperty("fileconn.dir.photos");
                        loadRoots();
                        if (initDir != null) {
                            try {
                                currentRoot =
                                        (FileConnection) Connector.open(
                                        initDir,
                                        Connector.READ);
                                displayCurrentRoot();
                            } catch (Exception e) {
                                midlet.showError(e);
                                displayAllRoots();
                            }
                        } else {
                            displayAllRoots();
                        }
                        break;
                    case OPEN_OP:
                        openSelected();
                        break;
                    case DELETE_OP:
                        deleteCurrent();
                        break;
                    case RENAME_OP:
    
                        if (parameter != null) {
                            renameCurrent(parameter);
                        } else {
                            renameCurrent();
                        }
                        break;
                    case MKDIR_OP:
                        if (parameter != null) {
                            createNewDir(parameter);
                        } else {
                            createNewDir();
                        }
                }
            }
        }
  13. Create a confirmation dialog for sensitive file operations, such as delete.

        boolean getConfirmation(String message) {
            Object lock = new Object();
            Confirm prompt = new Confirm(message, lock, this);
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
            // set current displayable, when notification arrives
            Display.getDisplay(midlet).setCurrent(this);
            return prompt.getResponse();
        }
    
        // ...
    
        private class Confirm extends Alert implements CommandListener {
    
            private Command okCommand = new Command("Yes", Command.OK, 0);
            private Command nokCommand = new Command("No", Command.EXIT, 0);
            private boolean isOkResponse;
            private Object waitLock;
    
            Confirm(String message, Object lock, Displayable next) {
                super("Image Viewer", message, midlet.getLogo(), AlertType.CONFIRMATION);
                addCommand(okCommand);
                addCommand(nokCommand);
                setCommandListener(this);
                waitLock = lock;
                Display.getDisplay(midlet).setCurrent(this, next);
            }
    
            public void commandAction(Command command, Displayable display) {
                if (command == okCommand) {
                    isOkResponse = true;
                } else if (command == nokCommand) {
                    isOkResponse = false;
                }
                synchronized (waitLock) {
                    waitLock.notifyAll();
                }
            }
    
            boolean getResponse() {
                return isOkResponse;
            }
        }
    }

Now that you have implemented the file browser functionality, implement the image viewing functionality.