| /* |
| * Copyright (C) 2011 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.bluetooth.health; |
| |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.app.Dialog; |
| import android.app.DialogFragment; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.BluetoothDevice; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.ServiceConnection; |
| import android.content.res.Resources; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.RemoteException; |
| import android.util.Log; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.ImageView; |
| import android.widget.TextView; |
| import android.widget.Toast; |
| |
| /** |
| * Main user interface for the Sample application. All Bluetooth health-related |
| * operations happen in {@link BluetoothHDPService}. This activity passes messages to and from |
| * the service. |
| */ |
| public class BluetoothHDPActivity extends Activity { |
| private static final String TAG = "BluetoothHealthActivity"; |
| |
| // Use the appropriate IEEE 11073 data types based on the devices used. |
| // Below are some examples. Refer to relevant Bluetooth HDP specifications for detail. |
| // 0x1007 - blood pressure meter |
| // 0x1008 - body thermometer |
| // 0x100F - body weight scale |
| private static final int HEALTH_PROFILE_SOURCE_DATA_TYPE = 0x1007; |
| |
| private static final int REQUEST_ENABLE_BT = 1; |
| |
| private TextView mConnectIndicator; |
| private ImageView mDataIndicator; |
| private TextView mStatusMessage; |
| |
| private BluetoothAdapter mBluetoothAdapter; |
| private BluetoothDevice[] mAllBondedDevices; |
| private BluetoothDevice mDevice; |
| private int mDeviceIndex = 0; |
| private Resources mRes; |
| private Messenger mHealthService; |
| private boolean mHealthServiceBound; |
| |
| // Handles events sent by {@link HealthHDPService}. |
| private Handler mIncomingHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| // Application registration complete. |
| case BluetoothHDPService.STATUS_HEALTH_APP_REG: |
| mStatusMessage.setText( |
| String.format(mRes.getString(R.string.status_reg), |
| msg.arg1)); |
| break; |
| // Application unregistration complete. |
| case BluetoothHDPService.STATUS_HEALTH_APP_UNREG: |
| mStatusMessage.setText( |
| String.format(mRes.getString(R.string.status_unreg), |
| msg.arg1)); |
| break; |
| // Reading data from HDP device. |
| case BluetoothHDPService.STATUS_READ_DATA: |
| mStatusMessage.setText(mRes.getString(R.string.read_data)); |
| mDataIndicator.setImageLevel(1); |
| break; |
| // Finish reading data from HDP device. |
| case BluetoothHDPService.STATUS_READ_DATA_DONE: |
| mStatusMessage.setText(mRes.getString(R.string.read_data_done)); |
| mDataIndicator.setImageLevel(0); |
| break; |
| // Channel creation complete. Some devices will automatically establish |
| // connection. |
| case BluetoothHDPService.STATUS_CREATE_CHANNEL: |
| mStatusMessage.setText( |
| String.format(mRes.getString(R.string.status_create_channel), |
| msg.arg1)); |
| mConnectIndicator.setText(R.string.connected); |
| break; |
| // Channel destroy complete. This happens when either the device disconnects or |
| // there is extended inactivity. |
| case BluetoothHDPService.STATUS_DESTROY_CHANNEL: |
| mStatusMessage.setText( |
| String.format(mRes.getString(R.string.status_destroy_channel), |
| msg.arg1)); |
| mConnectIndicator.setText(R.string.disconnected); |
| break; |
| default: |
| super.handleMessage(msg); |
| } |
| } |
| }; |
| |
| private final Messenger mMessenger = new Messenger(mIncomingHandler); |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Check for Bluetooth availability on the Android platform. |
| mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); |
| if (mBluetoothAdapter == null) { |
| Toast.makeText(this, R.string.bluetooth_not_available, Toast.LENGTH_LONG); |
| finish(); |
| return; |
| } |
| setContentView(R.layout.console); |
| mConnectIndicator = (TextView) findViewById(R.id.connect_ind); |
| mStatusMessage = (TextView) findViewById(R.id.status_msg); |
| mDataIndicator = (ImageView) findViewById(R.id.data_ind); |
| mRes = getResources(); |
| mHealthServiceBound = false; |
| |
| // Initiates application registration through {@link BluetoothHDPService}. |
| Button registerAppButton = (Button) findViewById(R.id.button_register_app); |
| registerAppButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| sendMessage(BluetoothHDPService.MSG_REG_HEALTH_APP, |
| HEALTH_PROFILE_SOURCE_DATA_TYPE); |
| } |
| }); |
| |
| // Initiates application unregistration through {@link BluetoothHDPService}. |
| Button unregisterAppButton = (Button) findViewById(R.id.button_unregister_app); |
| unregisterAppButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| sendMessage(BluetoothHDPService.MSG_UNREG_HEALTH_APP, 0); |
| } |
| }); |
| |
| // Initiates channel creation through {@link BluetoothHDPService}. Some devices will |
| // initiate the channel connection, in which case, it is not necessary to do this in the |
| // application. When pressed, the user is asked to select from one of the bonded devices |
| // to connect to. |
| Button connectButton = (Button) findViewById(R.id.button_connect_channel); |
| connectButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| mAllBondedDevices = |
| (BluetoothDevice[]) mBluetoothAdapter.getBondedDevices().toArray( |
| new BluetoothDevice[0]); |
| |
| if (mAllBondedDevices.length > 0) { |
| int deviceCount = mAllBondedDevices.length; |
| if (mDeviceIndex < deviceCount) mDevice = mAllBondedDevices[mDeviceIndex]; |
| else { |
| mDeviceIndex = 0; |
| mDevice = mAllBondedDevices[0]; |
| } |
| String[] deviceNames = new String[deviceCount]; |
| int i = 0; |
| for (BluetoothDevice device : mAllBondedDevices) { |
| deviceNames[i++] = device.getName(); |
| } |
| SelectDeviceDialogFragment deviceDialog = |
| SelectDeviceDialogFragment.newInstance(deviceNames, mDeviceIndex); |
| deviceDialog.show(getFragmentManager(), "deviceDialog"); |
| } |
| } |
| }); |
| |
| // Initiates channel disconnect through {@link BluetoothHDPService}. |
| Button disconnectButton = (Button) findViewById(R.id.button_disconnect_channel); |
| disconnectButton.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { |
| disconnectChannel(); |
| } |
| }); |
| registerReceiver(mReceiver, initIntentFilter()); |
| } |
| |
| // Sets up communication with {@link BluetoothHDPService}. |
| private ServiceConnection mConnection = new ServiceConnection() { |
| public void onServiceConnected(ComponentName name, IBinder service) { |
| mHealthServiceBound = true; |
| Message msg = Message.obtain(null, BluetoothHDPService.MSG_REG_CLIENT); |
| msg.replyTo = mMessenger; |
| mHealthService = new Messenger(service); |
| try { |
| mHealthService.send(msg); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Unable to register client to service."); |
| e.printStackTrace(); |
| } |
| } |
| |
| public void onServiceDisconnected(ComponentName name) { |
| mHealthService = null; |
| mHealthServiceBound = false; |
| } |
| }; |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| if (mHealthServiceBound) unbindService(mConnection); |
| unregisterReceiver(mReceiver); |
| } |
| |
| @Override |
| protected void onStart() { |
| super.onStart(); |
| // If Bluetooth is not on, request that it be enabled. |
| if (!mBluetoothAdapter.isEnabled()) { |
| Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); |
| startActivityForResult(enableIntent, REQUEST_ENABLE_BT); |
| } else { |
| initialize(); |
| } |
| } |
| |
| /** |
| * Ensures user has turned on Bluetooth on the Android device. |
| */ |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| switch (requestCode) { |
| case REQUEST_ENABLE_BT: |
| if (resultCode == Activity.RESULT_OK) { |
| initialize(); |
| } else { |
| finish(); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Used by {@link SelectDeviceDialogFragment} to record the bonded Bluetooth device selected |
| * by the user. |
| * |
| * @param position Position of the bonded Bluetooth device in the array. |
| */ |
| public void setDevice(int position) { |
| mDevice = this.mAllBondedDevices[position]; |
| mDeviceIndex = position; |
| } |
| |
| private void connectChannel() { |
| sendMessageWithDevice(BluetoothHDPService.MSG_CONNECT_CHANNEL); |
| } |
| |
| private void disconnectChannel() { |
| sendMessageWithDevice(BluetoothHDPService.MSG_DISCONNECT_CHANNEL); |
| } |
| |
| private void initialize() { |
| // Starts health service. |
| Intent intent = new Intent(this, BluetoothHDPService.class); |
| startService(intent); |
| bindService(intent, mConnection, Context.BIND_AUTO_CREATE); |
| } |
| |
| // Intent filter and broadcast receive to handle Bluetooth on event. |
| private IntentFilter initIntentFilter() { |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); |
| return filter; |
| } |
| |
| private final BroadcastReceiver mReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final String action = intent.getAction(); |
| if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { |
| if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) == |
| BluetoothAdapter.STATE_ON) { |
| initialize(); |
| } |
| } |
| } |
| }; |
| |
| // Sends a message to {@link BluetoothHDPService}. |
| private void sendMessage(int what, int value) { |
| if (mHealthService == null) { |
| Log.d(TAG, "Health Service not connected."); |
| return; |
| } |
| |
| try { |
| mHealthService.send(Message.obtain(null, what, value, 0)); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Unable to reach service."); |
| e.printStackTrace(); |
| } |
| } |
| |
| // Sends an update message, along with an HDP BluetoothDevice object, to |
| // {@link BluetoothHDPService}. The BluetoothDevice object is needed by the channel creation |
| // method. |
| private void sendMessageWithDevice(int what) { |
| if (mHealthService == null) { |
| Log.d(TAG, "Health Service not connected."); |
| return; |
| } |
| |
| try { |
| mHealthService.send(Message.obtain(null, what, mDevice)); |
| } catch (RemoteException e) { |
| Log.w(TAG, "Unable to reach service."); |
| e.printStackTrace(); |
| } |
| } |
| |
| /** |
| * Dialog to display a list of bonded Bluetooth devices for user to select from. This is |
| * needed only for channel connection initiated from the application. |
| */ |
| public static class SelectDeviceDialogFragment extends DialogFragment { |
| |
| public static SelectDeviceDialogFragment newInstance(String[] names, int position) { |
| SelectDeviceDialogFragment frag = new SelectDeviceDialogFragment(); |
| Bundle args = new Bundle(); |
| args.putStringArray("names", names); |
| args.putInt("position", position); |
| frag.setArguments(args); |
| return frag; |
| } |
| |
| @Override |
| public Dialog onCreateDialog(Bundle savedInstanceState) { |
| String[] deviceNames = getArguments().getStringArray("names"); |
| int position = getArguments().getInt("position", -1); |
| if (position == -1) position = 0; |
| return new AlertDialog.Builder(getActivity()) |
| .setTitle(R.string.select_device) |
| .setPositiveButton(R.string.ok, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| ((BluetoothHDPActivity) getActivity()).connectChannel(); |
| } |
| }) |
| .setSingleChoiceItems(deviceNames, position, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| ((BluetoothHDPActivity) getActivity()).setDevice(which); |
| } |
| } |
| ) |
| .create(); |
| } |
| } |
| } |