Home | History | Annotate | Download | only in bluetoothlegatt
      1 /*
      2  * Copyright (C) 2013 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.android.bluetoothlegatt;
     18 
     19 import android.app.Activity;
     20 import android.app.ListActivity;
     21 import android.bluetooth.BluetoothAdapter;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.bluetooth.BluetoothManager;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.pm.PackageManager;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.view.LayoutInflater;
     30 import android.view.Menu;
     31 import android.view.MenuItem;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 import android.widget.BaseAdapter;
     35 import android.widget.ListView;
     36 import android.widget.TextView;
     37 import android.widget.Toast;
     38 
     39 import java.util.ArrayList;
     40 
     41 /**
     42  * Activity for scanning and displaying available Bluetooth LE devices.
     43  */
     44 public class DeviceScanActivity extends ListActivity {
     45     private LeDeviceListAdapter mLeDeviceListAdapter;
     46     private BluetoothAdapter mBluetoothAdapter;
     47     private boolean mScanning;
     48     private Handler mHandler;
     49 
     50     private static final int REQUEST_ENABLE_BT = 1;
     51     // Stops scanning after 10 seconds.
     52     private static final long SCAN_PERIOD = 10000;
     53 
     54     @Override
     55     public void onCreate(Bundle savedInstanceState) {
     56         super.onCreate(savedInstanceState);
     57         getActionBar().setTitle(R.string.title_devices);
     58         mHandler = new Handler();
     59 
     60         // Use this check to determine whether BLE is supported on the device.  Then you can
     61         // selectively disable BLE-related features.
     62         if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
     63             Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
     64             finish();
     65         }
     66 
     67         // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
     68         // BluetoothAdapter through BluetoothManager.
     69         final BluetoothManager bluetoothManager =
     70                 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
     71         mBluetoothAdapter = bluetoothManager.getAdapter();
     72 
     73         // Checks if Bluetooth is supported on the device.
     74         if (mBluetoothAdapter == null) {
     75             Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
     76             finish();
     77             return;
     78         }
     79     }
     80 
     81     @Override
     82     public boolean onCreateOptionsMenu(Menu menu) {
     83         getMenuInflater().inflate(R.menu.main, menu);
     84         if (!mScanning) {
     85             menu.findItem(R.id.menu_stop).setVisible(false);
     86             menu.findItem(R.id.menu_scan).setVisible(true);
     87             menu.findItem(R.id.menu_refresh).setActionView(null);
     88         } else {
     89             menu.findItem(R.id.menu_stop).setVisible(true);
     90             menu.findItem(R.id.menu_scan).setVisible(false);
     91             menu.findItem(R.id.menu_refresh).setActionView(
     92                     R.layout.actionbar_indeterminate_progress);
     93         }
     94         return true;
     95     }
     96 
     97     @Override
     98     public boolean onOptionsItemSelected(MenuItem item) {
     99         switch (item.getItemId()) {
    100             case R.id.menu_scan:
    101                 mLeDeviceListAdapter.clear();
    102                 scanLeDevice(true);
    103                 break;
    104             case R.id.menu_stop:
    105                 scanLeDevice(false);
    106                 break;
    107         }
    108         return true;
    109     }
    110 
    111     @Override
    112     protected void onResume() {
    113         super.onResume();
    114 
    115         // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
    116         // fire an intent to display a dialog asking the user to grant permission to enable it.
    117         if (!mBluetoothAdapter.isEnabled()) {
    118             if (!mBluetoothAdapter.isEnabled()) {
    119                 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    120                 startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    121             }
    122         }
    123 
    124         // Initializes list view adapter.
    125         mLeDeviceListAdapter = new LeDeviceListAdapter();
    126         setListAdapter(mLeDeviceListAdapter);
    127         scanLeDevice(true);
    128     }
    129 
    130     @Override
    131     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    132         // User chose not to enable Bluetooth.
    133         if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
    134             finish();
    135             return;
    136         }
    137         super.onActivityResult(requestCode, resultCode, data);
    138     }
    139 
    140     @Override
    141     protected void onPause() {
    142         super.onPause();
    143         scanLeDevice(false);
    144         mLeDeviceListAdapter.clear();
    145     }
    146 
    147     @Override
    148     protected void onListItemClick(ListView l, View v, int position, long id) {
    149         final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
    150         if (device == null) return;
    151         final Intent intent = new Intent(this, DeviceControlActivity.class);
    152         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
    153         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
    154         if (mScanning) {
    155             mBluetoothAdapter.stopLeScan(mLeScanCallback);
    156             mScanning = false;
    157         }
    158         startActivity(intent);
    159     }
    160 
    161     private void scanLeDevice(final boolean enable) {
    162         if (enable) {
    163             // Stops scanning after a pre-defined scan period.
    164             mHandler.postDelayed(new Runnable() {
    165                 @Override
    166                 public void run() {
    167                     mScanning = false;
    168                     mBluetoothAdapter.stopLeScan(mLeScanCallback);
    169                     invalidateOptionsMenu();
    170                 }
    171             }, SCAN_PERIOD);
    172 
    173             mScanning = true;
    174             mBluetoothAdapter.startLeScan(mLeScanCallback);
    175         } else {
    176             mScanning = false;
    177             mBluetoothAdapter.stopLeScan(mLeScanCallback);
    178         }
    179         invalidateOptionsMenu();
    180     }
    181 
    182     // Adapter for holding devices found through scanning.
    183     private class LeDeviceListAdapter extends BaseAdapter {
    184         private ArrayList<BluetoothDevice> mLeDevices;
    185         private LayoutInflater mInflator;
    186 
    187         public LeDeviceListAdapter() {
    188             super();
    189             mLeDevices = new ArrayList<BluetoothDevice>();
    190             mInflator = DeviceScanActivity.this.getLayoutInflater();
    191         }
    192 
    193         public void addDevice(BluetoothDevice device) {
    194             if(!mLeDevices.contains(device)) {
    195                 mLeDevices.add(device);
    196             }
    197         }
    198 
    199         public BluetoothDevice getDevice(int position) {
    200             return mLeDevices.get(position);
    201         }
    202 
    203         public void clear() {
    204             mLeDevices.clear();
    205         }
    206 
    207         @Override
    208         public int getCount() {
    209             return mLeDevices.size();
    210         }
    211 
    212         @Override
    213         public Object getItem(int i) {
    214             return mLeDevices.get(i);
    215         }
    216 
    217         @Override
    218         public long getItemId(int i) {
    219             return i;
    220         }
    221 
    222         @Override
    223         public View getView(int i, View view, ViewGroup viewGroup) {
    224             ViewHolder viewHolder;
    225             // General ListView optimization code.
    226             if (view == null) {
    227                 view = mInflator.inflate(R.layout.listitem_device, null);
    228                 viewHolder = new ViewHolder();
    229                 viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
    230                 viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
    231                 view.setTag(viewHolder);
    232             } else {
    233                 viewHolder = (ViewHolder) view.getTag();
    234             }
    235 
    236             BluetoothDevice device = mLeDevices.get(i);
    237             final String deviceName = device.getName();
    238             if (deviceName != null && deviceName.length() > 0)
    239                 viewHolder.deviceName.setText(deviceName);
    240             else
    241                 viewHolder.deviceName.setText(R.string.unknown_device);
    242             viewHolder.deviceAddress.setText(device.getAddress());
    243 
    244             return view;
    245         }
    246     }
    247 
    248     // Device scan callback.
    249     private BluetoothAdapter.LeScanCallback mLeScanCallback =
    250             new BluetoothAdapter.LeScanCallback() {
    251 
    252         @Override
    253         public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
    254             runOnUiThread(new Runnable() {
    255                 @Override
    256                 public void run() {
    257                     mLeDeviceListAdapter.addDevice(device);
    258                     mLeDeviceListAdapter.notifyDataSetChanged();
    259                 }
    260             });
    261         }
    262     };
    263 
    264     static class ViewHolder {
    265         TextView deviceName;
    266         TextView deviceAddress;
    267     }
    268 }