net.emilymaier.movebot.HeartFragment.java Source code

Java tutorial

Introduction

Here is the source code for net.emilymaier.movebot.HeartFragment.java

Source

/**
 * Copyright  2015 Emily Maier
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of version 2 of the GNU General Public License as published by the
 * Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, see <http://www.gnu.org/licenses>.
 *
 * Linking MoveBot statically or dynamically with other modules is making a
 * combined work based on MoveBot. Thus, the terms and conditions of the GNU
 * General Public License cover the whole combination.
 *
 * In addition, as a special exception, the copyright holders of MoveBot give
 * you permission to combine MoveBot with code included in the standard release
 * of the Google Play Services Library. You may copy and distribute such a
 * system following the terms of the GNU GPL for MoveBot and the licenses of
 * the Google Play Services Library.
 *
 * Note that people who make modified versions of MoveBot are not obligated to
 * grant this special exception for their modified versions; it is their choice
 * whether to do so. The GNU General Public License gives permission to release
 * a modified version without this exception; this exception also makes it
 * possible to release a modified version which carries forward this exception.
 */

package net.emilymaier.movebot;

import android.app.AlertDialog;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;

import java.util.ArrayList;
import java.util.UUID;

/**
 * Fragment for the heart rate monitor.
 */
public class HeartFragment extends Fragment implements View.OnClickListener, BluetoothAdapter.LeScanCallback {
    private class NoBluetoothDialogFragment extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return new AlertDialog.Builder(act).setMessage("Bluetooth not found on this device.")
                    .setPositiveButton("Okay", null).create();
        }
    }

    private class BluetoothDisabledDialogFragment extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return new AlertDialog.Builder(act).setMessage("Bluetooth is disabled on this device.")
                    .setPositiveButton("Okay", null).create();
        }
    }

    private class NoLeDialogFragment extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return new AlertDialog.Builder(act).setMessage("Bluetooth Low Energy not found on this device.")
                    .setPositiveButton("Okay", null).create();
        }
    }

    /**
     * A single Bluetooth LE device that provides the HRM service.
     */
    private class HeartDevice {
        public BluetoothGatt bluetoothGatt;
        public BluetoothGattCharacteristic characteristic;

        @Override
        public String toString() {
            return bluetoothGatt.getDevice().getName();
        }
    }

    private MoveBotActivity act;
    private Button heartScan;
    private ListView heartList;
    private ArrayList<HeartDevice> heartDevices;
    private ArrayAdapter<HeartDevice> heartListAdapter;

    public HeartFragment() {
        super();
    }

    public HeartFragment(MoveBotActivity act) {
        this.act = act;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.main_heart, container, false);
        heartScan = (Button) rootView.findViewById(R.id.heartScan);
        heartList = (ListView) rootView.findViewById(R.id.heartList);
        heartDevices = new ArrayList<>();
        heartListAdapter = new ArrayAdapter<>(act, android.R.layout.simple_list_item_1, heartDevices);
        heartList.setAdapter(heartListAdapter);
        heartList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.d("HeartFragment", "Selecting device");
                for (HeartDevice device : heartDevices) {
                    device.bluetoothGatt.setCharacteristicNotification(device.characteristic, false);
                }
                HeartDevice device = heartDevices.get(position);
                device.bluetoothGatt.setCharacteristicNotification(device.characteristic, true);
                BluetoothGattDescriptor descriptor = device.characteristic
                        .getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                device.bluetoothGatt.writeDescriptor(descriptor);
            }
        });
        heartScan.setOnClickListener(this);
        return rootView;
    }

    @Override
    @SuppressWarnings("deprecation")
    public void onClick(View view) {
        Log.d("HeartFragment", "Scan button clicked");
        final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            new NoBluetoothDialogFragment().show(act.getSupportFragmentManager(), "bluetooth");
            return;
        }
        if (!bluetoothAdapter.isEnabled()) {
            new BluetoothDisabledDialogFragment().show(act.getSupportFragmentManager(), "disabled");
            return;
        }
        if (!act.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            new NoLeDialogFragment().show(act.getSupportFragmentManager(), "le");
            return;
        }
        Log.d("HeartFragment", "Starting scan");
        heartScan.setClickable(false);
        heartScan.setText("Scanning...");
        for (HeartDevice device : heartDevices) {
            device.bluetoothGatt.disconnect();
            device.bluetoothGatt.close();
        }
        heartDevices.clear();
        heartListAdapter.notifyDataSetChanged();
        bluetoothAdapter.startLeScan(new UUID[] { UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb") }, this);
        final HeartFragment t = this;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("HeartFragment", "Stopping scan");
                bluetoothAdapter.stopLeScan(t);
                heartScan.setText("Scan");
                heartScan.setClickable(true);
                heartListAdapter.notifyDataSetChanged();
            }
        }, 5000);
    }

    @Override
    @SuppressWarnings("deprecation")
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        Log.d("HeartFragment", "Bluetooth LE device found");
        BluetoothGatt bluetoothGatt = device.connectGatt(act, false, new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    Log.d("HeartFragment", "Connected to LE device");
                    gatt.discoverServices();
                }
            }

            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    BluetoothGattCharacteristic characteristic = null;
                    for (BluetoothGattService service : gatt.getServices()) {
                        characteristic = service
                                .getCharacteristic(UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb"));
                        if (characteristic != null) {
                            break;
                        }
                    }
                    if (characteristic != null) {
                        Log.d("HeartFragment", "Found device with HRM characteristic");
                        HeartDevice device = new HeartDevice();
                        device.bluetoothGatt = gatt;
                        device.characteristic = characteristic;
                        for (HeartDevice testDevice : heartDevices) {
                            if (testDevice.bluetoothGatt.getDevice().getAddress()
                                    .equals(device.bluetoothGatt.getDevice().getAddress())) {
                                heartDevices.remove(testDevice);
                            }
                        }
                        heartDevices.add(device);
                    } else {
                        Log.d("HeartFragment", "Device does not have HRM characteristic");
                        gatt.disconnect();
                        gatt.close();
                    }
                } else {
                    Log.w("HeartFragment", "Failed to discover device services");
                    gatt.disconnect();
                    gatt.close();
                }
            }

            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                int flag = characteristic.getProperties();
                int format = -1;
                if ((flag & 0x01) != 0) {
                    format = BluetoothGattCharacteristic.FORMAT_UINT16;
                } else {
                    format = BluetoothGattCharacteristic.FORMAT_UINT8;
                }
                final int heartRate = characteristic.getIntValue(format, 1);
                act.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        act.updateHeart(heartRate);
                    }
                });
            }
        });
    }
}