Home | History | Annotate | Download | only in health
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.bluetooth.health;
     18 
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.bluetooth.BluetoothAdapter;
     24 import android.bluetooth.BluetoothDevice;
     25 import android.content.BroadcastReceiver;
     26 import android.content.ComponentName;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.ServiceConnection;
     32 import android.content.res.Resources;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.Message;
     37 import android.os.Messenger;
     38 import android.os.RemoteException;
     39 import android.util.Log;
     40 import android.view.View;
     41 import android.widget.Button;
     42 import android.widget.ImageView;
     43 import android.widget.TextView;
     44 import android.widget.Toast;
     45 
     46 /**
     47  * Main user interface for the Sample application.  All Bluetooth health-related
     48  * operations happen in {@link BluetoothHDPService}.  This activity passes messages to and from
     49  * the service.
     50  */
     51 public class BluetoothHDPActivity extends Activity {
     52     private static final String TAG = "BluetoothHealthActivity";
     53 
     54     // Use the appropriate IEEE 11073 data types based on the devices used.
     55     // Below are some examples.  Refer to relevant Bluetooth HDP specifications for detail.
     56     //     0x1007 - blood pressure meter
     57     //     0x1008 - body thermometer
     58     //     0x100F - body weight scale
     59     private static final int HEALTH_PROFILE_SOURCE_DATA_TYPE = 0x1007;
     60 
     61     private static final int REQUEST_ENABLE_BT = 1;
     62 
     63     private TextView mConnectIndicator;
     64     private ImageView mDataIndicator;
     65     private TextView mStatusMessage;
     66 
     67     private BluetoothAdapter mBluetoothAdapter;
     68     private BluetoothDevice[] mAllBondedDevices;
     69     private BluetoothDevice mDevice;
     70     private int mDeviceIndex = 0;
     71     private Resources mRes;
     72     private Messenger mHealthService;
     73     private boolean mHealthServiceBound;
     74 
     75     // Handles events sent by {@link HealthHDPService}.
     76     private Handler mIncomingHandler = new Handler() {
     77         @Override
     78         public void handleMessage(Message msg) {
     79             switch (msg.what) {
     80                 // Application registration complete.
     81                 case BluetoothHDPService.STATUS_HEALTH_APP_REG:
     82                     mStatusMessage.setText(
     83                             String.format(mRes.getString(R.string.status_reg),
     84                             msg.arg1));
     85                     break;
     86                 // Application unregistration complete.
     87                 case BluetoothHDPService.STATUS_HEALTH_APP_UNREG:
     88                     mStatusMessage.setText(
     89                             String.format(mRes.getString(R.string.status_unreg),
     90                             msg.arg1));
     91                     break;
     92                 // Reading data from HDP device.
     93                 case BluetoothHDPService.STATUS_READ_DATA:
     94                     mStatusMessage.setText(mRes.getString(R.string.read_data));
     95                     mDataIndicator.setImageLevel(1);
     96                     break;
     97                 // Finish reading data from HDP device.
     98                 case BluetoothHDPService.STATUS_READ_DATA_DONE:
     99                     mStatusMessage.setText(mRes.getString(R.string.read_data_done));
    100                     mDataIndicator.setImageLevel(0);
    101                     break;
    102                 // Channel creation complete.  Some devices will automatically establish
    103                 // connection.
    104                 case BluetoothHDPService.STATUS_CREATE_CHANNEL:
    105                     mStatusMessage.setText(
    106                             String.format(mRes.getString(R.string.status_create_channel),
    107                             msg.arg1));
    108                     mConnectIndicator.setText(R.string.connected);
    109                     break;
    110                 // Channel destroy complete.  This happens when either the device disconnects or
    111                 // there is extended inactivity.
    112                 case BluetoothHDPService.STATUS_DESTROY_CHANNEL:
    113                     mStatusMessage.setText(
    114                             String.format(mRes.getString(R.string.status_destroy_channel),
    115                             msg.arg1));
    116                     mConnectIndicator.setText(R.string.disconnected);
    117                     break;
    118                 default:
    119                     super.handleMessage(msg);
    120             }
    121         }
    122     };
    123 
    124     private final Messenger mMessenger = new Messenger(mIncomingHandler);
    125 
    126     @Override
    127     public void onCreate(Bundle savedInstanceState) {
    128         super.onCreate(savedInstanceState);
    129 
    130         // Check for Bluetooth availability on the Android platform.
    131         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    132         if (mBluetoothAdapter == null) {
    133             Toast.makeText(this, R.string.bluetooth_not_available, Toast.LENGTH_LONG);
    134             finish();
    135             return;
    136         }
    137         setContentView(R.layout.console);
    138         mConnectIndicator = (TextView) findViewById(R.id.connect_ind);
    139         mStatusMessage = (TextView) findViewById(R.id.status_msg);
    140         mDataIndicator = (ImageView) findViewById(R.id.data_ind);
    141         mRes = getResources();
    142         mHealthServiceBound = false;
    143 
    144         // Initiates application registration through {@link BluetoothHDPService}.
    145         Button registerAppButton = (Button) findViewById(R.id.button_register_app);
    146         registerAppButton.setOnClickListener(new View.OnClickListener() {
    147             public void onClick(View v) {
    148                 sendMessage(BluetoothHDPService.MSG_REG_HEALTH_APP,
    149                         HEALTH_PROFILE_SOURCE_DATA_TYPE);
    150             }
    151         });
    152 
    153         // Initiates application unregistration through {@link BluetoothHDPService}.
    154         Button unregisterAppButton = (Button) findViewById(R.id.button_unregister_app);
    155         unregisterAppButton.setOnClickListener(new View.OnClickListener() {
    156             public void onClick(View v) {
    157                 sendMessage(BluetoothHDPService.MSG_UNREG_HEALTH_APP, 0);
    158             }
    159         });
    160 
    161         // Initiates channel creation through {@link BluetoothHDPService}.  Some devices will
    162         // initiate the channel connection, in which case, it is not necessary to do this in the
    163         // application.  When pressed, the user is asked to select from one of the bonded devices
    164         // to connect to.
    165         Button connectButton = (Button) findViewById(R.id.button_connect_channel);
    166         connectButton.setOnClickListener(new View.OnClickListener() {
    167             public void onClick(View v) {
    168                 mAllBondedDevices =
    169                         (BluetoothDevice[]) mBluetoothAdapter.getBondedDevices().toArray(
    170                                 new BluetoothDevice[0]);
    171 
    172                 if (mAllBondedDevices.length > 0) {
    173                     int deviceCount = mAllBondedDevices.length;
    174                     if (mDeviceIndex < deviceCount) mDevice = mAllBondedDevices[mDeviceIndex];
    175                     else {
    176                         mDeviceIndex = 0;
    177                         mDevice = mAllBondedDevices[0];
    178                     }
    179                     String[] deviceNames = new String[deviceCount];
    180                     int i = 0;
    181                     for (BluetoothDevice device : mAllBondedDevices) {
    182                         deviceNames[i++] = device.getName();
    183                     }
    184                     SelectDeviceDialogFragment deviceDialog =
    185                             SelectDeviceDialogFragment.newInstance(deviceNames, mDeviceIndex);
    186                     deviceDialog.show(getFragmentManager(), "deviceDialog");
    187                 }
    188             }
    189         });
    190 
    191         // Initiates channel disconnect through {@link BluetoothHDPService}.
    192         Button disconnectButton = (Button) findViewById(R.id.button_disconnect_channel);
    193         disconnectButton.setOnClickListener(new View.OnClickListener() {
    194             public void onClick(View v) {
    195                 disconnectChannel();
    196             }
    197         });
    198         registerReceiver(mReceiver, initIntentFilter());
    199     }
    200 
    201     // Sets up communication with {@link BluetoothHDPService}.
    202     private ServiceConnection mConnection = new ServiceConnection() {
    203         public void onServiceConnected(ComponentName name, IBinder service) {
    204             mHealthServiceBound = true;
    205             Message msg = Message.obtain(null, BluetoothHDPService.MSG_REG_CLIENT);
    206             msg.replyTo = mMessenger;
    207             mHealthService = new Messenger(service);
    208             try {
    209                 mHealthService.send(msg);
    210             } catch (RemoteException e) {
    211                 Log.w(TAG, "Unable to register client to service.");
    212                 e.printStackTrace();
    213             }
    214         }
    215 
    216         public void onServiceDisconnected(ComponentName name) {
    217             mHealthService = null;
    218             mHealthServiceBound = false;
    219         }
    220     };
    221 
    222     @Override
    223     protected void onDestroy() {
    224         super.onDestroy();
    225         if (mHealthServiceBound) unbindService(mConnection);
    226         unregisterReceiver(mReceiver);
    227     }
    228 
    229     @Override
    230     protected void onStart() {
    231         super.onStart();
    232         // If Bluetooth is not on, request that it be enabled.
    233         if (!mBluetoothAdapter.isEnabled()) {
    234             Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    235             startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    236         } else {
    237             initialize();
    238         }
    239     }
    240 
    241     /**
    242      * Ensures user has turned on Bluetooth on the Android device.
    243      */
    244     @Override
    245     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    246         switch (requestCode) {
    247         case REQUEST_ENABLE_BT:
    248             if (resultCode == Activity.RESULT_OK) {
    249                 initialize();
    250             } else {
    251                 finish();
    252                 return;
    253             }
    254         }
    255     }
    256 
    257     /**
    258      * Used by {@link SelectDeviceDialogFragment} to record the bonded Bluetooth device selected
    259      * by the user.
    260      *
    261      * @param position Position of the bonded Bluetooth device in the array.
    262      */
    263     public void setDevice(int position) {
    264         mDevice = this.mAllBondedDevices[position];
    265         mDeviceIndex = position;
    266     }
    267 
    268     private void connectChannel() {
    269         sendMessageWithDevice(BluetoothHDPService.MSG_CONNECT_CHANNEL);
    270     }
    271 
    272     private void disconnectChannel() {
    273         sendMessageWithDevice(BluetoothHDPService.MSG_DISCONNECT_CHANNEL);
    274     }
    275 
    276     private void initialize() {
    277         // Starts health service.
    278         Intent intent = new Intent(this, BluetoothHDPService.class);
    279         startService(intent);
    280         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    281     }
    282 
    283     // Intent filter and broadcast receive to handle Bluetooth on event.
    284     private IntentFilter initIntentFilter() {
    285         IntentFilter filter = new IntentFilter();
    286         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    287         return filter;
    288     }
    289 
    290     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    291         @Override
    292         public void onReceive(Context context, Intent intent) {
    293             final String action = intent.getAction();
    294             if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
    295                 if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) ==
    296                     BluetoothAdapter.STATE_ON) {
    297                     initialize();
    298                 }
    299             }
    300         }
    301     };
    302 
    303     // Sends a message to {@link BluetoothHDPService}.
    304     private void sendMessage(int what, int value) {
    305         if (mHealthService == null) {
    306             Log.d(TAG, "Health Service not connected.");
    307             return;
    308         }
    309 
    310         try {
    311             mHealthService.send(Message.obtain(null, what, value, 0));
    312         } catch (RemoteException e) {
    313             Log.w(TAG, "Unable to reach service.");
    314             e.printStackTrace();
    315         }
    316     }
    317 
    318     // Sends an update message, along with an HDP BluetoothDevice object, to
    319     // {@link BluetoothHDPService}.  The BluetoothDevice object is needed by the channel creation
    320     // method.
    321     private void sendMessageWithDevice(int what) {
    322         if (mHealthService == null) {
    323             Log.d(TAG, "Health Service not connected.");
    324             return;
    325         }
    326 
    327         try {
    328             mHealthService.send(Message.obtain(null, what, mDevice));
    329         } catch (RemoteException e) {
    330             Log.w(TAG, "Unable to reach service.");
    331             e.printStackTrace();
    332         }
    333     }
    334 
    335     /**
    336      * Dialog to display a list of bonded Bluetooth devices for user to select from.  This is
    337      * needed only for channel connection initiated from the application.
    338      */
    339     public static class SelectDeviceDialogFragment extends DialogFragment {
    340 
    341         public static SelectDeviceDialogFragment newInstance(String[] names, int position) {
    342             SelectDeviceDialogFragment frag = new SelectDeviceDialogFragment();
    343             Bundle args = new Bundle();
    344             args.putStringArray("names", names);
    345             args.putInt("position", position);
    346             frag.setArguments(args);
    347             return frag;
    348         }
    349 
    350         @Override
    351         public Dialog onCreateDialog(Bundle savedInstanceState) {
    352             String[] deviceNames = getArguments().getStringArray("names");
    353             int position = getArguments().getInt("position", -1);
    354             if (position == -1) position = 0;
    355             return new AlertDialog.Builder(getActivity())
    356                     .setTitle(R.string.select_device)
    357                     .setPositiveButton(R.string.ok,
    358                         new DialogInterface.OnClickListener() {
    359                             public void onClick(DialogInterface dialog, int which) {
    360                                 ((BluetoothHDPActivity) getActivity()).connectChannel();
    361                             }
    362                         })
    363                     .setSingleChoiceItems(deviceNames, position,
    364                         new DialogInterface.OnClickListener() {
    365                             public void onClick(DialogInterface dialog, int which) {
    366                                 ((BluetoothHDPActivity) getActivity()).setDevice(which);
    367                             }
    368                         }
    369                     )
    370                     .create();
    371         }
    372     }
    373 }
    374