/** * SMSDemo.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.smsdemo; import java.io.IOException; import java.util.Vector; import javax.microedition.io.Connection; import javax.microedition.io.Connector; import javax.microedition.io.Datagram; import javax.wireless.messaging.Message; import javax.wireless.messaging.MessageConnection; import javax.wireless.messaging.TextMessage; 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.io.DatagramBase; import net.rim.device.api.io.DatagramConnectionBase; import net.rim.device.api.io.SmsAddress; import net.rim.device.api.system.RadioInfo; import net.rim.device.api.system.SMSPacketHeader; import net.rim.device.api.system.SMSParameters; import net.rim.device.api.ui.Field; import net.rim.device.api.ui.MenuItem; import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.component.BasicEditField; import net.rim.device.api.ui.component.Dialog; import net.rim.device.api.ui.component.EditField; import net.rim.device.api.ui.component.SeparatorField; import net.rim.device.api.ui.container.MainScreen; import net.rim.device.api.util.StringProvider; /** * A demo application for sending and receiving SMS messages. Running this * program on a BlackBerry smartphone simulator requires the associated server * component found in the com.rim.samples.server.smsdemo package under the * samples directory in your JDE installation folder. This application * demonstrates sending and receiving SMS messages on both CDMA and GSM enabled * BlackBerry smartphones. CDMA devices require port 0 to be specified in the * call to Connector.open(). When running on a GSM device, this application * gives the user the option of the SMS message being routed to the inbox of the * SMS and MMS application as well as being received by this application's * listening thread. */ public class SMSDemo extends UiApplication { private static final int MAX_PHONE_NUMBER_LENGTH = 32; private static final String NON_ZERO_PORT_NUMBER = "3590"; private EditField _sendText; private EditField _address; private EditField _status; private ListeningThread _listener; private SendThread _sender; private Connection _conn; private String _port = "0"; // Cached for improved performance private final StringBuffer _statusMsgs = new StringBuffer(); /** * Determines whether the currently active WAF is CDMA * * @return True if currently active WAF is CDMA, otherwise false */ private static boolean isCDMA() { return (RadioInfo.getActiveWAFs() & RadioInfo.WAF_CDMA) == RadioInfo.WAF_CDMA; } /** * Entry point for application * * @param args * Command line arguments (not used) */ public static void main(final String[] args) { // Create a new instance of the application and make the currently // running thread the application's event dispatch thread. final SMSDemo smsDemo = new SMSDemo(); smsDemo.enterEventDispatcher(); } /** * This thread listens for any incoming messages */ private class ListeningThread extends Thread { private boolean _stop; /** * Stops this thread from listening for messages */ private synchronized void stop() { _stop = true; try { if (_conn != null) { _conn.close(); } } catch (final IOException ioe) { } } /** * Listens for incoming messages until stop() is called * * @see #stop() * @see java.lang.Runnable#run() */ public void run() { try { _conn = Connector.open("sms://:" + _port); for (;;) { if (_stop) { return; } final MessageConnection msgConn = (MessageConnection) _conn; final Message m = msgConn.receive(); receivedSmsMessage(m); } } catch (final IOException ioe) { updateStatus(ioe.toString()); } } } /** * A simple abstraction of an SMS message, used by the SendThread class */ private static final class SmsMessage { private final String _address; private final String _port; private final String _msg; /** * Creates a SMS message * * @param address * The address of the recipient of the SMS message * @param msg * The message to send */ public SmsMessage(final String address, final String msg, final String port) { _address = address; _port = port; _msg = msg; } /** * Returns a Message object representing this SMS message * * @param mc * The MessageConnection source with which to create the * Message from * @return The Message object representing the SMS message */ public Message toMessage(final MessageConnection mc) { // If the user chose to have messages routed to the inbox (port = // 0), // we need to specify an address without a port number. final String addressString = "//" + _address + (_port.equals(NON_ZERO_PORT_NUMBER) ? ":" + _port : ""); final TextMessage m = (TextMessage) mc.newMessage(MessageConnection.TEXT_MESSAGE, addressString); m.setPayloadText(_msg); return m; } /** * Returns a Datagram object representing this SMS message * * @param datagramConnectionBase * The DatagramConnectionBase object with which to create the * Datagram from * @return The Datagram object representing the SMS message */ public Datagram toDatagram( final DatagramConnectionBase datagramConnectionBase) throws IOException { DatagramBase datagram = null; final byte[] data = _msg.getBytes("ISO-8859-1"); datagram = (DatagramBase) datagramConnectionBase.newDatagram(); final SmsAddress smsAddress = new SmsAddress("//" + _address); final SMSPacketHeader smsPacketHeader = smsAddress.getHeader(); smsPacketHeader .setMessageCoding(SMSParameters.MESSAGE_CODING_ISO8859_1); datagram.setAddressBase(smsAddress); datagram.write(data, 0, data.length); return datagram; } } /** * A thread to manage outbound transactions */ private class SendThread extends Thread { private boolean _stopped = false; // Create a vector of SmsMessage objects with an initial capacity of 5. // For this implementation it is unlikely that more than 5 msgs will be // queued at any one time. private final Vector _msgs = new Vector(5); /** * Queues message send requests to send later * * @param address * The address to send the message to * @param msg * The message to send */ public void send(final String address, final String msg, final String port) { final SmsMessage message = new SmsMessage(address, msg, port); synchronized (this._msgs) { if (!this._stopped) { this._msgs.addElement(message); this._msgs.notifyAll(); } } } /** * Stops this thread from sending any more messages */ public void stop() { synchronized (this._msgs) { this._stopped = true; this._msgs.notifyAll(); this._msgs.removeAllElements(); try { if (_conn != null) { _conn.close(); } } catch (final IOException ioe) { } } } /** * Sends any queued messages until stop() is called * * @see #stop() * @see java.lang.Runnable#run() */ public void run() { while (true) { final SmsMessage smsMessage; synchronized (this._msgs) { if (this._stopped) { return; } else if (this._msgs.isEmpty()) { try { this._msgs.wait(); } catch (final InterruptedException ie) { return; } } if (this._stopped) { return; } else { smsMessage = (SmsMessage) this._msgs.elementAt(0); this._msgs.removeElementAt(0); } } try { if (isCDMA()) { final DatagramConnectionBase dcb = (DatagramConnectionBase) _conn; dcb.send(smsMessage.toDatagram(dcb)); } else { final MessageConnection mc = (MessageConnection) _conn; mc.send(smsMessage.toMessage(mc)); } } catch (final IOException ioe) { updateStatus(ioe.toString()); } } } } /** * This screen acts as the main screen to allow the user to send and receive * messages. */ private class SmsDemoScreen extends MainScreen { /** * Default constructor */ private SmsDemoScreen() { setTitle("SMS Demo"); _address = new EditField("Destination:", "", MAX_PHONE_NUMBER_LENGTH, BasicEditField.FILTER_PHONE); add(_address); _sendText = new EditField("Message:", ""); add(_sendText); add(new SeparatorField()); _status = new EditField(Field.NON_FOCUSABLE); add(_status); // Sends an SMS message final MenuItem sendMenuItem = new MenuItem(new StringProvider("Send"), 0x230010, 0); sendMenuItem.setCommand(new Command(new CommandHandler() { /** * @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata, * Object) */ public void execute(final ReadOnlyCommandMetadata metadata, final Object context) { final String text = _sendText.getText(); final String addr = _address.getText(); if (addr.length() == 0) { Dialog.alert("Destination field cannot be blank"); _address.setFocus(); } else if (text.length() == 0) { Dialog.alert("Message field cannot be blank"); _sendText.setFocus(); } else { _sender.send(addr, text, _port); } } })); addMenuItem(sendMenuItem); } /** * Prevent the save dialog from being displayed * * @see net.rim.device.api.ui.container.MainScreen#onSavePrompt() */ public boolean onSavePrompt() { return true; } /** * Closes the application * * @see net.rim.device.api.ui.Screen#close() */ public void close() { _listener.stop(); _sender.stop(); super.close(); } } /** * Creates a new SMSDemo object */ public SMSDemo() { invokeLater(new Runnable() { public void run() { if (!isCDMA()) { final int result = Dialog.ask(Dialog.D_YES_NO, "Send messages to inbox?", Dialog.YES); if (!(result == Dialog.YES)) { // If user chooses to not have message routed to inbox, // we need to specify an arbitrary non-zero port number. _port = NON_ZERO_PORT_NUMBER; } } _listener = new ListeningThread(); _listener.start(); _sender = new SendThread(); _sender.start(); } }); final SmsDemoScreen screen = new SmsDemoScreen(); pushScreen(screen); } /** * Update the GUI with the data just received * * @param msg * The new status message to display on screen */ private void updateStatus(final String msg) { System.err.println(msg); invokeLater(new Runnable() { /** * Updates the GUI's status message * * @see java.lang.Runnable#run() */ public void run() { // Clear the string buffer _statusMsgs.delete(0, _statusMsgs.length()); _statusMsgs.append(_status.getText()); _statusMsgs.append('\n'); _statusMsgs.append(msg); _status.setText(_statusMsgs.toString()); } }); } /** * Some simple formatting for a received SMS message * * @param m * The message just received */ private void receivedSmsMessage(final Message m) { final String address = m.getAddress(); String msg = null; if (m instanceof TextMessage) { final TextMessage tm = (TextMessage) m; msg = tm.getPayloadText(); } final StringBuffer sb = new StringBuffer(); sb.append("Received:"); sb.append('\n'); sb.append("Destination:"); sb.append(address); sb.append('\n'); sb.append("Data:"); sb.append(msg); sb.append('\n'); updateStatus(sb.toString()); } }