/*
* FileExplorerDemoScreen.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.fileexplorerdemo;
import java.util.Enumeration;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.io.file.FileSystemRegistry;
import net.rim.blackberry.api.invoke.CameraArguments;
import net.rim.blackberry.api.invoke.Invoke;
import net.rim.device.api.command.Command;
import net.rim.device.api.command.CommandHandler;
import net.rim.device.api.command.ReadOnlyCommandMetadata;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.table.AbstractTableModel;
import net.rim.device.api.ui.component.table.DataTemplate;
import net.rim.device.api.ui.component.table.TableController;
import net.rim.device.api.ui.component.table.TableModel;
import net.rim.device.api.ui.component.table.TableView;
import net.rim.device.api.ui.component.table.TemplateColumnProperties;
import net.rim.device.api.ui.component.table.TemplateRowProperties;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.decor.BackgroundFactory;
import net.rim.device.api.util.StringProvider;
/**
* Main screen displays list of all directories/files
*/
public final class FileExplorerDemoScreen extends MainScreen {
private FileExplorerDemo _uiApp;
private FileExplorerDemoJournalListener _fileListener;
private String _parentRoot;
private AbstractTableModel _model;
private TableView _view;
/**
* Creates a new FileExplorerDemoScreen object
*/
FileExplorerDemoScreen() {
super(Manager.NO_VERTICAL_SCROLL);
setTitle("File Explorer Demo");
_model = new TableModel();
_view = new TableView(_model);
final TableController controller = new TableController(_model, _view);
controller.setFocusPolicy(TableController.ROW_FOCUS);
controller.setCommand(new Command(new CommandHandler() {
/**
* @see CommandHandler#execute(ReadOnlyCommandMetadata, Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
selectAction();
}
}));
_view.setController(controller);
// Set the highlight style for the view
_view.setDataTemplateFocus(BackgroundFactory
.createLinearGradientBackground(Color.LIGHTBLUE,
Color.LIGHTBLUE, Color.BLUE, Color.BLUE));
// Create a data template that will format the model data as an array of
// LabelFields
final DataTemplate dataTemplate = new DataTemplate(_view, 1, 1) {
public Field[] getDataFields(final int modelRowIndex) {
final FileExplorerDemoFileHolder fileholder =
(FileExplorerDemoFileHolder) _model
.getRow(modelRowIndex);
String text;
if (fileholder.isDirectory()) {
text = fileholder.getPath();
} else {
text = fileholder.getFileName();
}
final Field[] fields =
{ new LabelField(text, DrawStyle.ELLIPSIS
| Field.NON_FOCUSABLE) };
return fields;
}
};
// Define the regions of the data template and column/row size
dataTemplate.createRegion(new XYRect(0, 0, 1, 1));
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(
Display.getWidth()));
dataTemplate.setRowProperties(0, new TemplateRowProperties(24));
_view.setDataTemplate(dataTemplate);
dataTemplate.useFixedHeight(true);
// Add the contact list to the screen
add(_view);
// Populate the table
readRoots(null);
_uiApp = (FileExplorerDemo) UiApplication.getUiApplication();
_fileListener = new FileExplorerDemoJournalListener(this);
_uiApp.addFileSystemJournalListener(_fileListener);
}
/**
* Overrides super. Removes listener before closing the screen
*
* @see net.rim.device.api.ui.Screen#close()
*/
public void close() {
_uiApp.removeFileSystemJournalListener(_fileListener);
super.close();
}
/**
* Deletes the selected file or directory
*/
private void deleteAction() {
final int index = _view.getRowNumberWithFocus();
final FileExplorerDemoFileHolder fileholder =
(FileExplorerDemoFileHolder) _model.getRow(index);
if (fileholder != null) {
final String filename =
fileholder.getPath() + fileholder.getFileName();
if (Dialog.ask(Dialog.D_DELETE) == Dialog.DELETE) {
FileConnection fc = null;
try {
fc = (FileConnection) Connector.open("file:///" + filename);
fc.delete();
_model.removeRowAt(index);
} catch (final Exception ex) {
FileExplorerDemo
.errorDialog("Unable to delete file or directory: "
+ filename);
} finally {
try {
if (fc != null) {
fc.close();
fc = null;
}
} catch (final Exception ioex) {
FileExplorerDemo.errorDialog("deleteAction() threw "
+ ioex.toString());
}
}
}
}
}
/**
* Overrides default. Enter key will take action on directory/file. Escape
* key will go up one directory or close application if at top level.
*
* @see net.rim.device.api.ui.Screen#keyChar(char,int,int)
*
*/
public boolean keyChar(final char c, final int status, final int time) {
if (c == Characters.ESCAPE && goBack()) {
return true;
}
return super.keyChar(c, status, time);
}
/**
* Creates the menu to be used in the application
*
* @see net.rim.device.api.ui.container.MainScreen#makeMenu(Menu,int)
*/
public void makeMenu(final Menu menu, final int instance) {
/*
* Menu item for invoking the camera application. This provides a
* convenient method of adding a file to the device file system in order
* to demonstrate the FileSystemJournalListener.
*/
final MenuItem cameraItem =
new MenuItem(new StringProvider("Camera"), 0x230010, 0);
cameraItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA,
new CameraArguments());
}
}));
// Menu item for deleting the selected file
final MenuItem deleteItem =
new MenuItem(new StringProvider("Delete"), 0x230020, 1);
deleteItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
deleteAction();
}
}));
// Menu item for displaying information on the selected file
final MenuItem selectItem =
new MenuItem(new StringProvider("Select"), 0x230030, 2);
selectItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
selectAction();
}
}));
// Menu item for going back one directory in the directory hierarchy
final MenuItem backItem =
new MenuItem(new StringProvider("Go Back"), 0x230040, 3);
backItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
goBack();
}
}));
// Only display the menu if no actions are performed
if (instance == Menu.INSTANCE_DEFAULT && _model.getNumberOfRows() > 0) {
if (DeviceInfo.hasCamera()) {
menu.add(cameraItem);
}
// Add delete item if selected item is not a directory and is not a
// file in read only system partition.
if (_view.getRowNumberWithFocus() < _model.getNumberOfRows()) {
final FileExplorerDemoFileHolder fileholder =
(FileExplorerDemoFileHolder) _model.getRow(_view
.getRowNumberWithFocus());
if (!fileholder.isDirectory()
&& !fileholder.getPath().startsWith("system/")) {
menu.add(deleteItem);
menu.add(selectItem);
}
if (_parentRoot != null) {
menu.add(backItem);
}
}
}
super.makeMenu(menu, instance);
}
/**
* Reads the path that was passed in and enumerates through it
*
* @param root
* Path to be read.
*/
private void readRoots(final String root) {
_parentRoot = root;
// Clear list contents
while (_model.getNumberOfRows() > 0) {
_model.removeRowAt(0);
}
FileConnection fc = null;
Enumeration rootEnum = null;
if (root != null) {
// Open the file system and get the list of directories/files
try {
fc =
(FileConnection) Connector.open("file:///" + root,
Connector.READ);
rootEnum = fc.list();
} catch (final Exception ioex) {
FileExplorerDemo.errorDialog(ioex.toString());
} finally {
if (fc != null) {
// Everything is read, make sure to close the connection
try {
fc.close();
fc = null;
} catch (final Exception ioex) {
FileExplorerDemo.errorDialog("readRoots() threw "
+ ioex.toString());
}
}
}
}
// There was no root to read, so now we are reading the system roots
if (rootEnum == null) {
rootEnum = FileSystemRegistry.listRoots();
}
// Read through the list of directories/files
while (rootEnum.hasMoreElements()) {
String file = (String) rootEnum.nextElement();
if (root != null) {
file = root + file;
}
readSubroots(file);
}
}
/**
* Reads all the directories and files from the provided path
*
* @param file
* Upper directory to be read.
*/
private void readSubroots(final String file) {
FileConnection fc = null;
try {
fc =
(FileConnection) Connector.open("file:///" + file,
Connector.READ);
// Create a file holder from the FileConnection so that the
// connection is not left open
final FileExplorerDemoFileHolder fileholder =
new FileExplorerDemoFileHolder(file);
fileholder.setDirectory(fc.isDirectory());
_model.addRow(fileholder);
} catch (final Exception ioex) {
FileExplorerDemo.errorDialog("Connector.open() threw "
+ ioex.toString());
} finally {
if (fc != null) {
// Everything is read, make sure to close the connection
try {
fc.close();
fc = null;
} catch (final Exception ioex) {
FileExplorerDemo.errorDialog("readSubRoots() threw "
+ ioex.toString());
}
}
}
}
/**
* Displays information on the selected file
*
* @return True.
*/
private boolean selectAction() {
if (_model.getNumberOfRows() <= 0) {
return false;
}
final FileExplorerDemoFileHolder fileholder =
(FileExplorerDemoFileHolder) _model.getRow(_view
.getRowNumberWithFocus());
if (fileholder != null) {
// If it's a directory then show what's in the directory
if (fileholder.isDirectory()) {
readRoots(fileholder.getPath());
} else {
// It's a file so display information on it
_uiApp.pushScreen(new FileExplorerDemoScreenFileInfoPopup(
fileholder));
}
}
return true;
}
/**
* Updates the list of files
*/
public void updateList() {
synchronized (_uiApp.getAppEventLock()) {
readRoots(_parentRoot);
}
;
}
/**
* Goes back one directory in the directory hierarchy, if possible
*
* @return True if we went back a directory; false otherwise
*/
private boolean goBack() {
if (_parentRoot != null) {
String backParentRoot =
_parentRoot.substring(0, _parentRoot.lastIndexOf('/'));
backParentRoot =
backParentRoot.substring(0,
backParentRoot.lastIndexOf('/') + 1);
if (backParentRoot.length() > 0) {
readRoots(backParentRoot);
} else {
readRoots(null);
}
return true;
}
return false;
}
}