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