• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onDestroy()223     protected void onDestroy() {
224         super.onDestroy();
225         if (mHealthServiceBound) unbindService(mConnection);
226         unregisterReceiver(mReceiver);
227     }
228 
229     @Override
onStart()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
onActivityResult(int requestCode, int resultCode, Intent data)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      */
setDevice(int position)263     public void setDevice(int position) {
264         mDevice = this.mAllBondedDevices[position];
265         mDeviceIndex = position;
266     }
267 
connectChannel()268     private void connectChannel() {
269         sendMessageWithDevice(BluetoothHDPService.MSG_CONNECT_CHANNEL);
270     }
271 
disconnectChannel()272     private void disconnectChannel() {
273         sendMessageWithDevice(BluetoothHDPService.MSG_DISCONNECT_CHANNEL);
274     }
275 
initialize()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.
initIntentFilter()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}.
sendMessage(int what, int value)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.
sendMessageWithDevice(int what)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 
newInstance(String[] names, int position)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
onCreateDialog(Bundle savedInstanceState)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