2020-01-09 10:44:32 +01:00
|
|
|
/* Copyright (C) 2015-2020 Andreas Shimokawa, boun, Carsten Pfeiffer, Daniel
|
2019-12-06 22:49:44 +01:00
|
|
|
Dakhno, Daniele Gobbetti, JohnnySun, jonnsoft, Lem Dulfo, Taavi Eomäe,
|
|
|
|
Uwe Hermann
|
2017-03-10 14:53:19 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
2021-05-30 00:00:48 +02:00
|
|
|
import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast;
|
|
|
|
|
2016-05-26 14:58:36 +02:00
|
|
|
import android.Manifest;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.app.Activity;
|
2021-12-23 22:41:50 +01:00
|
|
|
import android.app.AlertDialog;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.bluetooth.BluetoothAdapter;
|
|
|
|
import android.bluetooth.BluetoothDevice;
|
|
|
|
import android.bluetooth.BluetoothManager;
|
2018-03-30 15:04:53 +02:00
|
|
|
import android.bluetooth.le.BluetoothLeScanner;
|
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;
|
2021-12-23 18:28:02 +01:00
|
|
|
import android.content.ClipData;
|
|
|
|
import android.content.ClipboardManager;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.content.Context;
|
2021-12-23 22:41:50 +01:00
|
|
|
import android.content.DialogInterface;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2019-10-12 19:44:45 +02:00
|
|
|
import android.content.SharedPreferences;
|
2016-05-26 14:58:36 +02:00
|
|
|
import android.content.pm.PackageManager;
|
2020-07-19 23:58:21 +02:00
|
|
|
import android.location.LocationManager;
|
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;
|
2020-07-19 23:58:21 +02:00
|
|
|
import android.provider.Settings;
|
2021-12-23 18:28:02 +01:00
|
|
|
import android.text.TextUtils;
|
2021-12-23 22:41:50 +01:00
|
|
|
import android.util.Pair;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.view.View;
|
|
|
|
import android.widget.AdapterView;
|
|
|
|
import android.widget.Button;
|
2021-12-23 22:41:50 +01:00
|
|
|
import android.widget.LinearLayout;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.widget.ListView;
|
|
|
|
import android.widget.ProgressBar;
|
2021-12-23 22:41:50 +01:00
|
|
|
import android.widget.Spinner;
|
2015-05-05 00:48:02 +02:00
|
|
|
import android.widget.Toast;
|
|
|
|
|
2019-08-03 23:40:07 +02:00
|
|
|
import androidx.annotation.NonNull;
|
2020-07-19 23:58:21 +02:00
|
|
|
import androidx.annotation.RequiresApi;
|
2019-04-21 21:18:08 +02:00
|
|
|
import androidx.core.app.ActivityCompat;
|
|
|
|
|
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;
|
2021-12-23 22:41:50 +01:00
|
|
|
import java.util.LinkedHashMap;
|
2016-11-27 01:09:20 +01:00
|
|
|
import java.util.List;
|
2020-08-28 15:38:18 +02:00
|
|
|
import java.util.Locale;
|
2021-12-23 22:41:50 +01:00
|
|
|
import java.util.Map;
|
2018-03-30 15:04:53 +02:00
|
|
|
import java.util.Objects;
|
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;
|
2019-04-21 21:18:08 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity;
|
2015-09-24 14:45:21 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter;
|
2021-12-23 22:41:50 +01:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.SpinnerWithIconAdapter;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.SpinnerWithIconItem;
|
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;
|
2020-08-28 15:38:18 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
2015-08-03 23:09:49 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
2020-07-19 23:58:21 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
|
|
|
|
2016-09-23 05:55:34 +02:00
|
|
|
|
2020-08-28 15:38:18 +02:00
|
|
|
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, BondingInterface {
|
2015-05-12 06:28:11 +02:00
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
2020-07-19 23:58:21 +02:00
|
|
|
private static final long SCAN_DURATION = 30000; // 30s
|
2020-08-01 21:20:53 +02:00
|
|
|
private final Handler handler = new Handler();
|
|
|
|
private final ArrayList<GBDeviceCandidate> deviceCandidates = new ArrayList<>();
|
2020-07-19 23:58:21 +02:00
|
|
|
private ScanCallback newBLEScanCallback = null;
|
2020-08-01 21:20:53 +02:00
|
|
|
/**
|
|
|
|
* Use old BLE scanning
|
|
|
|
**/
|
2020-07-19 23:58:21 +02:00
|
|
|
private boolean oldBleScanning = false;
|
2020-08-01 21:20:53 +02:00
|
|
|
/**
|
|
|
|
* If already bonded devices are to be ignored when scanning
|
|
|
|
*/
|
2020-07-19 23:58:21 +02:00
|
|
|
private boolean ignoreBonded = true;
|
2021-12-23 18:28:02 +01:00
|
|
|
private boolean discoverUnsupported = false;
|
2020-07-19 23:58:21 +02:00
|
|
|
private ProgressBar bluetoothProgress;
|
|
|
|
private ProgressBar bluetoothLEProgress;
|
2020-08-01 21:20:53 +02:00
|
|
|
private DeviceCandidateAdapter deviceCandidateAdapter;
|
2021-05-30 00:00:48 +02:00
|
|
|
private GBDeviceCandidate deviceTarget;
|
2020-08-01 21:20:53 +02:00
|
|
|
private final BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
|
|
|
|
@Override
|
|
|
|
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
|
|
|
//logMessageContent(scanRecord);
|
|
|
|
handleDeviceFound(device, (short) rssi);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
private BluetoothAdapter adapter;
|
|
|
|
private Button startButton;
|
|
|
|
private Scanning isScanning = Scanning.SCANNING_OFF;
|
2021-12-23 22:41:50 +01:00
|
|
|
private long selectedUnsupportedDeviceKey = DebugActivity.SELECT_DEVICE;
|
2020-07-19 23:58:21 +02:00
|
|
|
private final Runnable stopRunnable = new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (isScanning == Scanning.SCANNING_BT_NEXT_BLE) {
|
|
|
|
// Start the next scan in the series
|
|
|
|
stopDiscovery();
|
|
|
|
startDiscovery(Scanning.SCANNING_BLE);
|
|
|
|
} else {
|
|
|
|
stopDiscovery();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
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) {
|
2018-03-30 15:04:53 +02:00
|
|
|
switch (Objects.requireNonNull(intent.getAction())) {
|
2020-08-02 22:43:00 +02:00
|
|
|
case BluetoothAdapter.ACTION_DISCOVERY_STARTED: {
|
|
|
|
LOG.debug("ACTION_DISCOVERY_STARTED");
|
2020-07-19 23:58:21 +02:00
|
|
|
if (isScanning != Scanning.SCANNING_BLE) {
|
|
|
|
if (isScanning != Scanning.SCANNING_BT_NEXT_BLE) {
|
2020-08-02 03:56:40 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_BT);
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
2016-10-03 22:31:33 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
break;
|
2020-08-02 22:43:00 +02:00
|
|
|
}
|
|
|
|
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED: {
|
|
|
|
LOG.debug("ACTION_DISCOVERY_FINISHED");
|
2016-10-28 23:48:13 +02:00
|
|
|
handler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-08-28 15:38:18 +02:00
|
|
|
// Continue with LE scan, if available
|
2020-07-19 23:58:21 +02:00
|
|
|
if (isScanning == Scanning.SCANNING_BT || isScanning == Scanning.SCANNING_BT_NEXT_BLE) {
|
2016-10-28 23:48:13 +02:00
|
|
|
checkAndRequestLocationPermission();
|
2020-07-19 23:58:21 +02:00
|
|
|
stopDiscovery();
|
|
|
|
startDiscovery(Scanning.SCANNING_BLE);
|
2016-10-28 23:48:13 +02:00
|
|
|
}
|
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;
|
2020-08-02 22:43:00 +02:00
|
|
|
}
|
|
|
|
case BluetoothAdapter.ACTION_STATE_CHANGED: {
|
|
|
|
LOG.debug("ACTION_STATE_CHANGED ");
|
2020-08-28 15:38:18 +02:00
|
|
|
bluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF));
|
2015-05-05 00:48:02 +02:00
|
|
|
break;
|
2020-08-02 22:43:00 +02:00
|
|
|
}
|
2015-05-10 21:32:41 +02:00
|
|
|
case BluetoothDevice.ACTION_FOUND: {
|
2020-08-02 22:43:00 +02:00
|
|
|
LOG.debug("ACTION_FOUND");
|
2015-05-09 23:54:47 +02:00
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
2020-08-28 15:38:18 +02:00
|
|
|
handleDeviceFound(device, intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN));
|
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: {
|
2020-08-02 22:43:00 +02:00
|
|
|
LOG.debug("ACTION_UUID");
|
2016-11-27 02:41:52 +01:00
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
|
|
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
|
|
|
|
Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
|
2018-03-30 15:38:29 +02:00
|
|
|
ParcelUuid[] uuids2 = AndroidUtils.toParcelUuids(uuids);
|
2016-11-27 02:41:52 +01:00
|
|
|
handleDeviceFound(device, rssi, uuids2);
|
|
|
|
break;
|
|
|
|
}
|
2015-05-10 21:32:41 +02:00
|
|
|
case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
|
2020-08-02 22:43:00 +02:00
|
|
|
LOG.debug("ACTION_BOND_STATE_CHANGED");
|
2015-05-10 21:32:41 +02:00
|
|
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
2020-08-28 15:38:18 +02:00
|
|
|
if (device != null) {
|
2015-05-10 21:32:41 +02:00
|
|
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
2020-08-28 15:38:18 +02:00
|
|
|
LOG.debug(String.format(Locale.ENGLISH, "Bond state: %d", bondState));
|
|
|
|
|
2015-05-10 21:32:41 +02:00
|
|
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
2020-08-28 15:38:18 +02:00
|
|
|
BondingUtil.handleDeviceBonded((BondingInterface) context, getCandidateFromMAC(device));
|
2015-05-10 21:32:41 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-28 15:38:18 +02:00
|
|
|
break;
|
2015-05-10 21:32:41 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2016-09-23 05:55:34 +02:00
|
|
|
|
2016-08-14 23:21:09 +02:00
|
|
|
public void logMessageContent(byte[] value) {
|
|
|
|
if (value != null) {
|
2018-07-22 14:52:17 +02:00
|
|
|
LOG.warn("DATA: " + GB.hexdump(value, 0, value.length));
|
2016-08-14 23:21:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
|
|
@Override
|
|
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
|
|
super.onActivityResult(requestCode, resultCode, data);
|
2020-08-28 15:38:18 +02:00
|
|
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
|
2020-08-28 15:38:18 +02:00
|
|
|
private GBDeviceCandidate getCandidateFromMAC(BluetoothDevice device) {
|
|
|
|
for (GBDeviceCandidate candidate : deviceCandidates) {
|
|
|
|
if (candidate.getMacAddress().equals(device.getAddress())) {
|
|
|
|
return candidate;
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
2015-05-06 22:06:09 +02:00
|
|
|
}
|
2020-08-28 15:38:18 +02:00
|
|
|
LOG.warn(String.format("This shouldn't happen unless the list somehow emptied itself, device MAC: %1$s", device.getAddress()));
|
|
|
|
return null;
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
private ScanCallback getScanCallback() {
|
|
|
|
if (newBLEScanCallback != null) {
|
|
|
|
return newBLEScanCallback;
|
|
|
|
}
|
2020-08-01 02:06:13 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
newBLEScanCallback = new ScanCallback() {
|
2020-07-29 13:32:00 +02:00
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
2020-07-19 23:58:21 +02:00
|
|
|
@Override
|
|
|
|
public void onScanResult(int callbackType, ScanResult result) {
|
|
|
|
super.onScanResult(callbackType, result);
|
|
|
|
try {
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG.warn(result.getDevice().getName() + ": " +
|
|
|
|
((scanRecord != null) ? scanRecord.getBytes().length : -1));
|
|
|
|
handleDeviceFound(result.getDevice(), (short) result.getRssi(), uuids);
|
|
|
|
} catch (NullPointerException e) {
|
|
|
|
LOG.warn("Error handling scan result", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2020-08-01 02:06:13 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
return newBLEScanCallback;
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
2020-07-29 13:32:00 +02:00
|
|
|
|
2015-05-05 00:48:02 +02:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
2021-12-23 18:56:36 +01:00
|
|
|
loadSettingsValues();
|
2015-05-05 00:48:02 +02:00
|
|
|
setContentView(R.layout.activity_discovery);
|
2018-03-30 15:04:53 +02:00
|
|
|
startButton = findViewById(R.id.discovery_start);
|
2015-05-05 00:48:02 +02:00
|
|
|
startButton.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
onStartButtonClick(startButton);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-12-23 18:56:36 +01:00
|
|
|
Button settingsButton = findViewById(R.id.discovery_preferences);
|
|
|
|
settingsButton.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
Intent enableIntent = new Intent(DiscoveryActivity.this, DiscoveryPairingPreferenceActivity.class);
|
|
|
|
startActivity(enableIntent);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
bluetoothProgress = findViewById(R.id.discovery_progressbar);
|
|
|
|
bluetoothProgress.setProgress(0);
|
|
|
|
bluetoothProgress.setIndeterminate(true);
|
|
|
|
bluetoothProgress.setVisibility(View.GONE);
|
|
|
|
ListView deviceCandidatesView = findViewById(R.id.discovery_device_candidates_list);
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
bluetoothLEProgress = findViewById(R.id.discovery_ble_progressbar);
|
|
|
|
bluetoothLEProgress.setProgress(0);
|
|
|
|
bluetoothLEProgress.setIndeterminate(true);
|
|
|
|
bluetoothLEProgress.setVisibility(View.GONE);
|
|
|
|
|
|
|
|
deviceCandidateAdapter = new DeviceCandidateAdapter(this, deviceCandidates);
|
|
|
|
deviceCandidatesView.setAdapter(deviceCandidateAdapter);
|
2015-05-05 00:48:02 +02:00
|
|
|
deviceCandidatesView.setOnItemClickListener(this);
|
2019-04-21 21:18:08 +02:00
|
|
|
deviceCandidatesView.setOnItemLongClickListener(this);
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2020-12-24 13:23:25 +01:00
|
|
|
registerBroadcastReceivers();
|
2015-05-05 00:48:02 +02:00
|
|
|
|
2020-08-01 21:20:53 +02:00
|
|
|
checkAndRequestLocationPermission();
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
startDiscovery(Scanning.SCANNING_BT_NEXT_BLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onStartButtonClick(View button) {
|
2020-08-01 21:20:53 +02:00
|
|
|
LOG.debug("Start button clicked");
|
2020-07-19 23:58:21 +02:00
|
|
|
if (isScanning()) {
|
|
|
|
stopDiscovery();
|
2020-01-31 06:01:04 +01:00
|
|
|
} else {
|
2021-12-23 18:56:36 +01:00
|
|
|
deviceCandidates.clear();
|
|
|
|
deviceCandidateAdapter.notifyDataSetChanged();
|
2020-07-19 23:58:21 +02:00
|
|
|
if (GB.supportsBluetoothLE()) {
|
|
|
|
startDiscovery(Scanning.SCANNING_BT_NEXT_BLE);
|
|
|
|
} else {
|
|
|
|
startDiscovery(Scanning.SCANNING_BT);
|
|
|
|
}
|
2020-01-31 06:01:04 +01:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2019-08-03 23:40:07 +02:00
|
|
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
2015-05-05 00:48:02 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2020-08-28 15:38:18 +02:00
|
|
|
unregisterBroadcastReceivers();
|
|
|
|
stopAllDiscovery();
|
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onStop() {
|
|
|
|
unregisterBroadcastReceivers();
|
|
|
|
stopAllDiscovery();
|
|
|
|
super.onStop();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPause() {
|
|
|
|
unregisterBroadcastReceivers();
|
|
|
|
stopAllDiscovery();
|
|
|
|
super.onPause();
|
|
|
|
}
|
|
|
|
|
Add BlueTooth receivers in onResume
Pairing some devices requires a PIN to be entered. E.g. the flow is: confirm you'd like to pair, pass off to Android which prompts for a PIN to pair with (typically shows up on the non-phone device's screen or is hard coded in the manual), then return back to the app.
When this pairing request screen pops up, it takes focus. This invokes onPause and onStop in the DiscoveryActivity, which removes the BlueTooth broadcast receivers. Returning focus (after successful pairing), there's nothing to listen to the event. Practically speaking, GadgetBridge last saw the device as BOND_BONDING, so it's out of sync. If queried, the device is BOND_BONDED, so it's good to move on to the next step.
Many existing pairing activities (Lenovo, MiBand, Pebble, Watch9) have an implementation of onResume() to presumably tackle this issue, this adds it to `DiscoveryActivity` as well. Testing with some other supporting code, this allows a Garmin vivosport to appear in Gadgetbridge.
2020-12-24 06:46:08 +01:00
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
2021-12-23 18:56:36 +01:00
|
|
|
loadSettingsValues();
|
2020-12-24 13:23:25 +01:00
|
|
|
registerBroadcastReceivers();
|
Add BlueTooth receivers in onResume
Pairing some devices requires a PIN to be entered. E.g. the flow is: confirm you'd like to pair, pass off to Android which prompts for a PIN to pair with (typically shows up on the non-phone device's screen or is hard coded in the manual), then return back to the app.
When this pairing request screen pops up, it takes focus. This invokes onPause and onStop in the DiscoveryActivity, which removes the BlueTooth broadcast receivers. Returning focus (after successful pairing), there's nothing to listen to the event. Practically speaking, GadgetBridge last saw the device as BOND_BONDING, so it's out of sync. If queried, the device is BOND_BONDED, so it's good to move on to the next step.
Many existing pairing activities (Lenovo, MiBand, Pebble, Watch9) have an implementation of onResume() to presumably tackle this issue, this adds it to `DiscoveryActivity` as well. Testing with some other supporting code, this allows a Garmin vivosport to appear in Gadgetbridge.
2020-12-24 06:46:08 +01:00
|
|
|
super.onResume();
|
|
|
|
}
|
|
|
|
|
2020-08-28 15:38:18 +02:00
|
|
|
private void stopAllDiscovery() {
|
2016-10-03 22:31:33 +02:00
|
|
|
try {
|
2020-08-28 15:38:18 +02:00
|
|
|
stopBTDiscovery();
|
|
|
|
if (oldBleScanning) {
|
|
|
|
stopOldBLEDiscovery();
|
|
|
|
} else {
|
|
|
|
if (GBApplication.isRunningLollipopOrLater()) {
|
|
|
|
stopBLEDiscovery();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
LOG.warn("Error stopping discovery", e);
|
2016-10-03 22:31:33 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
|
2015-05-09 23:54:47 +02:00
|
|
|
private void handleDeviceFound(BluetoothDevice device, short rssi) {
|
2019-10-12 20:15:09 +02:00
|
|
|
if (device.getName() != null) {
|
2020-07-19 23:58:21 +02:00
|
|
|
if (handleDeviceFound(device, rssi, null)) {
|
2019-10-12 20:15:09 +02:00
|
|
|
LOG.info("found supported device " + device.getName() + " without scanning services, skipping service scan.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-11-27 02:41:52 +01:00
|
|
|
ParcelUuid[] uuids = device.getUuids();
|
|
|
|
if (uuids == null) {
|
|
|
|
if (device.fetchUuidsWithSdp()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDeviceFound(device, rssi, uuids);
|
|
|
|
}
|
|
|
|
|
2019-10-12 20:15:09 +02:00
|
|
|
private boolean 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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-01 02:06:13 +02:00
|
|
|
|
|
|
|
if (device.getBondState() == BluetoothDevice.BOND_BONDED && ignoreBonded) {
|
|
|
|
return true; // Ignore already bonded devices
|
2016-07-05 20:46:47 +02:00
|
|
|
}
|
|
|
|
|
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);
|
2021-12-23 18:28:02 +01:00
|
|
|
if (deviceType.isSupported() || discoverUnsupported) {
|
2016-11-27 01:09:20 +01:00
|
|
|
candidate.setDeviceType(deviceType);
|
2021-12-23 18:28:02 +01:00
|
|
|
LOG.info("Recognized 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);
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
deviceCandidateAdapter.notifyDataSetChanged();
|
2019-10-12 20:15:09 +02:00
|
|
|
return true;
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
2019-10-12 20:15:09 +02:00
|
|
|
return false;
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
private void startDiscovery(Scanning what) {
|
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;
|
|
|
|
}
|
|
|
|
|
2015-05-12 06:28:11 +02:00
|
|
|
LOG.info("Starting discovery: " + what);
|
2020-07-19 23:58:21 +02:00
|
|
|
startButton.setText(getString(R.string.discovery_stop_scanning));
|
|
|
|
if (ensureBluetoothReady() && isScanning == Scanning.SCANNING_OFF) {
|
|
|
|
if (what == Scanning.SCANNING_BT || what == Scanning.SCANNING_BT_NEXT_BLE) {
|
|
|
|
startBTDiscovery(what);
|
|
|
|
} else if (what == Scanning.SCANNING_BLE && GB.supportsBluetoothLE()) {
|
2020-07-29 13:32:00 +02:00
|
|
|
if (oldBleScanning || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
startOldBTLEDiscovery();
|
2015-05-10 00:18:42 +02:00
|
|
|
} else {
|
2020-07-29 13:32:00 +02:00
|
|
|
startBTLEDiscovery();
|
2016-09-23 05:55:34 +02:00
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.discovery_enable_bluetooth), Toast.LENGTH_SHORT, GB.ERROR);
|
2015-05-10 00:18:42 +02:00
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
2020-07-19 23:58:21 +02:00
|
|
|
toast(DiscoveryActivity.this, getString(R.string.discovery_enable_bluetooth), Toast.LENGTH_SHORT, GB.ERROR);
|
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;
|
2020-07-19 23:58:21 +02:00
|
|
|
if (wasScanning == Scanning.SCANNING_BT || wasScanning == Scanning.SCANNING_BT_NEXT_BLE) {
|
2015-05-09 23:54:47 +02:00
|
|
|
stopBTDiscovery();
|
2020-07-19 23:58:21 +02:00
|
|
|
} else if (wasScanning == Scanning.SCANNING_BLE) {
|
2020-07-29 13:32:00 +02:00
|
|
|
if (oldBleScanning || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
2020-07-19 23:58:21 +02:00
|
|
|
stopOldBLEDiscovery();
|
|
|
|
} else {
|
|
|
|
stopBLEDiscovery();
|
|
|
|
}
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
discoveryFinished();
|
2015-05-06 22:06:09 +02:00
|
|
|
handler.removeMessages(0, stopRunnable);
|
2020-08-02 03:56:40 +02:00
|
|
|
} else {
|
|
|
|
discoveryFinished();
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
private boolean isScanning() {
|
|
|
|
return isScanning != Scanning.SCANNING_OFF;
|
|
|
|
}
|
|
|
|
|
2020-07-29 13:32:00 +02:00
|
|
|
private void startOldBTLEDiscovery() {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.info("Starting old BLE discovery");
|
|
|
|
|
|
|
|
handler.removeMessages(0, stopRunnable);
|
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
2020-08-28 15:38:18 +02:00
|
|
|
if (adapter.startLeScan(leScanCallback)) {
|
2020-08-02 20:55:16 +02:00
|
|
|
LOG.info("Old Bluetooth LE scan started successfully");
|
|
|
|
bluetoothLEProgress.setVisibility(View.VISIBLE);
|
2020-08-02 21:06:18 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_BLE);
|
2020-08-02 20:55:16 +02:00
|
|
|
} else {
|
|
|
|
LOG.info("Old Bluetooth LE scan starting failed");
|
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void stopOldBLEDiscovery() {
|
|
|
|
if (adapter != null) {
|
2018-07-12 18:25:18 +02:00
|
|
|
adapter.stopLeScan(leScanCallback);
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.info("Stopped old BLE discovery");
|
|
|
|
}
|
2020-08-02 03:56:40 +02:00
|
|
|
|
2020-08-02 20:55:16 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
2020-07-29 13:32:00 +02:00
|
|
|
/* New BTLE Discovery uses startScan (List<ScanFilter> filters,
|
|
|
|
ScanSettings settings,
|
|
|
|
ScanCallback callback) */
|
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
|
|
private void startBTLEDiscovery() {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.info("Starting BLE discovery");
|
|
|
|
|
|
|
|
handler.removeMessages(0, stopRunnable);
|
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
|
|
|
|
|
|
|
// Filters being non-null would be a very good idea with background scan, but in this case,
|
|
|
|
// not really required.
|
|
|
|
adapter.getBluetoothLeScanner().startScan(null, getScanSettings(), getScanCallback());
|
|
|
|
|
2020-08-02 20:55:16 +02:00
|
|
|
LOG.debug("Bluetooth LE discovery started successfully");
|
2020-07-19 23:58:21 +02:00
|
|
|
bluetoothLEProgress.setVisibility(View.VISIBLE);
|
2020-08-02 21:06:18 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_BLE);
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
2020-07-29 13:32:00 +02:00
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
2020-07-19 23:58:21 +02:00
|
|
|
private void stopBLEDiscovery() {
|
|
|
|
if (adapter == null) {
|
2018-07-12 18:25:18 +02:00
|
|
|
return;
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
2018-07-12 18:25:18 +02:00
|
|
|
|
2018-03-30 15:04:53 +02:00
|
|
|
BluetoothLeScanner bluetoothLeScanner = adapter.getBluetoothLeScanner();
|
|
|
|
if (bluetoothLeScanner == null) {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.warn("Could not get BluetoothLeScanner()!");
|
2018-03-30 15:04:53 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
if (newBLEScanCallback == null) {
|
2018-07-06 15:15:14 +02:00
|
|
|
LOG.warn("newLeScanCallback == null!");
|
|
|
|
return;
|
|
|
|
}
|
2020-06-15 15:34:17 +02:00
|
|
|
try {
|
2020-07-19 23:58:21 +02:00
|
|
|
bluetoothLeScanner.stopScan(newBLEScanCallback);
|
2020-06-15 15:34:17 +02:00
|
|
|
} catch (NullPointerException e) {
|
|
|
|
LOG.warn("Internal NullPointerException when stopping the scan!");
|
|
|
|
return;
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
LOG.debug("Stopped BLE discovery");
|
2020-08-02 21:06:18 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
2016-09-23 05:55:34 +02:00
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
/**
|
|
|
|
* Starts a regular Bluetooth scan
|
|
|
|
*
|
|
|
|
* @param what The scan type, only either SCANNING_BT or SCANNING_BT_NEXT_BLE!
|
|
|
|
*/
|
|
|
|
private void startBTDiscovery(Scanning what) {
|
|
|
|
LOG.info("Starting BT discovery");
|
2020-08-02 22:59:42 +02:00
|
|
|
try {
|
|
|
|
// LineageOS quirk, can't start scan properly,
|
|
|
|
// if scan has been started by something else
|
|
|
|
stopBTDiscovery();
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
handler.removeMessages(0, stopRunnable);
|
|
|
|
handler.sendMessageDelayed(getPostMessage(stopRunnable), SCAN_DURATION);
|
|
|
|
if (adapter.startDiscovery()) {
|
2020-08-02 20:55:16 +02:00
|
|
|
LOG.debug("Discovery started successfully");
|
2020-08-02 20:23:14 +02:00
|
|
|
bluetoothProgress.setVisibility(View.VISIBLE);
|
|
|
|
setIsScanning(what);
|
|
|
|
} else {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.error("Discovery starting failed");
|
2020-08-02 20:23:14 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void stopBTDiscovery() {
|
|
|
|
if (adapter != null) {
|
|
|
|
adapter.cancelDiscovery();
|
|
|
|
LOG.info("Stopped BT discovery");
|
2016-03-26 20:45:07 +01:00
|
|
|
}
|
2020-08-02 21:06:18 +02:00
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void discoveryFinished() {
|
2020-07-19 23:58:21 +02:00
|
|
|
if (isScanning != Scanning.SCANNING_OFF) {
|
2020-07-29 13:32:00 +02:00
|
|
|
LOG.warn("Scan was not properly stopped: " + isScanning);
|
2020-07-19 23:58:21 +02:00
|
|
|
}
|
2020-08-02 03:56:40 +02:00
|
|
|
|
|
|
|
setIsScanning(Scanning.SCANNING_OFF);
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
2020-08-02 03:56:40 +02:00
|
|
|
private void setIsScanning(Scanning to) {
|
|
|
|
this.isScanning = to;
|
|
|
|
|
|
|
|
if (isScanning == Scanning.SCANNING_OFF) {
|
|
|
|
startButton.setText(getString(R.string.discovery_start_scanning));
|
2020-08-02 21:06:18 +02:00
|
|
|
bluetoothProgress.setVisibility(View.GONE);
|
|
|
|
bluetoothLEProgress.setVisibility(View.GONE);
|
2020-08-02 03:56:40 +02:00
|
|
|
} else {
|
|
|
|
startButton.setText(getString(R.string.discovery_stop_scanning));
|
|
|
|
}
|
|
|
|
}
|
2015-05-09 23:54:47 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
private void bluetoothStateChanged(int newState) {
|
|
|
|
if (newState == BluetoothAdapter.STATE_ON) {
|
|
|
|
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
|
|
|
startButton.setEnabled(true);
|
|
|
|
} else {
|
|
|
|
this.adapter = null;
|
|
|
|
startButton.setEnabled(false);
|
2020-08-02 20:55:16 +02:00
|
|
|
bluetoothProgress.setVisibility(View.GONE);
|
|
|
|
bluetoothLEProgress.setVisibility(View.GONE);
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
2020-08-02 03:56:40 +02:00
|
|
|
|
|
|
|
discoveryFinished();
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean checkBluetoothAvailable() {
|
|
|
|
BluetoothManager bluetoothService = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
|
|
|
|
if (bluetoothService == null) {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.warn("No bluetooth service 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) {
|
2020-07-19 23:58:21 +02:00
|
|
|
LOG.warn("No bluetooth adapter available");
|
2016-04-09 03:12:40 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +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;
|
2016-11-27 01:09:20 +01:00
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
return false;
|
2016-09-23 05:55:34 +02:00
|
|
|
}
|
|
|
|
|
2020-07-29 13:32:00 +02:00
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
2016-09-23 05:55:34 +02:00
|
|
|
private ScanSettings getScanSettings() {
|
2020-07-19 23:58:21 +02:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
return new ScanSettings.Builder()
|
|
|
|
.setCallbackType(android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
|
|
|
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
|
|
|
|
.setMatchMode(android.bluetooth.le.ScanSettings.MATCH_MODE_AGGRESSIVE)
|
|
|
|
.setPhy(android.bluetooth.le.ScanSettings.PHY_LE_ALL_SUPPORTED)
|
|
|
|
.setNumOfMatches(android.bluetooth.le.ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
|
|
|
|
.build();
|
|
|
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
2016-09-23 05:55:34 +02:00
|
|
|
return new ScanSettings.Builder()
|
2020-07-19 23:58:21 +02:00
|
|
|
.setCallbackType(android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
2018-03-30 15:04:53 +02:00
|
|
|
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
|
2020-07-19 23:58:21 +02:00
|
|
|
.setMatchMode(android.bluetooth.le.ScanSettings.MATCH_MODE_AGGRESSIVE)
|
|
|
|
.setNumOfMatches(android.bluetooth.le.ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
|
2016-09-23 05:55:34 +02:00
|
|
|
.build();
|
|
|
|
} else {
|
|
|
|
return new ScanSettings.Builder()
|
2018-03-30 15:04:53 +02:00
|
|
|
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
|
2016-09-23 05:55:34 +02:00
|
|
|
.build();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
private List<ScanFilter> getScanFilters() {
|
|
|
|
List<ScanFilter> allFilters = new ArrayList<>();
|
|
|
|
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
|
|
|
allFilters.addAll(coordinator.createBLEScanFilters());
|
|
|
|
}
|
|
|
|
return allFilters;
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
private Message getPostMessage(Runnable runnable) {
|
|
|
|
Message message = Message.obtain(handler, runnable);
|
|
|
|
message.obj = runnable;
|
|
|
|
return message;
|
2015-05-09 23:54:47 +02:00
|
|
|
}
|
|
|
|
|
2016-05-26 14:58:36 +02:00
|
|
|
private void checkAndRequestLocationPermission() {
|
|
|
|
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
2020-08-01 21:20:53 +02:00
|
|
|
LOG.error("No permission to access coarse location!");
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.error_no_location_access), Toast.LENGTH_SHORT, GB.ERROR);
|
2016-05-26 14:58:36 +02:00
|
|
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 0);
|
|
|
|
}
|
2020-08-01 21:20:53 +02:00
|
|
|
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
LOG.error("No permission to access fine location!");
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.error_no_location_access), Toast.LENGTH_SHORT, GB.ERROR);
|
|
|
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 0);
|
|
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
|
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
LOG.error("No permission to access background location!");
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.error_no_location_access), Toast.LENGTH_SHORT, GB.ERROR);
|
|
|
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LocationManager locationManager = (LocationManager) DiscoveryActivity.this.getSystemService(Context.LOCATION_SERVICE);
|
|
|
|
try {
|
|
|
|
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
|
|
|
|
// Do nothing
|
|
|
|
LOG.debug("Some location provider is enabled, assuming location is enabled");
|
|
|
|
} else {
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.require_location_provider), Toast.LENGTH_LONG, GB.ERROR);
|
|
|
|
DiscoveryActivity.this.startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
|
|
|
// We can't be sure location was enabled, cancel scan start and wait for new user action
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.error_location_enabled_mandatory), Toast.LENGTH_SHORT, GB.ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LOG.error("Exception when checking location status: ", ex);
|
|
|
|
}
|
2020-08-28 15:38:18 +02:00
|
|
|
LOG.error("Problem with permissions, returning");
|
2016-05-26 14:58:36 +02:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2021-12-23 18:28:02 +01:00
|
|
|
if (!deviceCandidate.getDeviceType().isSupported()){
|
|
|
|
LOG.error("Unsupported device candidate");
|
|
|
|
ArrayList deviceDetails = new ArrayList<>();
|
|
|
|
deviceDetails.add(deviceCandidate.getName());
|
|
|
|
deviceDetails.add(deviceCandidate.getMacAddress());
|
|
|
|
try {
|
|
|
|
for (ParcelUuid uuid : deviceCandidate.getServiceUuids()) {
|
|
|
|
deviceDetails.add(uuid.getUuid().toString());
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
LOG.error("Error collecting device uuids: " + e);
|
|
|
|
}
|
|
|
|
String clipboardData = TextUtils.join(", ", deviceDetails);
|
|
|
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
|
|
|
ClipData clip = ClipData.newPlainText(deviceCandidate.getName(), clipboardData);
|
|
|
|
clipboard.setPrimaryClip(clip);
|
|
|
|
toast(this, "Device details copied to clipboard", Toast.LENGTH_SHORT, GB.INFO);
|
|
|
|
return;
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
|
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());
|
2019-10-12 19:44:45 +02:00
|
|
|
|
|
|
|
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_REQUIRE_KEY) {
|
|
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceCandidate.getMacAddress());
|
|
|
|
|
|
|
|
String authKey = sharedPrefs.getString("authkey", null);
|
2020-12-20 00:25:44 +01:00
|
|
|
if (authKey == null || authKey.isEmpty() ) {
|
2020-07-19 23:58:21 +02:00
|
|
|
toast(DiscoveryActivity.this, getString(R.string.discovery_need_to_enter_authkey), Toast.LENGTH_LONG, GB.WARN);
|
2019-10-12 19:44:45 +02:00
|
|
|
return;
|
2020-12-20 00:25:44 +01:00
|
|
|
} else if (authKey.getBytes().length < 34 || !authKey.startsWith("0x")) {
|
|
|
|
toast(DiscoveryActivity.this, getString(R.string.discovery_entered_invalid_authkey), Toast.LENGTH_LONG, GB.WARN);
|
|
|
|
return;
|
2019-10-12 19:44:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2021-09-23 07:09:15 +02:00
|
|
|
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_NONE ||
|
|
|
|
coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_LAZY) {
|
2017-04-06 23:47:35 +02:00
|
|
|
LOG.info("No bonding needed, according to coordinator, so connecting right away");
|
2020-08-28 15:38:18 +02:00
|
|
|
BondingUtil.connectThenComplete(this, deviceCandidate);
|
2017-04-06 23:47:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-10 16:27:31 +02:00
|
|
|
try {
|
2021-05-30 00:00:48 +02:00
|
|
|
this.deviceTarget = deviceCandidate;
|
2020-08-28 15:38:18 +02:00
|
|
|
BondingUtil.initiateCorrectBonding(this, deviceCandidate);
|
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
|
|
|
}
|
2018-06-18 20:26:28 +02:00
|
|
|
|
2020-07-19 23:58:21 +02:00
|
|
|
@Override
|
|
|
|
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
|
2021-12-23 22:41:50 +01:00
|
|
|
stopDiscovery();
|
|
|
|
final GBDeviceCandidate deviceCandidate = deviceCandidates.get(position);
|
2020-07-19 23:58:21 +02:00
|
|
|
if (deviceCandidate == null) {
|
|
|
|
LOG.error("Device candidate clicked, but item not found");
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-23 18:28:02 +01:00
|
|
|
if (!deviceCandidate.getDeviceType().isSupported()) {
|
|
|
|
LOG.error("Unsupported device candidate");
|
2021-12-23 22:41:50 +01:00
|
|
|
LinkedHashMap<String, Pair<Long, Integer>> allDevices;
|
|
|
|
allDevices = DebugActivity.getAllSupportedDevices(getApplicationContext());
|
|
|
|
|
|
|
|
final LinearLayout linearLayout = new LinearLayout(DiscoveryActivity.this);
|
|
|
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
|
|
|
|
final LinearLayout macLayout = new LinearLayout(DiscoveryActivity.this);
|
|
|
|
macLayout.setOrientation(LinearLayout.HORIZONTAL);
|
|
|
|
macLayout.setPadding(20, 0, 20, 0);
|
|
|
|
|
|
|
|
final Spinner deviceListSpinner = new Spinner(DiscoveryActivity.this);
|
|
|
|
ArrayList<SpinnerWithIconItem> deviceListArray = new ArrayList<>();
|
|
|
|
for (Map.Entry<String, Pair<Long, Integer>> item : allDevices.entrySet()) {
|
|
|
|
deviceListArray.add(new SpinnerWithIconItem(item.getKey(), item.getValue().first, item.getValue().second));
|
|
|
|
}
|
|
|
|
final SpinnerWithIconAdapter deviceListAdapter = new SpinnerWithIconAdapter(DiscoveryActivity.this,
|
|
|
|
R.layout.spinner_with_image_layout, R.id.spinner_item_text, deviceListArray);
|
|
|
|
deviceListSpinner.setAdapter(deviceListAdapter);
|
|
|
|
addListenerOnSpinnerDeviceSelection(deviceListSpinner);
|
|
|
|
|
|
|
|
linearLayout.addView(deviceListSpinner);
|
|
|
|
linearLayout.addView(macLayout);
|
|
|
|
|
|
|
|
new AlertDialog.Builder(DiscoveryActivity.this)
|
|
|
|
.setCancelable(true)
|
|
|
|
.setTitle(R.string.add_test_device)
|
|
|
|
.setView(linearLayout)
|
|
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
if (selectedUnsupportedDeviceKey != DebugActivity.SELECT_DEVICE) {
|
|
|
|
DebugActivity.createTestDevice(DiscoveryActivity.this, selectedUnsupportedDeviceKey, deviceCandidate.getMacAddress());
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.show();
|
|
|
|
|
2021-12-23 18:28:02 +01:00
|
|
|
return true;
|
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate);
|
|
|
|
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
|
|
|
if (coordinator.getSupportedDeviceSpecificSettings(device) == null) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Intent startIntent;
|
|
|
|
startIntent = new Intent(this, DeviceSettingsActivity.class);
|
|
|
|
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
|
|
|
startActivity(startIntent);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-23 22:41:50 +01:00
|
|
|
private void addListenerOnSpinnerDeviceSelection(Spinner spinner) {
|
|
|
|
spinner.setOnItemSelectedListener(new CustomOnDeviceSelectedListener());
|
|
|
|
}
|
|
|
|
|
|
|
|
public class CustomOnDeviceSelectedListener implements AdapterView.OnItemSelectedListener {
|
|
|
|
|
|
|
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
|
|
|
SpinnerWithIconItem selectedItem = (SpinnerWithIconItem) parent.getItemAtPosition(pos);
|
|
|
|
selectedUnsupportedDeviceKey = selectedItem.getId();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onNothingSelected(AdapterView<?> arg0) {
|
|
|
|
// TODO Auto-generated method stub
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-28 15:38:18 +02:00
|
|
|
public void onBondingComplete(boolean success) {
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
2021-05-30 00:00:48 +02:00
|
|
|
public GBDeviceCandidate getCurrentTarget() {
|
|
|
|
return this.deviceTarget;
|
2020-08-28 15:38:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void unregisterBroadcastReceivers() {
|
|
|
|
AndroidUtils.safeUnregisterBroadcastReceiver(this, bluetoothReceiver);
|
|
|
|
}
|
|
|
|
|
2020-12-24 13:23:25 +01:00
|
|
|
public void registerBroadcastReceivers() {
|
2020-08-28 15:38:18 +02:00
|
|
|
IntentFilter bluetoothIntents = new IntentFilter();
|
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND);
|
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_UUID);
|
|
|
|
bluetoothIntents.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
|
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
|
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
|
|
|
bluetoothIntents.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
|
|
|
|
|
|
|
registerReceiver(bluetoothReceiver, bluetoothIntents);
|
|
|
|
}
|
|
|
|
|
2021-12-23 18:56:36 +01:00
|
|
|
private void loadSettingsValues() {
|
|
|
|
Prefs prefs = GBApplication.getPrefs();
|
|
|
|
ignoreBonded = prefs.getBoolean("ignore_bonded_devices", true);
|
|
|
|
discoverUnsupported = prefs.getBoolean("discover_unsupported_devices", false);
|
|
|
|
oldBleScanning = prefs.getBoolean("disable_new_ble_scanning", false);
|
|
|
|
if (oldBleScanning) {
|
|
|
|
LOG.info("New BLE scanning disabled via settings, using old method");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-18 20:26:28 +02:00
|
|
|
@Override
|
2020-08-28 15:38:18 +02:00
|
|
|
public Context getContext() {
|
|
|
|
return this;
|
2018-06-18 20:26:28 +02:00
|
|
|
}
|
2020-07-19 23:58:21 +02:00
|
|
|
|
|
|
|
private enum Scanning {
|
|
|
|
/**
|
|
|
|
* Regular Bluetooth scan
|
|
|
|
*/
|
|
|
|
SCANNING_BT,
|
|
|
|
/**
|
|
|
|
* Regular Bluetooth scan but when ends, start BLE scan
|
|
|
|
*/
|
|
|
|
SCANNING_BT_NEXT_BLE,
|
|
|
|
/**
|
|
|
|
* Regular BLE scan
|
|
|
|
*/
|
|
|
|
SCANNING_BLE,
|
|
|
|
/**
|
|
|
|
* Scanning has ended or hasn't been started
|
|
|
|
*/
|
|
|
|
SCANNING_OFF
|
|
|
|
}
|
2015-05-05 00:48:02 +02:00
|
|
|
}
|