/*
* SPPScreen.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.bluetoothdemo;
import java.io.IOException;
import net.rim.device.api.bluetooth.BluetoothSerialPort;
import net.rim.device.api.bluetooth.BluetoothSerialPortInfo;
import net.rim.device.api.bluetooth.BluetoothSerialPortListener;
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.ui.Field;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.Arrays;
import net.rim.device.api.util.DataBuffer;
import net.rim.device.api.util.StringProvider;
/**
* This class handles the bluetooth communication between two devices.
* Connecting, waiting for a connection, sending and receiving messages and
* displaying the information sent between the two devices are all handled by
* this class.
*/
public class SPPScreen extends MainScreen implements
BluetoothSerialPortListener {
private RichTextField _rtf;
private final StringBuffer _data;
private final byte[] _receiveBuffer = new byte[1024];
private BluetoothSerialPort _port;
private boolean _loopback;
private static boolean _dataSent = true;
private String _deviceName;
private final DataBuffer _db;
/**
* Constructs a serial port connection screen.
*
* @param info
* The information describing the bluetooth serial port to
* connect to. If 'null' then a new serial port is opened on this
* device.
*/
public SPPScreen(final BluetoothSerialPortInfo info) {
// Fill an lk array with the 'a' character.
Arrays.fill(_receiveBuffer, (byte) 'a');
// Initialize the buffers
_data = new StringBuffer();
_db = new DataBuffer();
try {
if (info == null) {
// Open a port to listen for incoming connections
_rtf =
new RichTextField(
"Connect external device and then type something...",
Field.NON_FOCUSABLE);
_port =
new BluetoothSerialPort(
"Hi there",
BluetoothSerialPort.BAUD_115200,
BluetoothSerialPort.DATA_FORMAT_PARITY_NONE
| BluetoothSerialPort.DATA_FORMAT_STOP_BITS_1
| BluetoothSerialPort.DATA_FORMAT_DATA_BITS_8,
BluetoothSerialPort.FLOW_CONTROL_NONE, 1024,
1024, this);
_deviceName = "unknown";
} else {
// Connect to the selected device
_rtf =
new RichTextField("Type something...",
Field.NON_FOCUSABLE);
_port =
new BluetoothSerialPort(
info,
BluetoothSerialPort.BAUD_115200,
BluetoothSerialPort.DATA_FORMAT_PARITY_NONE
| BluetoothSerialPort.DATA_FORMAT_STOP_BITS_1
| BluetoothSerialPort.DATA_FORMAT_DATA_BITS_8,
BluetoothSerialPort.FLOW_CONTROL_NONE, 1024,
1024, this);
_deviceName = info.getDeviceName();
}
} catch (final IOException ex) {
BluetoothDemo.errorDialog(ex.toString());
}
add(_rtf);
// Closes the screen
final MenuItem closeSP =
new MenuItem(new StringProvider("Close serial port"), 0x230010,
0);
closeSP.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
close();
}
}));
// Displays the DTR line's state
final MenuItem dtr =
new MenuItem(new StringProvider("Get DTR"), 0x230020, 1);
dtr.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
try {
Status.show("DTR: " + _port.getDtr());
} catch (final IOException ioex) {
Status.show("");
BluetoothDemo
.errorDialog("BluetoothSerialPort#getDtr() threw "
+ ioex.toString());
}
}
}));
// Turns DSR on
final MenuItem dsrOn =
new MenuItem(new StringProvider("DSR on"), 0x230030, 2);
dsrOn.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
Status.show("DSR on");
try {
_port.setDsr(true);
} catch (final IOException ioex) {
Status.show("");
BluetoothDemo
.errorDialog("BluetoothSerialPort#setDsr(boolean) threw "
+ ioex.toString());
}
}
}));
// Turns DSR off
final MenuItem dsrOff =
new MenuItem(new StringProvider("DSR off"), 0x230040, 3);
dsrOff.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
Status.show("DSR off");
try {
_port.setDsr(false);
} catch (final IOException ioex) {
Status.show("");
BluetoothDemo
.errorDialog("BluetoothSerialPort#setDsr(boolean) threw "
+ ioex.toString());
}
}
}));
// Enables loop back
final MenuItem enableLoopback =
new MenuItem(new StringProvider("Enable loopback"), 0x230050, 4);
enableLoopback.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
_loopback = true;
}
}));
// Disables loop back
final MenuItem disableLoopback =
new MenuItem(new StringProvider("Disable loopback"), 0x230060,
5);
disableLoopback.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
_loopback = false;
}
}));
// Sends 1kb of information to the other device
final MenuItem send1k =
new MenuItem(new StringProvider("Send 1k"), 0x230070, 6);
send1k.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
writeData(_receiveBuffer, 0, _receiveBuffer.length);
}
}));
// Add menu items to the screen
addMenuItem(closeSP);
addMenuItem(dtr);
addMenuItem(dsrOn);
addMenuItem(dsrOff);
addMenuItem(enableLoopback);
addMenuItem(disableLoopback);
addMenuItem(send1k);
}
/**
* Alerts the user if a device was connected or if it failed to connect
*
* @param success
* True if the connection was successful, false otherwise
* @see net.rim.device.api.bluetooth.BluetoothSerialPortListener#deviceConnected(boolean)
*/
public void deviceConnected(final boolean success) {
if (success) {
Status.show("Bluetooth SPP connected to " + _deviceName);
} else {
Status.show("Bluetooth SPP failed to connect to " + _deviceName);
}
}
/**
* @see net.rim.device.api.bluetooth.BluetoothSerialPortListener#deviceDisconnected()
*/
public void deviceDisconnected() {
Status.show("Disconnected from " + _deviceName);
}
/**
* @see net.rim.device.api.bluetooth.BluetoothSerialPortListener#dtrStateChange(boolean)
*/
public void dtrStateChange(final boolean high) {
Status.show("DTR: " + high);
}
/**
* @see net.rim.device.api.bluetooth.BluetoothSerialPortListener#dataReceived(int)
*/
public void dataReceived(final int length) {
int len;
try {
// Read the data that arrived
if ((len =
_port.read(_receiveBuffer, 0,
length == -1 ? _receiveBuffer.length : length)) != 0) {
// If loopback is enabled write the data back
if (_loopback) {
writeData(_receiveBuffer, 0, len);
} else {
if (len == 1 && _receiveBuffer[0] == '\r') {
_receiveBuffer[1] = '\n';
++len;
}
}
// Update the screen with the new data that arrived
_data.append(new String(_receiveBuffer, 0, len));
_rtf.setText(_data.toString());
}
} catch (final IOException ioex) {
BluetoothDemo.errorDialog(ioex.toString());
}
}
/**
* @see net.rim.device.api.bluetooth.BluetoothSerialPortListener#dataSent()
*/
public void dataSent() {
// Set the _dataSent flag to true to allow more data to be written
_dataSent = true;
// Call sendData in case there is data waiting to be sent
sendData();
}
/*
* Invoked when a key is pressed
*
* @see net.rim.device.api.ui.Screen#keyChar(char,int,int)
*/
public boolean keyChar(final char key, final int status, final int time) {
if (!(key == Characters.ESCAPE)) {
// Send the key if a Bluetooth connection has been established
if (_port != null) {
if (key == '\n') {
writeData((byte) '\r');
} else {
writeData((byte) key);
}
// Update the screen adding the character just pressed
_data.append(key);
_rtf.setText(_data.toString());
return true;
}
}
return super.keyChar(key, status, time);
}
/**
* Writes a byte to the 'send' buffer
*
* @param theData
* The byte to write to the buffer
*/
private void writeData(final byte theData) {
synchronized (_db) {
_db.write(theData);
// Call sendData to send the data
sendData();
}
}
/**
* Writes data from a source byte array to the 'send' buffer
*
* @param theData
* The source data to write to the buffer
* @param offset
* The off set in the source data to write to the buffer
* @param length
* The length of the array of bytes to write to the buffer
*/
private void writeData(final byte[] theData, final int offset,
final int length) {
synchronized (_db) {
_db.write(theData, offset, length);
// Call sendData to send the data
sendData();
}
}
/**
* Sends the data currently stored in the DataBuffer to the other device
*/
private void sendData() {
// Ensure we have data to send
if (_db.getArrayLength() > 0) {
// Ensure the last write call has resulted in the sending of the
// data
// prior to calling write again. Calling write in sequence without
// waiting
// for the data to be sent can overwrite existing requests and
// result in
// data loss.
if (_dataSent) {
try {
// Set the _dataSent flag to false so we don't send any more
// data until it has been verified that this data was sent.
_dataSent = false;
synchronized (_db) {
// Write out the data in the DataBuffer and reset the
// DataBuffer
_port.write(_db.getArray(), 0, _db.getArrayLength());
_db.reset();
}
} catch (final IOException ioex) {
// Reset _dataSent to true so we can attempt another data
// write
_dataSent = true;
BluetoothDemo.errorDialog("Failed to write data: "
+ ioex.toString());
}
} else {
System.out
.println("Can't send data right now, data will be sent after dataSent notify call.");
}
}
}
/**
* @see net.rim.device.api.ui.Screen#close()
*/
public void close() {
if (_port != null) {
_port.close();
}
super.close();
}
}