/*
* ClientScreen.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.bluetoothjsr82demo;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.L2CAPConnection;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.obex.ClientSession;
import javax.obex.HeaderSet;
import javax.obex.Operation;
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.Display;
import net.rim.device.api.ui.Color;
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.RichTextField;
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.TableModelAdapter;
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;
/**
* Client Screen class for the BluetoothJSR82Demo application
*/
public final class ClientScreen extends MainScreen implements DiscoveryListener {
private int _uuid;
private RichTextField _statusField;
private String _url;
private BluetoothTableModelAdapter _model;
private TableView _view;
private DiscoveryAgent _discoveryAgent;
private final Vector _remoteDevices = new Vector();
/**
* Creates a new ClientScreen object
*/
public ClientScreen(final int uuid) {
super(Manager.NO_VERTICAL_SCROLL);
setTitle("Bluetooth JSR82 Demo Client");
_uuid = uuid;
_statusField = new RichTextField(Field.NON_FOCUSABLE);
add(_statusField);
final MenuItem infoScreen =
new MenuItem(new StringProvider("Info Screen"), 0x230010, 1);
infoScreen.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
UiApplication.getUiApplication().pushScreen(new InfoScreen());
}
}));
addMenuItem(infoScreen);
try {
Dialog.alert("Search for Devices");
_model = new BluetoothTableModelAdapter();
_view = new TableView(_model);
final TableController controller =
new TableController(_model, _view);
controller.setFocusPolicy(TableController.ROW_FOCUS);
_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 Fields
final DataTemplate dataTemplate = new DataTemplate(_view, 1, 1) {
public Field[] getDataFields(final int modelRowIndex) {
final RemoteDevice _remoteDevice =
(RemoteDevice) _model.getRow(modelRowIndex);
final Field[] fields =
{ new LabelField(_remoteDevice.toString(),
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 bluetooth list to the screen
add(_view);
_remoteDevices.setSize(0);
_discoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent();
_discoveryAgent
.startInquiry(DiscoveryAgent.GIAC, ClientScreen.this);
updateStatus("Searching...");
} catch (final BluetoothStateException bse) {
BluetoothJSR82Demo.errorDialog(bse.toString());
}
}
/**
* Updates the text displayed by the status field
*
* @param message
* the text to be displayed
*/
public void updateStatus(final String message) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_statusField.setText(_statusField.getText() + "\n" + message);
}
});
}
/**
* Adapter for displaying Bluetooth device information in table format
*/
private class BluetoothTableModelAdapter extends TableModelAdapter {
/**
* @see net.rim.device.api.ui.component.table.TableModelAdapter#getNumberOfRows()
*/
public int getNumberOfRows() {
return _remoteDevices.size();
}
/**
* @see net.rim.device.api.ui.component.table.TableModelAdapter#getNumberOfColumns()
*/
public int getNumberOfColumns() {
return 1;
}
/**
* @see net.rim.device.api.ui.component.table.TableModelAdapter#doAddRow(Object)
*/
public boolean doAddRow(final Object row) {
_remoteDevices.addElement(row);
return true;
}
/**
* @see net.rim.device.api.ui.component.table.TableModelAdapter#doGetRow(int)
*/
public Object doGetRow(final int index) {
return _remoteDevices.elementAt(index);
}
}
// DiscoveryListener
// methods*************************************************
/**
* @see javax.bluetooth.DiscoveryListener#deviceDiscovered(RemoteDevice,
* DeviceClass)
*/
public void deviceDiscovered(final RemoteDevice remoteDevice,
final DeviceClass cod) {
if (!_remoteDevices.contains(remoteDevice)) {
_model.addRow(remoteDevice);
}
}
/**
* @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int, int)
*/
public void inquiryCompleted(final int discType) {
delete(_statusField);
add(_view);
final MenuItem connectToDevice =
new MenuItem(new StringProvider("Connect to Device"), 0x230020,
0);
connectToDevice.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
ClientScreen.this.deleteAll();
_statusField = new RichTextField();
ClientScreen.this.add(_statusField);
try {
final UUID[] uuidSet = { new UUID(_uuid) };
final int[] attrSet = { 0x0100 };
final RemoteDevice _remoteDevice =
(RemoteDevice) _model.getRow(_view
.getRowNumberWithFocus());
_discoveryAgent.searchServices(attrSet, uuidSet,
_remoteDevice, ClientScreen.this);
} catch (final BluetoothStateException bse) {
BluetoothJSR82Demo.errorDialog(bse.toString());
}
}
}));
addMenuItem(connectToDevice);
}
/**
* @see javax.bluetooth.DiscoveryListener#servicesDiscovered(int,
* ServiceRecord[])
*/
public void servicesDiscovered(final int transID,
final ServiceRecord[] servRecord) {
_url = servRecord[0].getConnectionURL(0, false);
}
/**
* @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int, int)
*/
public void serviceSearchCompleted(final int transID, final int respCode) {
switch (respCode) {
// If the search is completed and the server URL was found,
// connect to the URL and handle the connection.
case DiscoveryListener.SERVICE_SEARCH_COMPLETED:
if (_url != null) {
switch (_uuid) {
case BluetoothJSR82Demo.SPP_UUID:
final SPPConnectThread sppThread = new SPPConnectThread();
sppThread.start();
break;
case BluetoothJSR82Demo.L2CAP_UUID:
final L2CAPConnectThread l2capThread =
new L2CAPConnectThread();
l2capThread.start();
break;
case BluetoothJSR82Demo.OPP_UUID:
final OPPConnectThread oppThread = new OPPConnectThread();
oppThread.start();
break;
}
}
break;
case DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
Dialog.alert("Search service device not reachable");
break;
case DiscoveryListener.SERVICE_SEARCH_NO_RECORDS:
Dialog.alert("Service search has found no records");
break;
case DiscoveryListener.SERVICE_SEARCH_ERROR:
Dialog.alert("Service search error");
break;
case DiscoveryListener.SERVICE_SEARCH_TERMINATED:
Dialog.alert("Service search terminated");
break;
}
}
// SPP CLIENT THREAD
// ********************************************************************
/**
* A thread that connects to an SPP Server, sends a single lined message and
* waits for a reponse.
*/
class SPPConnectThread extends Thread {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
try {
StreamConnection connection = null;
DataOutputStream os = null;
DataInputStream is = null;
try {
// Send the server a request to open a connection
connection = (StreamConnection) Connector.open(_url);
updateStatus("[CLIENT] SPP session created");
// Send a message to the server
final String message = "\nJSR-82 CLIENT says hello!";
updateStatus("[CLIENT] Sending message....");
os = connection.openDataOutputStream();
os.write(message.getBytes());
os.flush();
// Read a message
is = connection.openDataInputStream();
final byte[] buffer = new byte[1024];
final int readBytes = is.read(buffer);
final String receivedMessage =
new String(buffer, 0, readBytes);
updateStatus("[CLIENT] Message received: "
+ receivedMessage);
} finally {
os.close();
is.close();
connection.close();
updateStatus("[CLIENT] SPP session closed");
}
} catch (final IOException ioe) {
BluetoothJSR82Demo.errorDialog(ioe.toString());
}
}
}
// L2CAP CLIENT THREAD
// ********************************************************************
/**
* A thread that connects to a selected L2CAP server, sends a single lined
* message and waits for a response.
*/
class L2CAPConnectThread extends Thread {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
try {
L2CAPConnection connection = null;
try {
// Send the server a request to open a connection
connection = (L2CAPConnection) Connector.open(_url);
updateStatus("[ClIENT] L2CAP session created");
// Send a message to the server
final String message =
"\n[CLIENT] JSR-82 CLIENT says hello!";
updateStatus("Sending message....");
connection.send(message.getBytes());
// Read a message
final byte[] buffer = new byte[1024];
final int readBytes = connection.receive(buffer);
final String receivedMessage =
new String(buffer, 0, readBytes);
updateStatus("[CLIENT] Message received: "
+ receivedMessage);
} finally {
connection.close();
updateStatus("[ClIENT] L2CAP session closed");
}
} catch (final IOException ioe) {
BluetoothJSR82Demo.errorDialog(ioe.toString());
}
}
}
// OPP CLIENT THREAD
// ********************************************************************
/**
* A thread that connects to a selected OPP Server and pushes a single file
* with metadata.
*/
class OPPConnectThread extends Thread {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
try {
Connection connection = null;
OutputStream outputStream = null;
Operation putOperation = null;
ClientSession cs = null;
try {
// Send a request to the server to open a connection
connection = Connector.open(_url);
cs = (ClientSession) connection;
cs.connect(null);
updateStatus("[CLIENT] OPP session created");
// Send a file with meta data to the server
final byte filebytes[] = "[CLIENT] Hello..".getBytes();
final HeaderSet hs = cs.createHeaderSet();
hs.setHeader(HeaderSet.NAME, "test.txt");
hs.setHeader(HeaderSet.TYPE, "text/plain");
hs.setHeader(HeaderSet.LENGTH, new Long(filebytes.length));
putOperation = cs.put(hs);
updateStatus("[CLIENT] Pushing file: " + "test.txt");
updateStatus("[CLIENT] Total file size: "
+ filebytes.length + " bytes");
outputStream = putOperation.openOutputStream();
outputStream.write(filebytes);
updateStatus("[CLIENT] File push complete");
} finally {
outputStream.close();
putOperation.close();
cs.disconnect(null);
connection.close();
updateStatus("[CLIENT] Connection Closed");
}
} catch (final Exception e) {
BluetoothJSR82Demo.errorDialog(e.toString());
}
}
}
}