2017-03-10 14:53:19 +01:00
|
|
|
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
|
|
|
Gobbetti, JohnnySun, Lem Dulfo, Uwe Hermann
|
|
|
|
|
|
|
|
This file is part of Gadgetbridge.
|
|
|
|
|
|
|
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published
|
|
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Gadgetbridge 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 Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
2015-08-03 23:09:49 +02:00
|
|
|
package nodomain.freeyourgadget.gadgetbridge.activities;
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2016-05-26 14:58:36 +02:00
|
|
|
import android.Manifest;
|
2016-09-23 05:55:34 +02:00
|
|
|
import android.annotation.TargetApi;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.app.Activity;
|
|
|
|
import android.bluetooth.BluetoothAdapter;
|
|
|
|
import android.bluetooth.BluetoothDevice;
|
|
|
|
import android.bluetooth.BluetoothManager;
|
2016-09-23 05:55:34 +02:00
|
|
|
import android.bluetooth.le.ScanCallback;
|
2016-11-27 01:09:20 +01:00
|
|
|
import android.bluetooth.le.ScanFilter;
|
2016-11-27 02:41:52 +01:00
|
|
|
import android.bluetooth.le.ScanRecord;
|
2016-09-23 05:55:34 +02:00
|
|
|
import android.bluetooth.le.ScanResult;
|
|
|
|
import android.bluetooth.le.ScanSettings;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2016-05-26 14:58:36 +02:00
|
|
|
import android.content.pm.PackageManager;
|
2016-09-23 05:55:34 +02:00
|
|
|
import android.os.Build;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.os.Bundle;
|
2015-05-06 22:06:09 +02:00
|
|
|
import android.os.Handler;
|
2015-05-10 23:14:32 +02:00
|
|
|
import android.os.Message;
|
2016-08-14 23:21:09 +02:00
|
|
|
import android.os.ParcelUuid;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.os.Parcelable;
|
2016-05-26 14:58:36 +02:00
|
|
|
import android.support.v4.app.ActivityCompat;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.view.View;
|
|
|
|
import android.widget.AdapterView;
|
|
|
|
import android.widget.Button;
|
|
|
|
import android.widget.ListView;
|
|
|
|
import android.widget.ProgressBar;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
2015-05-12 06:28:11 +02:00
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2015-05-18 20:56:19 +02:00
|
|
|
import java.util.ArrayList;
|
2016-11-27 01:09:20 +01:00
|
|
|
import java.util.List;
|
2015-05-18 20:56:19 +02:00
|
|
|
|
2016-09-26 22:30:15 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
2015-09-24 14:45:21 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter;
|
2015-08-03 23:09:49 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
2015-09-24 14:45:21 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
2015-08-03 23:09:49 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
2016-11-27 01:09:20 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
2016-11-27 02:41:52 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
2015-08-03 23:09:49 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY;
|
|
|
|
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY;
|
|
|
|
|
2016-04-14 15:21:25 +02:00
|
|
|
public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemClickListener {
|
2015-05-12 06:28:11 +02:00
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
2015-05-06 22:06:09 +02:00
|
|
|
private static final long SCAN_DURATION = 60000; // 60s
|
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
private ScanCallback newLeScanCallback = null;
|
|
|
|
|
2015-11-23 23:04:46 +01:00
|
|
|
private final Handler handler = new Handler();
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2015-11-23 23:04:46 +01:00
|
|
|
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
|
2015-05-05 00:48:02 +02:00
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
switch (intent.getAction()) {
|
|
|
|
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
|
2016-10-03 22:31:33 +02:00
|
|
|
if (isScanning != Scanning.SCANNING_BTLE && isScanning != Scanning.SCANNING_NEW_BTLE) {
|
|
|
|
discoveryStarted(Scanning.SCANNING_BT);
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
break;
|
|
|
|
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
|
2016-10-28 23:48:13 +02:00
|
|
|
handler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
// continue with LE scan, if available
|
|
|
|
if (isScanning == Scanning.SCANNING_BT) {
|
|
|
|
checkAndRequestLocationPermission();
|
|
|
|
if (GBApplication.isRunningLollipopOrLater()) {
|
|
|
|
startDiscovery(Scanning.SCANNING_NEW_BTLE);
|
|
|
|
} else {
|
|
|
|
startDiscovery(Scanning.SCANNING_BTLE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
|
|
|
}
|
2016-09-26 22:30:15 +02:00
|
|
|
}
|
2016-10-28 23:48:13 +02:00
|
|
|
});
|
2015-05-05 00:48:02 +02:00
|
|
|
break;
|
|
|
|
case BluetoothAdapter.ACTION_STATE_CHANGED:
|
|
|
|
int oldState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF);
|
|
|
|
int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
|
|
|
bluetoothStateChanged(oldState, newState);
|
|
|
|
break;
|
2015-05-10 21:32:41 +02:00
|
|
|
case BluetoothDevice.ACTION_FOUND: {
|
2015-05-09 23:54:47 +02:00
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
|
|
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
|
|
|
|
handleDeviceFound(device, rssi);
|
2015-05-05 00:48:02 +02:00
|
|
|
break;
|
2015-05-10 21:32:41 +02:00
|
|
|
}
|
2016-11-27 02:41:52 +01:00
|
|
|
case BluetoothDevice.ACTION_UUID: {
|
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
|
|
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
|
|
|
|
Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
|
|
|
|
ParcelUuid[] uuids2 = AndroidUtils.toParcelUUids(uuids);
|
|
|
|
handleDeviceFound(device, rssi, uuids2);
|
|
|
|
break;
|
|
|
|
}
|
2015-05-10 21:32:41 +02:00
|
|
|
case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
|
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
|
|
if (device != null && device.getAddress().equals(bondingAddress)) {
|
|
|
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
|
|
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
2017-03-04 16:03:36 +01:00
|
|
|
handleDeviceBonded();
|
2015-05-10 21:32:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2015-05-09 23:54:47 +02:00
|
|
|
|
2017-03-04 16:03:36 +01:00
|
|
|
private void handleDeviceBonded() {
|
|
|
|
GB.toast(DiscoveryActivity.this, "Successfully bonded with: " + bondingAddress, Toast.LENGTH_SHORT, GB.INFO);
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
2015-11-23 23:04:46 +01:00
|
|
|
private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
|
2015-05-09 23:54:47 +02:00
|
|
|
@Override
|
|
|
|
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
2016-08-14 23:21:09 +02:00
|
|
|
LOG.warn(device.getName() + ": " + ((scanRecord != null) ? scanRecord.length : -1));
|
|
|
|
logMessageContent(scanRecord);
|
2015-05-09 23:54:47 +02:00
|
|
|
handleDeviceFound(device, (short) rssi);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
|
2016-12-01 20:18:36 +01:00
|
|
|
// why use a method to get callback?
|
2016-09-23 05:55:34 +02:00
|
|
|
// because this callback need API >= 21
|
|
|
|
// we cant add @TARGETAPI("Lollipop") at class header
|
2016-12-01 20:18:36 +01:00
|
|
|
// so use a method with SDK check to return this callback
|
2016-09-23 05:55:34 +02:00
|
|
|
private ScanCallback getScanCallback() {
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
newLeScanCallback = new ScanCallback() {
|
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
@Override
|
|
|
|
public void onScanResult(int callbackType, ScanResult result) {
|
|
|
|
super.onScanResult(callbackType, result);
|
|
|
|
try {
|
2016-11-27 02:41:52 +01:00
|
|
|
ScanRecord scanRecord = result.getScanRecord();
|
|
|
|
ParcelUuid[] uuids = null;
|
|
|
|
if (scanRecord != null) {
|
|
|
|
//logMessageContent(scanRecord.getBytes());
|
|
|
|
List<ParcelUuid> serviceUuids = scanRecord.getServiceUuids();
|
|
|
|
if (serviceUuids != null) {
|
|
|
|
uuids = serviceUuids.toArray(new ParcelUuid[0]);
|
|
|
|
}
|
|
|
|
}
|
2016-09-23 05:55:34 +02:00
|
|
|
LOG.warn(result.getDevice().getName() + ": " +
|
2016-11-27 02:41:52 +01:00
|
|
|
((scanRecord != null) ? scanRecord.getBytes().length : -1));
|
|
|
|
handleDeviceFound(result.getDevice(), (short) result.getRssi(), uuids);
|
2016-09-23 05:55:34 +02:00
|
|
|
} catch (NullPointerException e) {
|
2016-09-25 23:05:27 +02:00
|
|
|
LOG.warn("Error handling scan result", e);
|
2016-09-23 05:55:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return newLeScanCallback;
|
|
|
|
}
|
|
|
|
|
2016-08-14 23:21:09 +02:00
|
|
|
public void logMessageContent(byte[] value) {
|
|
|
|
if (value != null) {
|
|
|
|
for (byte b : value) {
|
|
|
|
LOG.warn("DATA: " + String.format("0x%2x", b) + " - " + (char) (b & 0xff));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-23 23:04:46 +01:00
|
|
|
private final Runnable stopRunnable = new Runnable() {
|
2015-05-06 22:06:09 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
stopDiscovery();
|
|
|
|
}
|
|
|
|
};
|
2015-05-05 00:48:02 +02:00
|
|
|
|
|
|
|
private ProgressBar progressView;
|
|
|
|
private BluetoothAdapter adapter;
|
2015-11-23 23:04:46 +01:00
|
|
|
private final ArrayList<GBDeviceCandidate> deviceCandidates = new ArrayList<>();
|
2015-05-05 00:48:02 +02:00
|
|
|
private DeviceCandidateAdapter cadidateListAdapter;
|
|
|
|
private Button startButton;
|
2015-05-09 23:54:47 +02:00
|
|
|
private Scanning isScanning = Scanning.SCANNING_OFF;
|
2015-05-10 21:32:41 +02:00
|
|
|
private String bondingAddress;
|
2015-05-09 23:54:47 +02:00
|
|
|
|
|
|
|
private enum Scanning {
|
|
|
|
SCANNING_BT,
|
|
|
|
SCANNING_BTLE,
|
2016-09-23 05:55:34 +02:00
|
|
|
SCANNING_NEW_BTLE,
|
2015-05-09 23:54:47 +02:00
|
|
|
SCANNING_OFF
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
|
|
|
setContentView(R.layout.activity_discovery);
|
|
|
|
startButton = (Button) findViewById(R.id.discovery_start);
|
|
|
|
startButton.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
onStartButtonClick(startButton);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
progressView = (ProgressBar) findViewById(R.id.discovery_progressbar);
|
|
|
|
progressView.setProgress(0);
|
|
|
|
progressView.setIndeterminate(true);
|
|
|
|
progressView.setVisibility(View.GONE);
|
2015-05-12 20:09:35 +02:00
|
|
|
ListView deviceCandidatesView = (ListView) findViewById(R.id.discovery_deviceCandidatesView);
|
2015-05-05 00:48:02 +02:00
|
|
|
|
|
|
|
cadidateListAdapter = new DeviceCandidateAdapter(this, deviceCandidates);
|
|
|
|
deviceCandidatesView.setAdapter(cadidateListAdapter);
|
|
|
|
deviceCandidatesView.setOnItemClickListener(this);
|
|
|
|
|
|
|
|
IntentFilter bluetoothIntents = new IntentFilter();
|
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND);
|
2016-11-27 02:41:52 +01:00
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_UUID);
|
2015-05-10 21:32:41 +02:00
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
|
2015-05-05 00:48:02 +02:00
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
|
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
|
|
|
|
|
|
|
registerReceiver(bluetoothReceiver, bluetoothIntents);
|
|
|
|
|
|
|
|
startDiscovery();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
outState.putParcelableArrayList("deviceCandidates", deviceCandidates);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
|
|
ArrayList<Parcelable> restoredCandidates = savedInstanceState.getParcelableArrayList("deviceCandidates");
|
|
|
|
if (restoredCandidates != null) {
|
|
|
|
deviceCandidates.clear();
|
|
|
|
for (Parcelable p : restoredCandidates) {
|
2015-08-03 23:09:49 +02:00
|
|
|
deviceCandidates.add((GBDeviceCandidate) p);
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onStartButtonClick(View button) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.debug("Start Button clicked");
|
2015-05-09 23:54:47 +02:00
|
|
|
if (isScanning()) {
|
2015-05-05 00:48:02 +02:00
|
|
|
stopDiscovery();
|
|
|
|
} else {
|
|
|
|
startDiscovery();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2016-10-03 22:31:33 +02:00
|
|
|
try {
|
|
|
|
unregisterReceiver(bluetoothReceiver);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
LOG.warn("Tried to unregister Bluetooth Receiver that wasn't registered.");
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void handleDeviceFound(BluetoothDevice device, short rssi) {
|
2016-11-27 02:41:52 +01:00
|
|
|
ParcelUuid[] uuids = device.getUuids();
|
|
|
|
if (uuids == null) {
|
|
|
|
if (device.fetchUuidsWithSdp()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDeviceFound(device, rssi, uuids);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void handleDeviceFound(BluetoothDevice device, short rssi, ParcelUuid[] uuids) {
|
2016-07-05 22:39:05 +02:00
|
|
|
LOG.debug("found device: " + device.getName() + ", " + device.getAddress());
|
2016-08-14 23:21:09 +02:00
|
|
|
if (LOG.isDebugEnabled()) {
|
|
|
|
if (uuids != null && uuids.length > 0) {
|
|
|
|
for (ParcelUuid uuid : uuids) {
|
|
|
|
LOG.debug(" supports uuid: " + uuid.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-05 20:46:47 +02:00
|
|
|
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
|
|
|
|
return; // ignore already bonded devices
|
|
|
|
}
|
|
|
|
|
2016-11-27 02:41:52 +01:00
|
|
|
GBDeviceCandidate candidate = new GBDeviceCandidate(device, rssi, uuids);
|
2016-11-27 01:09:20 +01:00
|
|
|
DeviceType deviceType = DeviceHelper.getInstance().getSupportedType(candidate);
|
|
|
|
if (deviceType.isSupported()) {
|
|
|
|
candidate.setDeviceType(deviceType);
|
2017-01-28 22:52:22 +01:00
|
|
|
LOG.info("Recognized supported device: " + candidate);
|
2015-05-09 23:54:47 +02:00
|
|
|
int index = deviceCandidates.indexOf(candidate);
|
|
|
|
if (index >= 0) {
|
|
|
|
deviceCandidates.set(index, candidate); // replace
|
|
|
|
} else {
|
|
|
|
deviceCandidates.add(candidate);
|
|
|
|
}
|
|
|
|
cadidateListAdapter.notifyDataSetChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 00:48:02 +02:00
|
|
|
/**
|
2015-05-09 23:54:47 +02:00
|
|
|
* Pre: bluetooth is available, enabled and scanning is off.
|
|
|
|
* Post: BT is discovering
|
2015-05-05 00:48:02 +02:00
|
|
|
*/
|
|
|
|
private void startDiscovery() {
|
2015-05-09 23:54:47 +02:00
|
|
|
if (isScanning()) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.warn("Not starting discovery, because already scanning.");
|
2015-05-06 22:06:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-09-26 22:30:15 +02:00
|
|
|
startDiscovery(Scanning.SCANNING_BT);
|
2015-05-10 00:18:42 +02:00
|
|
|
}
|
2015-05-06 22:06:09 +02:00
|
|
|
|
2015-05-10 00:18:42 +02:00
|
|
|
private void startDiscovery(Scanning what) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.info("Starting discovery: " + what);
|
2015-05-10 00:18:42 +02:00
|
|
|
discoveryStarted(what); // just to make sure
|
2015-05-05 00:48:02 +02:00
|
|
|
if (ensureBluetoothReady()) {
|
2015-05-10 00:18:42 +02:00
|
|
|
if (what == Scanning.SCANNING_BT) {
|
|
|
|
startBTDiscovery();
|
2015-05-18 20:56:19 +02:00
|
|
|
} else if (what == Scanning.SCANNING_BTLE) {
|
2015-05-10 00:18:42 +02:00
|
|
|
if (GB.supportsBluetoothLE()) {
|
|
|
|
startBTLEDiscovery();
|
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
|
|
|
}
|
2016-09-23 05:55:34 +02:00
|
|
|
} else if (what == Scanning.SCANNING_NEW_BTLE) {
|
|
|
|
if (GB.supportsBluetoothLE()) {
|
|
|
|
startNEWBTLEDiscovery();
|
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
|
|
|
}
|
2015-05-10 00:18:42 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
|
|
|
Toast.makeText(this, "Enable Bluetooth to discover devices.", Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private boolean isScanning() {
|
|
|
|
return isScanning != Scanning.SCANNING_OFF;
|
|
|
|
}
|
|
|
|
|
2015-05-05 00:48:02 +02:00
|
|
|
private void stopDiscovery() {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.info("Stopping discovery");
|
2015-05-09 23:54:47 +02:00
|
|
|
if (isScanning()) {
|
2015-05-11 00:50:18 +02:00
|
|
|
Scanning wasScanning = isScanning;
|
|
|
|
// unfortunately, we don't always get a call back when stopping the scan, so
|
|
|
|
// we do it manually; BEFORE stopping the scan!
|
|
|
|
discoveryFinished();
|
|
|
|
|
|
|
|
if (wasScanning == Scanning.SCANNING_BT) {
|
2015-05-09 23:54:47 +02:00
|
|
|
stopBTDiscovery();
|
2015-05-11 00:50:18 +02:00
|
|
|
} else if (wasScanning == Scanning.SCANNING_BTLE) {
|
2015-05-09 23:54:47 +02:00
|
|
|
stopBTLEDiscovery();
|
2016-09-23 05:55:34 +02:00
|
|
|
} else if (wasScanning == Scanning.SCANNING_NEW_BTLE) {
|
|
|
|
stopNewBTLEDiscovery();
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
2015-05-06 22:06:09 +02:00
|
|
|
handler.removeMessages(0, stopRunnable);
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void stopBTLEDiscovery() {
|
|
|
|
adapter.stopLeScan(leScanCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void stopBTDiscovery() {
|
|
|
|
adapter.cancelDiscovery();
|
|
|
|
}
|
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
private void stopNewBTLEDiscovery() {
|
|
|
|
adapter.getBluetoothLeScanner().stopScan(newLeScanCallback);
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void bluetoothStateChanged(int oldState, int newState) {
|
|
|
|
discoveryFinished();
|
2016-03-26 20:45:07 +01:00
|
|
|
if (newState == BluetoothAdapter.STATE_ON) {
|
|
|
|
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
|
|
|
startButton.setEnabled(true);
|
|
|
|
} else {
|
|
|
|
this.adapter = null;
|
|
|
|
startButton.setEnabled(false);
|
|
|
|
}
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void discoveryFinished() {
|
|
|
|
isScanning = Scanning.SCANNING_OFF;
|
|
|
|
progressView.setVisibility(View.GONE);
|
|
|
|
startButton.setText(getString(R.string.discovery_start_scanning));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void discoveryStarted(Scanning what) {
|
|
|
|
isScanning = what;
|
|
|
|
progressView.setVisibility(View.VISIBLE);
|
|
|
|
startButton.setText(getString(R.string.discovery_stop_scanning));
|
|
|
|
}
|
|
|
|
|
2015-05-05 00:48:02 +02:00
|
|
|
private boolean ensureBluetoothReady() {
|
|
|
|
boolean available = checkBluetoothAvailable();
|
|
|
|
startButton.setEnabled(available);
|
|
|
|
if (available) {
|
|
|
|
adapter.cancelDiscovery();
|
|
|
|
// must not return the result of cancelDiscovery()
|
|
|
|
// appears to return false when currently not scanning
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean checkBluetoothAvailable() {
|
|
|
|
BluetoothManager bluetoothService = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
|
|
|
|
if (bluetoothService == null) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.warn("No bluetooth available");
|
2015-05-05 00:48:02 +02:00
|
|
|
this.adapter = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
BluetoothAdapter adapter = bluetoothService.getAdapter();
|
2016-04-09 03:12:40 +02:00
|
|
|
if (adapter == null) {
|
|
|
|
LOG.warn("No bluetooth available");
|
|
|
|
this.adapter = null;
|
|
|
|
return false;
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
if (!adapter.isEnabled()) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.warn("Bluetooth not enabled");
|
2016-03-26 20:45:07 +01:00
|
|
|
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
|
|
|
startActivity(enableBtIntent);
|
2015-05-05 00:48:02 +02:00
|
|
|
this.adapter = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
this.adapter = adapter;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
// New BTLE Discovery use startScan (List<ScanFilter> filters,
|
|
|
|
// ScanSettings settings,
|
|
|
|
// ScanCallback callback)
|
2016-12-01 20:18:36 +01:00
|
|
|
// It's added on API21
|
2016-09-23 05:55:34 +02:00
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
private void startNEWBTLEDiscovery() {
|
2016-12-01 20:18:36 +01:00
|
|
|
// Only use new API when user uses Lollipop+ device
|
2016-09-23 05:55:34 +02:00
|
|
|
LOG.info("Start New BTLE Discovery");
|
|
|
|
handler.removeMessages(0, stopRunnable);
|
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
2016-11-27 01:09:20 +01:00
|
|
|
adapter.getBluetoothLeScanner().startScan(getScanFilters(), getScanSettings(), getScanCallback());
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<ScanFilter> getScanFilters() {
|
|
|
|
List<ScanFilter> allFilters = new ArrayList<>();
|
|
|
|
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
|
|
|
allFilters.addAll(coordinator.createBLEScanFilters());
|
|
|
|
}
|
|
|
|
return allFilters;
|
2016-09-23 05:55:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
private ScanSettings getScanSettings() {
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
return new ScanSettings.Builder()
|
|
|
|
.setScanMode(SCAN_MODE_LOW_LATENCY)
|
|
|
|
.setMatchMode(MATCH_MODE_STICKY)
|
|
|
|
.build();
|
|
|
|
} else {
|
|
|
|
return new ScanSettings.Builder()
|
|
|
|
.setScanMode(SCAN_MODE_LOW_LATENCY)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void startBTLEDiscovery() {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.info("Starting BTLE Discovery");
|
2015-05-06 22:06:09 +02:00
|
|
|
handler.removeMessages(0, stopRunnable);
|
2015-05-10 23:14:32 +02:00
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
2015-05-05 00:48:02 +02:00
|
|
|
adapter.startLeScan(leScanCallback);
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void startBTDiscovery() {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.info("Starting BT Discovery");
|
2015-05-09 23:54:47 +02:00
|
|
|
handler.removeMessages(0, stopRunnable);
|
2015-05-10 23:14:32 +02:00
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
2015-05-09 23:54:47 +02:00
|
|
|
adapter.startDiscovery();
|
|
|
|
}
|
|
|
|
|
2016-05-26 14:58:36 +02:00
|
|
|
private void checkAndRequestLocationPermission() {
|
|
|
|
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-10 23:14:32 +02:00
|
|
|
private Message getPostMessage(Runnable runnable) {
|
|
|
|
Message m = Message.obtain(handler, runnable);
|
|
|
|
m.obj = runnable;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2015-05-05 00:48:02 +02:00
|
|
|
@Override
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
2015-08-03 23:09:49 +02:00
|
|
|
GBDeviceCandidate deviceCandidate = deviceCandidates.get(position);
|
2015-05-05 00:48:02 +02:00
|
|
|
if (deviceCandidate == null) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.error("Device candidate clicked, but item not found");
|
2015-05-05 00:48:02 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-10 21:35:31 +02:00
|
|
|
stopDiscovery();
|
2015-05-05 00:48:02 +02:00
|
|
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate);
|
2017-01-28 22:52:22 +01:00
|
|
|
LOG.info("Using device candidate " + deviceCandidate + " with coordinator: " + coordinator.getClass());
|
2015-05-10 16:27:31 +02:00
|
|
|
Class<? extends Activity> pairingActivity = coordinator.getPairingActivity();
|
|
|
|
if (pairingActivity != null) {
|
|
|
|
Intent intent = new Intent(this, pairingActivity);
|
2017-01-26 00:11:52 +01:00
|
|
|
intent.putExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE, deviceCandidate);
|
2015-05-10 16:27:31 +02:00
|
|
|
startActivity(intent);
|
2015-05-18 20:56:19 +02:00
|
|
|
} else {
|
2015-05-10 16:27:31 +02:00
|
|
|
try {
|
|
|
|
BluetoothDevice btDevice = adapter.getRemoteDevice(deviceCandidate.getMacAddress());
|
2017-03-04 16:03:36 +01:00
|
|
|
switch (btDevice.getBondState()) {
|
|
|
|
case BluetoothDevice.BOND_NONE: {
|
|
|
|
if (btDevice.createBond()) {
|
|
|
|
// async, wait for bonding event to finish this activity
|
|
|
|
bondingAddress = btDevice.getAddress();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BluetoothDevice.BOND_BONDING:
|
|
|
|
// async, wait for bonding event to finish this activity
|
|
|
|
bondingAddress = btDevice.getAddress();
|
|
|
|
break;
|
|
|
|
case BluetoothDevice.BOND_BONDED:
|
|
|
|
handleDeviceBonded();
|
|
|
|
break;
|
2015-05-10 21:32:41 +02:00
|
|
|
}
|
2015-05-10 16:27:31 +02:00
|
|
|
} catch (Exception e) {
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.error("Error pairing device: " + deviceCandidate.getMacAddress());
|
2015-05-10 16:27:31 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|