Mega Code Archive

 
Categories / Android / Hardware
 

Bluetooth Chat

/*  * Copyright (C) 2009 The Android Open Source Project  *  * 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.  */ package com.example.android.BluetoothChat; import java.util.Set; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; /**  * This Activity appears as a dialog. It lists any paired devices and  * devices detected in the area after discovery. When a device is chosen  * by the user, the MAC address of the device is sent back to the parent  * Activity in the result Intent.  */ public class DeviceListActivity extends Activity {     // Debugging     private static final String TAG = "DeviceListActivity";     private static final boolean D = true;     // Return Intent extra     public static String EXTRA_DEVICE_ADDRESS = "device_address";     // Member fields     private BluetoothAdapter mBtAdapter;     private ArrayAdapter<String> mPairedDevicesArrayAdapter;     private ArrayAdapter<String> mNewDevicesArrayAdapter;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         // Setup the window         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);         setContentView(R.layout.device_list);         // Set result CANCELED in case the user backs out         setResult(Activity.RESULT_CANCELED);         // Initialize the button to perform device discovery         Button scanButton = (Button) findViewById(R.id.button_scan);         scanButton.setOnClickListener(new OnClickListener() {             public void onClick(View v) {                 doDiscovery();                 v.setVisibility(View.GONE);             }         });         // Initialize array adapters. One for already paired devices and         // one for newly discovered devices         mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);         mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);         // Find and set up the ListView for paired devices         ListView pairedListView = (ListView) findViewById(R.id.paired_devices);         pairedListView.setAdapter(mPairedDevicesArrayAdapter);         pairedListView.setOnItemClickListener(mDeviceClickListener);         // Find and set up the ListView for newly discovered devices         ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);         newDevicesListView.setAdapter(mNewDevicesArrayAdapter);         newDevicesListView.setOnItemClickListener(mDeviceClickListener);         // Register for broadcasts when a device is discovered         IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);         this.registerReceiver(mReceiver, filter);         // Register for broadcasts when discovery has finished         filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);         this.registerReceiver(mReceiver, filter);         // Get the local Bluetooth adapter         mBtAdapter = BluetoothAdapter.getDefaultAdapter();         // Get a set of currently paired devices         Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();         // If there are paired devices, add each one to the ArrayAdapter         if (pairedDevices.size() > 0) {             findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);             for (BluetoothDevice device : pairedDevices) {                 mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());             }         } else {             String noDevices = getResources().getText(R.string.none_paired).toString();             mPairedDevicesArrayAdapter.add(noDevices);         }     }     @Override     protected void onDestroy() {         super.onDestroy();         // Make sure we're not doing discovery anymore         if (mBtAdapter != null) {             mBtAdapter.cancelDiscovery();         }         // Unregister broadcast listeners         this.unregisterReceiver(mReceiver);     }     /**      * Start device discover with the BluetoothAdapter      */     private void doDiscovery() {         if (D) Log.d(TAG, "doDiscovery()");         // Indicate scanning in the title         setProgressBarIndeterminateVisibility(true);         setTitle(R.string.scanning);         // Turn on sub-title for new devices         findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);         // If we're already discovering, stop it         if (mBtAdapter.isDiscovering()) {             mBtAdapter.cancelDiscovery();         }         // Request discover from BluetoothAdapter         mBtAdapter.startDiscovery();     }     // The on-click listener for all devices in the ListViews     private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {         public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {             // Cancel discovery because it's costly and we're about to connect             mBtAdapter.cancelDiscovery();             // Get the device MAC address, which is the last 17 chars in the View             String info = ((TextView) v).getText().toString();             String address = info.substring(info.length() - 17);             // Create the result Intent and include the MAC address             Intent intent = new Intent();             intent.putExtra(EXTRA_DEVICE_ADDRESS, address);             // Set result and finish this Activity             setResult(Activity.RESULT_OK, intent);             finish();         }     };     // The BroadcastReceiver that listens for discovered devices and     // changes the title when discovery is finished     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {         @Override         public void onReceive(Context context, Intent intent) {             String action = intent.getAction();             // When discovery finds a device             if (BluetoothDevice.ACTION_FOUND.equals(action)) {                 // Get the BluetoothDevice object from the Intent                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                 // If it's already paired, skip it, because it's been listed already                 if (device.getBondState() != BluetoothDevice.BOND_BONDED) {                     mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());                 }             // When discovery is finished, change the Activity title             } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {                 setProgressBarIndeterminateVisibility(false);                 setTitle(R.string.select_device);                 if (mNewDevicesArrayAdapter.getCount() == 0) {                     String noDevices = getResources().getText(R.string.none_found).toString();                     mNewDevicesArrayAdapter.add(noDevices);                 }             }         }     }; } /*  * Copyright (C) 2009 The Android Open Source Project  *  * 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.  */ package com.example.android.BluetoothChat; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; /**  * This class does all the work for setting up and managing Bluetooth  * connections with other devices. It has a thread that listens for  * incoming connections, a thread for connecting with a device, and a  * thread for performing data transmissions when connected.  */ public class BluetoothChatService {     // Debugging     private static final String TAG = "BluetoothChatService";     private static final boolean D = true;     // Name for the SDP record when creating server socket     private static final String NAME_SECURE = "BluetoothChatSecure";     private static final String NAME_INSECURE = "BluetoothChatInsecure";     // Unique UUID for this application     private static final UUID MY_UUID_SECURE =         UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");     private static final UUID MY_UUID_INSECURE =         UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");     // Member fields     private final BluetoothAdapter mAdapter;     private final Handler mHandler;     private AcceptThread mSecureAcceptThread;     private AcceptThread mInsecureAcceptThread;     private ConnectThread mConnectThread;     private ConnectedThread mConnectedThread;     private int mState;     // Constants that indicate the current connection state     public static final int STATE_NONE = 0;       // we're doing nothing     public static final int STATE_LISTEN = 1;     // now listening for incoming connections     public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection     public static final int STATE_CONNECTED = 3;  // now connected to a remote device     /**      * Constructor. Prepares a new BluetoothChat session.      * @param context  The UI Activity Context      * @param handler  A Handler to send messages back to the UI Activity      */     public BluetoothChatService(Context context, Handler handler) {         mAdapter = BluetoothAdapter.getDefaultAdapter();         mState = STATE_NONE;         mHandler = handler;     }     /**      * Set the current state of the chat connection      * @param state  An integer defining the current connection state      */     private synchronized void setState(int state) {         if (D) Log.d(TAG, "setState() " + mState + " -> " + state);         mState = state;         // Give the new state to the Handler so the UI Activity can update         mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();     }     /**      * Return the current connection state. */     public synchronized int getState() {         return mState;     }     /**      * Start the chat service. Specifically start AcceptThread to begin a      * session in listening (server) mode. Called by the Activity onResume() */     public synchronized void start() {         if (D) Log.d(TAG, "start");         // Cancel any thread attempting to make a connection         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}         // Cancel any thread currently running a connection         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}         setState(STATE_LISTEN);         // Start the thread to listen on a BluetoothServerSocket         if (mSecureAcceptThread == null) {             mSecureAcceptThread = new AcceptThread(true);             mSecureAcceptThread.start();         }         if (mInsecureAcceptThread == null) {             mInsecureAcceptThread = new AcceptThread(false);             mInsecureAcceptThread.start();         }     }     /**      * Start the ConnectThread to initiate a connection to a remote device.      * @param device  The BluetoothDevice to connect      * @param secure Socket Security type - Secure (true) , Insecure (false)      */     public synchronized void connect(BluetoothDevice device, boolean secure) {         if (D) Log.d(TAG, "connect to: " + device);         // Cancel any thread attempting to make a connection         if (mState == STATE_CONNECTING) {             if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}         }         // Cancel any thread currently running a connection         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}         // Start the thread to connect with the given device         mConnectThread = new ConnectThread(device, secure);         mConnectThread.start();         setState(STATE_CONNECTING);     }     /**      * Start the ConnectedThread to begin managing a Bluetooth connection      * @param socket  The BluetoothSocket on which the connection was made      * @param device  The BluetoothDevice that has been connected      */     public synchronized void connected(BluetoothSocket socket, BluetoothDevice             device, final String socketType) {         if (D) Log.d(TAG, "connected, Socket Type:" + socketType);         // Cancel the thread that completed the connection         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}         // Cancel any thread currently running a connection         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}         // Cancel the accept thread because we only want to connect to one device         if (mSecureAcceptThread != null) {             mSecureAcceptThread.cancel();             mSecureAcceptThread = null;         }         if (mInsecureAcceptThread != null) {             mInsecureAcceptThread.cancel();             mInsecureAcceptThread = null;         }         // Start the thread to manage the connection and perform transmissions         mConnectedThread = new ConnectedThread(socket, socketType);         mConnectedThread.start();         // Send the name of the connected device back to the UI Activity         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);         Bundle bundle = new Bundle();         bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());         msg.setData(bundle);         mHandler.sendMessage(msg);         setState(STATE_CONNECTED);     }     /**      * Stop all threads      */     public synchronized void stop() {         if (D) Log.d(TAG, "stop");         if (mConnectThread != null) {             mConnectThread.cancel();             mConnectThread = null;         }         if (mConnectedThread != null) {             mConnectedThread.cancel();             mConnectedThread = null;         }         if (mSecureAcceptThread != null) {             mSecureAcceptThread.cancel();             mSecureAcceptThread = null;         }         if (mInsecureAcceptThread != null) {             mInsecureAcceptThread.cancel();             mInsecureAcceptThread = null;         }         setState(STATE_NONE);     }     /**      * Write to the ConnectedThread in an unsynchronized manner      * @param out The bytes to write      * @see ConnectedThread#write(byte[])      */     public void write(byte[] out) {         // Create temporary object         ConnectedThread r;         // Synchronize a copy of the ConnectedThread         synchronized (this) {             if (mState != STATE_CONNECTED) return;             r = mConnectedThread;         }         // Perform the write unsynchronized         r.write(out);     }     /**      * Indicate that the connection attempt failed and notify the UI Activity.      */     private void connectionFailed() {         // Send a failure message back to the Activity         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);         Bundle bundle = new Bundle();         bundle.putString(BluetoothChat.TOAST, "Unable to connect device");         msg.setData(bundle);         mHandler.sendMessage(msg);         // Start the service over to restart listening mode         BluetoothChatService.this.start();     }     /**      * Indicate that the connection was lost and notify the UI Activity.      */     private void connectionLost() {         // Send a failure message back to the Activity         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);         Bundle bundle = new Bundle();         bundle.putString(BluetoothChat.TOAST, "Device connection was lost");         msg.setData(bundle);         mHandler.sendMessage(msg);         // Start the service over to restart listening mode         BluetoothChatService.this.start();     }     /**      * This thread runs while listening for incoming connections. It behaves      * like a server-side client. It runs until a connection is accepted      * (or until cancelled).      */     private class AcceptThread extends Thread {         // The local server socket         private final BluetoothServerSocket mmServerSocket;         private String mSocketType;         public AcceptThread(boolean secure) {             BluetoothServerSocket tmp = null;             mSocketType = secure ? "Secure":"Insecure";             // Create a new listening server socket             try {                 if (secure) {                     tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,                         MY_UUID_SECURE);                 } else {                     tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(                             NAME_INSECURE, MY_UUID_INSECURE);                 }             } catch (IOException e) {                 Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);             }             mmServerSocket = tmp;         }         public void run() {             if (D) Log.d(TAG, "Socket Type: " + mSocketType +                     "BEGIN mAcceptThread" + this);             setName("AcceptThread" + mSocketType);             BluetoothSocket socket = null;             // Listen to the server socket if we're not connected             while (mState != STATE_CONNECTED) {                 try {                     // This is a blocking call and will only return on a                     // successful connection or an exception                     socket = mmServerSocket.accept();                 } catch (IOException e) {                     Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);                     break;                 }                 // If a connection was accepted                 if (socket != null) {                     synchronized (BluetoothChatService.this) {                         switch (mState) {                         case STATE_LISTEN:                         case STATE_CONNECTING:                             // Situation normal. Start the connected thread.                             connected(socket, socket.getRemoteDevice(),                                     mSocketType);                             break;                         case STATE_NONE:                         case STATE_CONNECTED:                             // Either not ready or already connected. Terminate new socket.                             try {                                 socket.close();                             } catch (IOException e) {                                 Log.e(TAG, "Could not close unwanted socket", e);                             }                             break;                         }                     }                 }             }             if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);         }         public void cancel() {             if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);             try {                 mmServerSocket.close();             } catch (IOException e) {                 Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);             }         }     }     /**      * This thread runs while attempting to make an outgoing connection      * with a device. It runs straight through; the connection either      * succeeds or fails.      */     private class ConnectThread extends Thread {         private final BluetoothSocket mmSocket;         private final BluetoothDevice mmDevice;         private String mSocketType;         public ConnectThread(BluetoothDevice device, boolean secure) {             mmDevice = device;             BluetoothSocket tmp = null;             mSocketType = secure ? "Secure" : "Insecure";             // Get a BluetoothSocket for a connection with the             // given BluetoothDevice             try {                 if (secure) {                     tmp = device.createRfcommSocketToServiceRecord(                             MY_UUID_SECURE);                 } else {                     tmp = device.createInsecureRfcommSocketToServiceRecord(                             MY_UUID_INSECURE);                 }             } catch (IOException e) {                 Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);             }             mmSocket = tmp;         }         public void run() {             Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);             setName("ConnectThread" + mSocketType);             // Always cancel discovery because it will slow down a connection             mAdapter.cancelDiscovery();             // Make a connection to the BluetoothSocket             try {                 // This is a blocking call and will only return on a                 // successful connection or an exception                 mmSocket.connect();             } catch (IOException e) {                 // Close the socket                 try {                     mmSocket.close();                 } catch (IOException e2) {                     Log.e(TAG, "unable to close() " + mSocketType +                             " socket during connection failure", e2);                 }                 connectionFailed();                 return;             }             // Reset the ConnectThread because we're done             synchronized (BluetoothChatService.this) {                 mConnectThread = null;             }             // Start the connected thread             connected(mmSocket, mmDevice, mSocketType);         }         public void cancel() {             try {                 mmSocket.close();             } catch (IOException e) {                 Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);             }         }     }     /**      * This thread runs during a connection with a remote device.      * It handles all incoming and outgoing transmissions.      */     private class ConnectedThread extends Thread {         private final BluetoothSocket mmSocket;         private final InputStream mmInStream;         private final OutputStream mmOutStream;         public ConnectedThread(BluetoothSocket socket, String socketType) {             Log.d(TAG, "create ConnectedThread: " + socketType);             mmSocket = socket;             InputStream tmpIn = null;             OutputStream tmpOut = null;             // Get the BluetoothSocket input and output streams             try {                 tmpIn = socket.getInputStream();                 tmpOut = socket.getOutputStream();             } catch (IOException e) {                 Log.e(TAG, "temp sockets not created", e);             }             mmInStream = tmpIn;             mmOutStream = tmpOut;         }         public void run() {             Log.i(TAG, "BEGIN mConnectedThread");             byte[] buffer = new byte[1024];             int bytes;             // Keep listening to the InputStream while connected             while (true) {                 try {                     // Read from the InputStream                     bytes = mmInStream.read(buffer);                     // Send the obtained bytes to the UI Activity                     mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)                             .sendToTarget();                 } catch (IOException e) {                     Log.e(TAG, "disconnected", e);                     connectionLost();                     // Start the service over to restart listening mode                     BluetoothChatService.this.start();                     break;                 }             }         }         /**          * Write to the connected OutStream.          * @param buffer  The bytes to write          */         public void write(byte[] buffer) {             try {                 mmOutStream.write(buffer);                 // Share the sent message back to the UI Activity                 mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)                         .sendToTarget();             } catch (IOException e) {                 Log.e(TAG, "Exception during write", e);             }         }         public void cancel() {             try {                 mmSocket.close();             } catch (IOException e) {                 Log.e(TAG, "close() of connect socket failed", e);             }         }     } } /*  * Copyright (C) 2009 The Android Open Source Project  *  * 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.  */ package com.example.android.BluetoothChat; import android.app.ActionBar; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; /**  * This is the main Activity that displays the current chat session.  */ public class BluetoothChat extends Activity {     // Debugging     private static final String TAG = "BluetoothChat";     private static final boolean D = true;     // Message types sent from the BluetoothChatService Handler     public static final int MESSAGE_STATE_CHANGE = 1;     public static final int MESSAGE_READ = 2;     public static final int MESSAGE_WRITE = 3;     public static final int MESSAGE_DEVICE_NAME = 4;     public static final int MESSAGE_TOAST = 5;     // Key names received from the BluetoothChatService Handler     public static final String DEVICE_NAME = "device_name";     public static final String TOAST = "toast";     // Intent request codes     private static final int REQUEST_CONNECT_DEVICE_SECURE = 1;     private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;     private static final int REQUEST_ENABLE_BT = 3;     // Layout Views     private ListView mConversationView;     private EditText mOutEditText;     private Button mSendButton;     // Name of the connected device     private String mConnectedDeviceName = null;     // Array adapter for the conversation thread     private ArrayAdapter<String> mConversationArrayAdapter;     // String buffer for outgoing messages     private StringBuffer mOutStringBuffer;     // Local Bluetooth adapter     private BluetoothAdapter mBluetoothAdapter = null;     // Member object for the chat services     private BluetoothChatService mChatService = null;     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         if(D) Log.e(TAG, "+++ ON CREATE +++");         // Set up the window layout         setContentView(R.layout.main);         // Get local Bluetooth adapter         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();         // If the adapter is null, then Bluetooth is not supported         if (mBluetoothAdapter == null) {             Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();             finish();             return;         }     }     @Override     public void onStart() {         super.onStart();         if(D) Log.e(TAG, "++ ON START ++");         // If BT is not on, request that it be enabled.         // setupChat() will then be called during onActivityResult         if (!mBluetoothAdapter.isEnabled()) {             Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);             startActivityForResult(enableIntent, REQUEST_ENABLE_BT);         // Otherwise, setup the chat session         } else {             if (mChatService == null) setupChat();         }     }     @Override     public synchronized void onResume() {         super.onResume();         if(D) Log.e(TAG, "+ ON RESUME +");         // Performing this check in onResume() covers the case in which BT was         // not enabled during onStart(), so we were paused to enable it...         // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.         if (mChatService != null) {             // Only if the state is STATE_NONE, do we know that we haven't started already             if (mChatService.getState() == BluetoothChatService.STATE_NONE) {               // Start the Bluetooth chat services               mChatService.start();             }         }     }     private void setupChat() {         Log.d(TAG, "setupChat()");         // Initialize the array adapter for the conversation thread         mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);         mConversationView = (ListView) findViewById(R.id.in);         mConversationView.setAdapter(mConversationArrayAdapter);         // Initialize the compose field with a listener for the return key         mOutEditText = (EditText) findViewById(R.id.edit_text_out);         mOutEditText.setOnEditorActionListener(mWriteListener);         // Initialize the send button with a listener that for click events         mSendButton = (Button) findViewById(R.id.button_send);         mSendButton.setOnClickListener(new OnClickListener() {             public void onClick(View v) {                 // Send a message using content of the edit text widget                 TextView view = (TextView) findViewById(R.id.edit_text_out);                 String message = view.getText().toString();                 sendMessage(message);             }         });         // Initialize the BluetoothChatService to perform bluetooth connections         mChatService = new BluetoothChatService(this, mHandler);         // Initialize the buffer for outgoing messages         mOutStringBuffer = new StringBuffer("");     }     @Override     public synchronized void onPause() {         super.onPause();         if(D) Log.e(TAG, "- ON PAUSE -");     }     @Override     public void onStop() {         super.onStop();         if(D) Log.e(TAG, "-- ON STOP --");     }     @Override     public void onDestroy() {         super.onDestroy();         // Stop the Bluetooth chat services         if (mChatService != null) mChatService.stop();         if(D) Log.e(TAG, "--- ON DESTROY ---");     }     private void ensureDiscoverable() {         if(D) Log.d(TAG, "ensure discoverable");         if (mBluetoothAdapter.getScanMode() !=             BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {             Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);             discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);             startActivity(discoverableIntent);         }     }     /**      * Sends a message.      * @param message  A string of text to send.      */     private void sendMessage(String message) {         // Check that we're actually connected before trying anything         if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {             Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();             return;         }         // Check that there's actually something to send         if (message.length() > 0) {             // Get the message bytes and tell the BluetoothChatService to write             byte[] send = message.getBytes();             mChatService.write(send);             // Reset out string buffer to zero and clear the edit text field             mOutStringBuffer.setLength(0);             mOutEditText.setText(mOutStringBuffer);         }     }     // The action listener for the EditText widget, to listen for the return key     private TextView.OnEditorActionListener mWriteListener =         new TextView.OnEditorActionListener() {         public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {             // If the action is a key-up event on the return key, send the message             if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {                 String message = view.getText().toString();                 sendMessage(message);             }             if(D) Log.i(TAG, "END onEditorAction");             return true;         }     };     private final void setStatus(int resId) {         final ActionBar actionBar = getActionBar();         actionBar.setSubtitle(resId);     }     private final void setStatus(CharSequence subTitle) {         final ActionBar actionBar = getActionBar();         actionBar.setSubtitle(subTitle);     }     // The Handler that gets information back from the BluetoothChatService     private final Handler mHandler = new Handler() {         @Override         public void handleMessage(Message msg) {             switch (msg.what) {             case MESSAGE_STATE_CHANGE:                 if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);                 switch (msg.arg1) {                 case BluetoothChatService.STATE_CONNECTED:                     setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));                     mConversationArrayAdapter.clear();                     break;                 case BluetoothChatService.STATE_CONNECTING:                     setStatus(R.string.title_connecting);                     break;                 case BluetoothChatService.STATE_LISTEN:                 case BluetoothChatService.STATE_NONE:                     setStatus(R.string.title_not_connected);                     break;                 }                 break;             case MESSAGE_WRITE:                 byte[] writeBuf = (byte[]) msg.obj;                 // construct a string from the buffer                 String writeMessage = new String(writeBuf);                 mConversationArrayAdapter.add("Me:  " + writeMessage);                 break;             case MESSAGE_READ:                 byte[] readBuf = (byte[]) msg.obj;                 // construct a string from the valid bytes in the buffer                 String readMessage = new String(readBuf, 0, msg.arg1);                 mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);                 break;             case MESSAGE_DEVICE_NAME:                 // save the connected device's name                 mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);                 Toast.makeText(getApplicationContext(), "Connected to "                                + mConnectedDeviceName, Toast.LENGTH_SHORT).show();                 break;             case MESSAGE_TOAST:                 Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),                                Toast.LENGTH_SHORT).show();                 break;             }         }     };     public void onActivityResult(int requestCode, int resultCode, Intent data) {         if(D) Log.d(TAG, "onActivityResult " + resultCode);         switch (requestCode) {         case REQUEST_CONNECT_DEVICE_SECURE:             // When DeviceListActivity returns with a device to connect             if (resultCode == Activity.RESULT_OK) {                 connectDevice(data, true);             }             break;         case REQUEST_CONNECT_DEVICE_INSECURE:             // When DeviceListActivity returns with a device to connect             if (resultCode == Activity.RESULT_OK) {                 connectDevice(data, false);             }             break;         case REQUEST_ENABLE_BT:             // When the request to enable Bluetooth returns             if (resultCode == Activity.RESULT_OK) {                 // Bluetooth is now enabled, so set up a chat session                 setupChat();             } else {                 // User did not enable Bluetooth or an error occurred                 Log.d(TAG, "BT not enabled");                 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();                 finish();             }         }     }     private void connectDevice(Intent data, boolean secure) {         // Get the device MAC address         String address = data.getExtras()             .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);         // Get the BluetoothDevice object         BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);         // Attempt to connect to the device         mChatService.connect(device, secure);     }     @Override     public boolean onCreateOptionsMenu(Menu menu) {         MenuInflater inflater = getMenuInflater();         inflater.inflate(R.menu.option_menu, menu);         return true;     }     @Override     public boolean onOptionsItemSelected(MenuItem item) {         Intent serverIntent = null;         switch (item.getItemId()) {         case R.id.secure_connect_scan:             // Launch the DeviceListActivity to see devices and do scan             serverIntent = new Intent(this, DeviceListActivity.class);             startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);             return true;         case R.id.insecure_connect_scan:             // Launch the DeviceListActivity to see devices and do scan             serverIntent = new Intent(this, DeviceListActivity.class);             startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);             return true;         case R.id.discoverable:             // Ensure this device is discoverable by others             ensureDiscoverable();             return true;         }         return false;     } } //res\layout\custom_title.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:gravity="center_vertical"   >   <TextView android:id="@+id/title_left_text"       android:layout_alignParentLeft="true"       android:ellipsize="end"       android:singleLine="true"       style="?android:attr/windowTitleStyle"       android:layout_width="wrap_content"       android:layout_height="match_parent"       android:layout_weight="1"     />     <TextView android:id="@+id/title_right_text"         android:layout_alignParentRight="true"         android:ellipsize="end"         android:singleLine="true"         android:layout_width="wrap_content"         android:layout_height="match_parent"         android:textColor="#fff"         android:layout_weight="1"      /> </RelativeLayout> //res\layout\device_list.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical"     android:layout_width="match_parent"     android:layout_height="match_parent"     >     <TextView android:id="@+id/title_paired_devices"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:text="@string/title_paired_devices"         android:visibility="gone"         android:background="#666"         android:textColor="#fff"         android:paddingLeft="5dp"     />     <ListView android:id="@+id/paired_devices"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:stackFromBottom="true"         android:layout_weight="1"     />     <TextView android:id="@+id/title_new_devices"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:text="@string/title_other_devices"         android:visibility="gone"         android:background="#666"         android:textColor="#fff"         android:paddingLeft="5dp"     />     <ListView android:id="@+id/new_devices"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:stackFromBottom="true"         android:layout_weight="2"     />     <Button android:id="@+id/button_scan"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:text="@string/button_scan"     /> </LinearLayout> //res\layout\device_name.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:textSize="18sp"     android:padding="5dp" /> //res\layout\main.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical"     android:layout_width="match_parent"     android:layout_height="match_parent"     >     <ListView android:id="@+id/in"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:stackFromBottom="true"         android:transcriptMode="alwaysScroll"         android:layout_weight="1"     />     <LinearLayout         android:orientation="horizontal"         android:layout_width="match_parent"         android:layout_height="wrap_content"         >         <EditText android:id="@+id/edit_text_out"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:layout_weight="1"             android:layout_gravity="bottom"         />         <Button android:id="@+id/button_send"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="@string/send"         />     </LinearLayout> </LinearLayout> //res\layout\message.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:textSize="18sp"     android:padding="5dp" /> //res\menu\option_menu.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <menu xmlns:android="http://schemas.android.com/apk/res/android">     <item android:id="@+id/secure_connect_scan"           android:icon="@android:drawable/ic_menu_search"           android:title="@string/secure_connect"           android:showAsAction="ifRoom|withText" />     <item android:id="@+id/insecure_connect_scan"           android:icon="@android:drawable/ic_menu_search"           android:title="@string/insecure_connect"           android:showAsAction="ifRoom|withText" />     <item android:id="@+id/discoverable"           android:icon="@android:drawable/ic_menu_mylocation"           android:title="@string/discoverable"           android:showAsAction="ifRoom|withText" /> </menu> //res\values\strings.xml <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project      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. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">     <string name="app_name">Bluetooth Chat</string>     <!--  BluetoothChat -->     <string name="send">Send</string>     <string name="not_connected">You are not connected to a device</string>     <string name="bt_not_enabled_leaving">Bluetooth was not enabled. Leaving Bluetooth Chat.</string>     <string name="title_connecting">connecting...</string>     <string name="title_connected_to">connected to <xliff:g id="device_name">%1$s</xliff:g></string>     <string name="title_not_connected">not connected</string>     <!--  DeviceListActivity -->     <string name="scanning">scanning for devices...</string>     <string name="select_device">select a device to connect</string>     <string name="none_paired">No devices have been paired</string>     <string name="none_found">No devices found</string>     <string name="title_paired_devices">Paired Devices</string>     <string name="title_other_devices">Other Available Devices</string>     <string name="button_scan">Scan for devices</string>     <!-- Options Menu -->     <string name="secure_connect">Connect a device - Secure</string>     <string name="insecure_connect">Connect a device - Insecure</string>     <string name="discoverable">Make discoverable</string> </resources>