-
-
Save cwdesautels/3410454 to your computer and use it in GitHub Desktop.
AndroidBluetoothChat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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.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 TextView mTitle; | |
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 | |
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); | |
setContentView(R.layout.main); | |
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); | |
// Set up the custom title | |
mTitle = (TextView) findViewById(R.id.title_left_text); | |
mTitle.setText(R.string.app_name); | |
mTitle = (TextView) findViewById(R.id.title_right_text); | |
// 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; | |
} | |
}; | |
// 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: | |
mTitle.setText(R.string.title_connected_to); | |
mTitle.append(mConnectedDeviceName); | |
mConversationArrayAdapter.clear(); | |
break; | |
case BluetoothChatService.STATE_CONNECTING: | |
mTitle.setText(R.string.title_connecting); | |
break; | |
case BluetoothChatService.STATE_LISTEN: | |
case BluetoothChatService.STATE_NONE: | |
mTitle.setText(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 occured | |
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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(); | |
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 incase 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); | |
} | |
} | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment