mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-13 11:17:33 +01:00
Refactored all pairing and bonding activities (#1989)
Fixed a few warnings Refactored all bonding and bonding activities Co-authored-by: TaaviE <taavi.eomae+github@gmail.com> Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/1989
This commit is contained in:
parent
c5daa28214
commit
6cd59fbd24
@ -20,7 +20,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothManager;
|
import android.bluetooth.BluetoothManager;
|
||||||
@ -30,15 +29,10 @@ import android.bluetooth.le.ScanFilter;
|
|||||||
import android.bluetooth.le.ScanRecord;
|
import android.bluetooth.le.ScanRecord;
|
||||||
import android.bluetooth.le.ScanResult;
|
import android.bluetooth.le.ScanResult;
|
||||||
import android.bluetooth.le.ScanSettings;
|
import android.bluetooth.le.ScanSettings;
|
||||||
import android.companion.AssociationRequest;
|
|
||||||
import android.companion.BluetoothDeviceFilter;
|
|
||||||
import android.companion.CompanionDeviceManager;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.IntentSender;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
@ -65,6 +59,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
@ -76,6 +71,8 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||||
@ -83,10 +80,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast;
|
import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast;
|
||||||
|
|
||||||
|
|
||||||
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, BondingInterface {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
||||||
private static final long SCAN_DURATION = 30000; // 30s
|
private static final long SCAN_DURATION = 30000; // 30s
|
||||||
private static final int REQUEST_CODE = 1;
|
|
||||||
private final Handler handler = new Handler();
|
private final Handler handler = new Handler();
|
||||||
private final ArrayList<GBDeviceCandidate> deviceCandidates = new ArrayList<>();
|
private final ArrayList<GBDeviceCandidate> deviceCandidates = new ArrayList<>();
|
||||||
private ScanCallback newBLEScanCallback = null;
|
private ScanCallback newBLEScanCallback = null;
|
||||||
@ -98,10 +94,6 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
* If already bonded devices are to be ignored when scanning
|
* If already bonded devices are to be ignored when scanning
|
||||||
*/
|
*/
|
||||||
private boolean ignoreBonded = true;
|
private boolean ignoreBonded = true;
|
||||||
/**
|
|
||||||
* If new CompanionDevice-type pairing is enabled on newer Androids
|
|
||||||
**/
|
|
||||||
private boolean enableCompanionDevicePairing = false;
|
|
||||||
private ProgressBar bluetoothProgress;
|
private ProgressBar bluetoothProgress;
|
||||||
private ProgressBar bluetoothLEProgress;
|
private ProgressBar bluetoothLEProgress;
|
||||||
private DeviceCandidateAdapter deviceCandidateAdapter;
|
private DeviceCandidateAdapter deviceCandidateAdapter;
|
||||||
@ -127,7 +119,6 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private GBDeviceCandidate bondingDevice;
|
|
||||||
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@ -146,7 +137,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// continue with LE scan, if available
|
// Continue with LE scan, if available
|
||||||
if (isScanning == Scanning.SCANNING_BT || isScanning == Scanning.SCANNING_BT_NEXT_BLE) {
|
if (isScanning == Scanning.SCANNING_BT || isScanning == Scanning.SCANNING_BT_NEXT_BLE) {
|
||||||
checkAndRequestLocationPermission();
|
checkAndRequestLocationPermission();
|
||||||
stopDiscovery();
|
stopDiscovery();
|
||||||
@ -158,15 +149,13 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
}
|
}
|
||||||
case BluetoothAdapter.ACTION_STATE_CHANGED: {
|
case BluetoothAdapter.ACTION_STATE_CHANGED: {
|
||||||
LOG.debug("ACTION_STATE_CHANGED ");
|
LOG.debug("ACTION_STATE_CHANGED ");
|
||||||
int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
bluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF));
|
||||||
bluetoothStateChanged(newState);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BluetoothDevice.ACTION_FOUND: {
|
case BluetoothDevice.ACTION_FOUND: {
|
||||||
LOG.debug("ACTION_FOUND");
|
LOG.debug("ACTION_FOUND");
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
|
handleDeviceFound(device, intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN));
|
||||||
handleDeviceFound(device, rssi);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BluetoothDevice.ACTION_UUID: {
|
case BluetoothDevice.ACTION_UUID: {
|
||||||
@ -181,70 +170,20 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
|
case BluetoothDevice.ACTION_BOND_STATE_CHANGED: {
|
||||||
LOG.debug("ACTION_BOND_STATE_CHANGED");
|
LOG.debug("ACTION_BOND_STATE_CHANGED");
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
if (device != null && bondingDevice != null && device.getAddress().equals(bondingDevice.getMacAddress())) {
|
if (device != null) {
|
||||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
||||||
|
LOG.debug(String.format(Locale.ENGLISH, "Bond state: %d", bondState));
|
||||||
|
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
handleDeviceBonded();
|
BondingUtil.handleDeviceBonded((BondingInterface) context, getCandidateFromMAC(device));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private BluetoothDevice bluetoothTarget;
|
||||||
private void connectAndFinish(GBDevice device) {
|
|
||||||
toast(DiscoveryActivity.this, getString(R.string.discovery_trying_to_connect_to, device.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
|
||||||
GBApplication.deviceService().connect(device, true);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createBond(final GBDeviceCandidate deviceCandidate, int bondingStyle) {
|
|
||||||
if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) {
|
|
||||||
// Do nothing
|
|
||||||
return;
|
|
||||||
} else if (bondingStyle == DeviceCoordinator.BONDING_STYLE_ASK) {
|
|
||||||
new AlertDialog.Builder(this)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setTitle(DiscoveryActivity.this.getString(R.string.discovery_pair_title, deviceCandidate.getName()))
|
|
||||||
.setMessage(DiscoveryActivity.this.getString(R.string.discovery_pair_question))
|
|
||||||
.setPositiveButton(DiscoveryActivity.this.getString(R.string.discovery_yes_pair), new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
doCreatePair(deviceCandidate);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.discovery_dont_pair, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
|
||||||
connectAndFinish(device);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
doCreatePair(deviceCandidate);
|
|
||||||
}
|
|
||||||
LOG.debug("Bonding initiated");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doCreatePair(GBDeviceCandidate deviceCandidate) {
|
|
||||||
toast(DiscoveryActivity.this, getString(R.string.discovery_attempting_to_pair, deviceCandidate.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
|
||||||
if (enableCompanionDevicePairing && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
companionDevicePair(deviceCandidate);
|
|
||||||
} else {
|
|
||||||
deviceBond(deviceCandidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deviceBond(GBDeviceCandidate deviceCandidate) {
|
|
||||||
if (deviceCandidate.getDevice().createBond()) {
|
|
||||||
// Async, wait for bonding event to finish this activity
|
|
||||||
LOG.info("Bonding in progress...");
|
|
||||||
bondingDevice = deviceCandidate;
|
|
||||||
} else {
|
|
||||||
toast(DiscoveryActivity.this, getString(R.string.discovery_bonding_failed_immediately, deviceCandidate.getName()), Toast.LENGTH_SHORT, GB.ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void logMessageContent(byte[] value) {
|
public void logMessageContent(byte[] value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@ -252,66 +191,22 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
private void companionDevicePair(final GBDeviceCandidate deviceCandidate) {
|
|
||||||
CompanionDeviceManager deviceManager = getSystemService(CompanionDeviceManager.class);
|
|
||||||
|
|
||||||
BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder()
|
|
||||||
.setAddress(deviceCandidate.getMacAddress())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AssociationRequest pairingRequest = new AssociationRequest.Builder()
|
|
||||||
.addDeviceFilter(deviceFilter)
|
|
||||||
.setSingleDevice(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
deviceManager.associate(pairingRequest,
|
|
||||||
new CompanionDeviceManager.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onFailure(CharSequence error) {
|
|
||||||
toast(DiscoveryActivity.this, getString(R.string.discovery_bonding_failed_immediately, deviceCandidate.getName()), Toast.LENGTH_SHORT, GB.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDeviceFound(IntentSender chooserLauncher) {
|
|
||||||
try {
|
|
||||||
startIntentSenderForResult(chooserLauncher,
|
|
||||||
REQUEST_CODE, null, 0, 0, 0);
|
|
||||||
} catch (IntentSender.SendIntentException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if (requestCode == REQUEST_CODE &&
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
||||||
resultCode == Activity.RESULT_OK) {
|
|
||||||
|
|
||||||
BluetoothDevice deviceToPair =
|
|
||||||
data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE);
|
|
||||||
|
|
||||||
if (deviceToPair != null) {
|
|
||||||
deviceBond(new GBDeviceCandidate(deviceToPair, (short) 0, null));
|
|
||||||
handleDeviceBonded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDeviceBonded() {
|
|
||||||
if (bondingDevice == null) {
|
|
||||||
LOG.error("deviceCandidate was null! Can't handle bonded device!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast(DiscoveryActivity.this, getString(R.string.discovery_successfully_bonded, bondingDevice.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
private GBDeviceCandidate getCandidateFromMAC(BluetoothDevice device) {
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(bondingDevice);
|
for (GBDeviceCandidate candidate : deviceCandidates) {
|
||||||
connectAndFinish(device);
|
if (candidate.getMacAddress().equals(device.getAddress())) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.warn(String.format("This shouldn't happen unless the list somehow emptied itself, device MAC: %1$s", device.getAddress()));
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
@ -359,11 +254,6 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
LOG.info("New BLE scanning disabled via settings, using old method");
|
LOG.info("New BLE scanning disabled via settings, using old method");
|
||||||
}
|
}
|
||||||
|
|
||||||
enableCompanionDevicePairing = prefs.getBoolean("enable_companiondevice_pairing", true);
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
||||||
enableCompanionDevicePairing = false; // No support below 26
|
|
||||||
}
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_discovery);
|
setContentView(R.layout.activity_discovery);
|
||||||
startButton = findViewById(R.id.discovery_start);
|
startButton = findViewById(R.id.discovery_start);
|
||||||
startButton.setOnClickListener(new View.OnClickListener() {
|
startButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -389,15 +279,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
deviceCandidatesView.setOnItemClickListener(this);
|
deviceCandidatesView.setOnItemClickListener(this);
|
||||||
deviceCandidatesView.setOnItemLongClickListener(this);
|
deviceCandidatesView.setOnItemLongClickListener(this);
|
||||||
|
|
||||||
IntentFilter bluetoothIntents = new IntentFilter();
|
removeBroadcastReceivers();
|
||||||
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);
|
|
||||||
|
|
||||||
checkAndRequestLocationPermission();
|
checkAndRequestLocationPermission();
|
||||||
|
|
||||||
@ -437,14 +319,38 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
try {
|
unregisterBroadcastReceivers();
|
||||||
unregisterReceiver(bluetoothReceiver);
|
stopAllDiscovery();
|
||||||
} catch (IllegalArgumentException e) {
|
super.onDestroy();
|
||||||
LOG.warn("Tried to unregister Bluetooth Receiver that wasn't registered");
|
|
||||||
LOG.warn(e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDestroy();
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
|
stopAllDiscovery();
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
|
stopAllDiscovery();
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAllDiscovery() {
|
||||||
|
try {
|
||||||
|
stopBTDiscovery();
|
||||||
|
if (oldBleScanning) {
|
||||||
|
stopOldBLEDiscovery();
|
||||||
|
} else {
|
||||||
|
if (GBApplication.isRunningLollipopOrLater()) {
|
||||||
|
stopBLEDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Error stopping discovery", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDeviceFound(BluetoothDevice device, short rssi) {
|
private void handleDeviceFound(BluetoothDevice device, short rssi) {
|
||||||
@ -791,6 +697,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.error("Exception when checking location status: ", ex);
|
LOG.error("Exception when checking location status: ", ex);
|
||||||
}
|
}
|
||||||
|
LOG.error("Problem with permissions, returning");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -824,32 +731,15 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
intent.putExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE, deviceCandidate);
|
intent.putExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE, deviceCandidate);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_NONE) {
|
||||||
int bondingStyle = coordinator.getBondingStyle();
|
|
||||||
if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) {
|
|
||||||
LOG.info("No bonding needed, according to coordinator, so connecting right away");
|
LOG.info("No bonding needed, according to coordinator, so connecting right away");
|
||||||
connectAndFinish(device);
|
BondingUtil.connectThenComplete(this, deviceCandidate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BluetoothDevice btDevice = adapter.getRemoteDevice(deviceCandidate.getMacAddress());
|
this.bluetoothTarget = deviceCandidate.getDevice();
|
||||||
switch (btDevice.getBondState()) {
|
BondingUtil.initiateCorrectBonding(this, deviceCandidate);
|
||||||
case BluetoothDevice.BOND_NONE: {
|
|
||||||
createBond(deviceCandidate, bondingStyle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BluetoothDevice.BOND_BONDING: {
|
|
||||||
// async, wait for bonding event to finish this activity
|
|
||||||
bondingDevice = deviceCandidate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BluetoothDevice.BOND_BONDED: {
|
|
||||||
bondingDevice = deviceCandidate;
|
|
||||||
handleDeviceBonded();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Error pairing device: " + deviceCandidate.getMacAddress());
|
LOG.error("Error pairing device: " + deviceCandidate.getMacAddress());
|
||||||
}
|
}
|
||||||
@ -877,17 +767,33 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onBondingComplete(boolean success) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BluetoothDevice getCurrentTarget() {
|
||||||
|
return this.bluetoothTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterBroadcastReceivers() {
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(this, bluetoothReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBroadcastReceivers() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
public Context getContext() {
|
||||||
super.onPause();
|
return this;
|
||||||
stopBTDiscovery();
|
|
||||||
if (oldBleScanning) {
|
|
||||||
stopOldBLEDiscovery();
|
|
||||||
} else {
|
|
||||||
if (GBApplication.isRunningLollipopOrLater()) {
|
|
||||||
stopBLEDiscovery();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Scanning {
|
private enum Scanning {
|
||||||
|
@ -37,7 +37,6 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9PairingActivity;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributesDao;
|
||||||
@ -109,6 +108,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for subclasses to perform device-specific deletion logic, e.g. db cleanup.
|
* Hook for subclasses to perform device-specific deletion logic, e.g. db cleanup.
|
||||||
|
*
|
||||||
* @param gbDevice the GBDevice
|
* @param gbDevice the GBDevice
|
||||||
* @param device the corresponding database Device
|
* @param device the corresponding database Device
|
||||||
* @param session the session to use
|
* @param session the session to use
|
||||||
@ -185,7 +185,9 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsUnicodeEmojis() { return false; }
|
public boolean supportsUnicodeEmojis() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
|
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2018-2019 Daniele Gobbetti, maxirnilian
|
/* Copyright (C) 2018-2020 Daniele Gobbetti, maxirnilian, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -16,6 +16,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.devices.lenovo;
|
package nodomain.freeyourgadget.gadgetbridge.devices.lenovo;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -24,12 +25,12 @@ import android.os.Bundle;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
||||||
@ -38,33 +39,18 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
||||||
|
|
||||||
public class LenovoWatchPairingActivity extends AbstractGBActivity {
|
import static nodomain.freeyourgadget.gadgetbridge.util.BondingUtil.STATE_DEVICE_CANDIDATE;
|
||||||
|
|
||||||
|
public class LenovoWatchPairingActivity extends AbstractGBActivity implements BondingInterface {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(LenovoWatchPairingActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(LenovoWatchPairingActivity.class);
|
||||||
|
|
||||||
private static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate";
|
|
||||||
|
|
||||||
private TextView message;
|
private TextView message;
|
||||||
private GBDeviceCandidate deviceCandidate;
|
private GBDeviceCandidate deviceCandidate;
|
||||||
|
|
||||||
private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver pairingReceiver = BondingUtil.getPairingReceiver(this);
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
|
||||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
|
||||||
LOG.debug("pairing activity: device changed: " + device);
|
|
||||||
if (deviceCandidate.getMacAddress().equals(device.getAddress())) {
|
|
||||||
if (device.isInitialized()) {
|
|
||||||
pairingFinished();
|
|
||||||
} else if (device.isConnecting() || device.isInitializing()) {
|
|
||||||
LOG.info("still connecting/initializing device...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -83,7 +69,8 @@ public class LenovoWatchPairingActivity extends AbstractGBActivity {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startPairing();
|
|
||||||
|
startPairing(deviceCandidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -98,33 +85,72 @@ public class LenovoWatchPairingActivity extends AbstractGBActivity {
|
|||||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startPairing(GBDeviceCandidate deviceCandidate) {
|
||||||
|
message.setText(getString(R.string.pairing, deviceCandidate));
|
||||||
|
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
|
||||||
|
BondingUtil.connectThenComplete(this, deviceCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBondingComplete(boolean success) {
|
||||||
|
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BluetoothDevice getCurrentTarget() {
|
||||||
|
return this.deviceCandidate.getDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
unregisterBroadcastReceivers();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPairing() {
|
@Override
|
||||||
message.setText(getString(R.string.pairing, deviceCandidate));
|
protected void onStop() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED);
|
super.onStop();
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter);
|
|
||||||
|
|
||||||
GBApplication.deviceService().disconnect();
|
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
|
||||||
if (device != null) {
|
|
||||||
GBApplication.deviceService().connect(device, true);
|
|
||||||
} else {
|
|
||||||
GB.toast(this, "Unable to connect, can't recognize the device type: " + deviceCandidate, Toast.LENGTH_LONG, GB.ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pairingFinished() {
|
@Override
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
protected void onPause() {
|
||||||
|
// WARN: Do not remove listeners on pause
|
||||||
|
// Bonding process can pause the activity and you might miss broadcasts
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
public void unregisterBroadcastReceivers() {
|
||||||
startActivity(intent);
|
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), pairingReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
finish();
|
public void removeBroadcastReceivers() {
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(pairingReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
Gobbetti
|
Gobbetti, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -25,8 +25,6 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -45,74 +43,24 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||||
|
|
||||||
public class MiBandPairingActivity extends AbstractGBActivity {
|
import static nodomain.freeyourgadget.gadgetbridge.util.BondingUtil.STATE_DEVICE_CANDIDATE;
|
||||||
|
|
||||||
|
public class MiBandPairingActivity extends AbstractGBActivity implements BondingInterface {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class);
|
||||||
|
|
||||||
private static final int REQ_CODE_USER_SETTINGS = 52;
|
private static final int REQ_CODE_USER_SETTINGS = 52;
|
||||||
private static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate";
|
|
||||||
private static final long DELAY_AFTER_BONDING = 1000; // 1s
|
private final BroadcastReceiver pairingReceiver = BondingUtil.getPairingReceiver(this);
|
||||||
|
private final BroadcastReceiver bondingReceiver = BondingUtil.getBondingReceiver(this);
|
||||||
private TextView message;
|
private TextView message;
|
||||||
private boolean isPairing;
|
private boolean isPairing;
|
||||||
private GBDeviceCandidate deviceCandidate;
|
private GBDeviceCandidate deviceCandidate;
|
||||||
private String bondingMacAddress;
|
|
||||||
|
|
||||||
private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
|
||||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
|
||||||
LOG.debug("pairing activity: device changed: " + device);
|
|
||||||
if (deviceCandidate.getMacAddress().equals(device.getAddress())) {
|
|
||||||
if (device.isInitialized()) {
|
|
||||||
pairingFinished(true, deviceCandidate);
|
|
||||||
} else if (device.isConnecting() || device.isInitializing()) {
|
|
||||||
LOG.info("still connecting/initializing device...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BroadcastReceiver mBondingReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
|
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
||||||
LOG.info("Bond state changed: " + device + ", state: " + device.getBondState() + ", expected address: " + bondingMacAddress);
|
|
||||||
if (bondingMacAddress != null && bondingMacAddress.equals(device.getAddress())) {
|
|
||||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
LOG.info("Bonded with " + device.getAddress());
|
|
||||||
bondingMacAddress = null;
|
|
||||||
attemptToConnect();
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_BONDING) {
|
|
||||||
LOG.info("Bonding in progress with " + device.getAddress());
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
|
||||||
LOG.info("Not bonded with " + device.getAddress() + ", attempting to connect anyway.");
|
|
||||||
bondingMacAddress = null;
|
|
||||||
attemptToConnect();
|
|
||||||
} else {
|
|
||||||
LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState);
|
|
||||||
pairingFinished(false, deviceCandidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void attemptToConnect() {
|
|
||||||
Looper mainLooper = Looper.getMainLooper();
|
|
||||||
new Handler(mainLooper).postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
performApplicationLevelPair();
|
|
||||||
}
|
|
||||||
}, DELAY_AFTER_BONDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -120,11 +68,11 @@ public class MiBandPairingActivity extends AbstractGBActivity {
|
|||||||
setContentView(R.layout.activity_mi_band_pairing);
|
setContentView(R.layout.activity_mi_band_pairing);
|
||||||
|
|
||||||
message = findViewById(R.id.miband_pair_message);
|
message = findViewById(R.id.miband_pair_message);
|
||||||
Intent intent = getIntent();
|
this.deviceCandidate = getIntent().getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
||||||
deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
|
||||||
if (deviceCandidate == null && savedInstanceState != null) {
|
if (deviceCandidate == null && savedInstanceState != null) {
|
||||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
this.deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceCandidate == null) {
|
if (deviceCandidate == null) {
|
||||||
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
||||||
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
@ -179,56 +127,40 @@ public class MiBandPairingActivity extends AbstractGBActivity {
|
|||||||
}
|
}
|
||||||
startPairing();
|
startPairing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
// just to be sure, remove the receivers -- might actually be already unregistered
|
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(this, mBondingReceiver);
|
|
||||||
if (isPairing) {
|
|
||||||
stopPairing();
|
|
||||||
}
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startPairing() {
|
private void startPairing() {
|
||||||
isPairing = true;
|
isPairing = true;
|
||||||
message.setText(getString(R.string.pairing, deviceCandidate));
|
message.setText(getString(R.string.pairing, deviceCandidate));
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED);
|
if (!BondingUtil.shouldUseBonding()) {
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter);
|
BondingUtil.attemptToFirstConnect(getCurrentTarget());
|
||||||
|
|
||||||
if (!shouldSetupBTLevelPairing()) {
|
|
||||||
// there are connection problems on certain Galaxy S devices at least;
|
|
||||||
// try to connect without BT pairing (bonding)
|
|
||||||
attemptToConnect();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
|
BondingUtil.tryBondThenComplete(this, deviceCandidate);
|
||||||
registerReceiver(mBondingReceiver, filter);
|
|
||||||
|
|
||||||
performBluetoothPair(deviceCandidate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldSetupBTLevelPairing() {
|
|
||||||
Prefs prefs = GBApplication.getPrefs();
|
private void stopPairing() {
|
||||||
return prefs.getPreferences().getBoolean(MiBandConst.PREF_MIBAND_SETUP_BT_PAIRING, true);
|
isPairing = false;
|
||||||
|
BondingUtil.stopBluetoothBonding(deviceCandidate.getDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pairingFinished(boolean pairedSuccessfully, GBDeviceCandidate candidate) {
|
@Override
|
||||||
LOG.debug("pairingFinished: " + pairedSuccessfully);
|
public void onBondingComplete(boolean success) {
|
||||||
|
LOG.debug("pairingFinished: " + success);
|
||||||
if (!isPairing) {
|
if (!isPairing) {
|
||||||
// already gone?
|
// already gone?
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
isPairing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPairing = false;
|
if (success) {
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(this, mBondingReceiver);
|
|
||||||
|
|
||||||
if (pairedSuccessfully) {
|
|
||||||
// remember the device since we do not necessarily pair... temporary -- we probably need
|
// remember the device since we do not necessarily pair... temporary -- we probably need
|
||||||
// to query the db for available devices in ControlCenter. But only remember un-bonded
|
// to query the db for available devices in ControlCenter. But only remember un-bonded
|
||||||
// devices, as bonded devices are displayed anyway.
|
// devices, as bonded devices are displayed anyway.
|
||||||
@ -244,40 +176,59 @@ public class MiBandPairingActivity extends AbstractGBActivity {
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopPairing() {
|
@Override
|
||||||
// TODO
|
public BluetoothDevice getCurrentTarget() {
|
||||||
isPairing = false;
|
return this.deviceCandidate.getDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void performBluetoothPair(GBDeviceCandidate deviceCandidate) {
|
@Override
|
||||||
BluetoothDevice device = deviceCandidate.getDevice();
|
protected void onResume() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
int bondState = device.getBondState();
|
super.onResume();
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
GB.toast(getString(R.string.pairing_already_bonded, device.getName(), device.getAddress()), Toast.LENGTH_SHORT, GB.INFO);
|
|
||||||
performApplicationLevelPair();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bondingMacAddress = device.getAddress();
|
@Override
|
||||||
if (bondState == BluetoothDevice.BOND_BONDING) {
|
protected void onStart() {
|
||||||
GB.toast(this, getString(R.string.pairing_in_progress, device.getName(), bondingMacAddress), Toast.LENGTH_LONG, GB.INFO);
|
removeBroadcastReceivers();
|
||||||
return;
|
super.onStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
GB.toast(this, getString(R.string.pairing_creating_bond_with, device.getName(), bondingMacAddress), Toast.LENGTH_LONG, GB.INFO);
|
@Override
|
||||||
if (!device.createBond()) {
|
protected void onDestroy() {
|
||||||
GB.toast(this, getString(R.string.pairing_unable_to_pair_with, device.getName(), bondingMacAddress), Toast.LENGTH_LONG, GB.ERROR);
|
unregisterBroadcastReceivers();
|
||||||
|
if (isPairing) {
|
||||||
|
stopPairing();
|
||||||
}
|
}
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performApplicationLevelPair() {
|
@Override
|
||||||
GBApplication.deviceService().disconnect(); // just to make sure...
|
protected void onStop() {
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
unregisterBroadcastReceivers();
|
||||||
if (device != null) {
|
if (isPairing) {
|
||||||
GBApplication.deviceService().connect(device, true);
|
stopPairing();
|
||||||
} else {
|
}
|
||||||
GB.toast(this, "Unable to connect, can't recognize the device type: " + deviceCandidate, Toast.LENGTH_LONG, GB.ERROR);
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
// WARN: Do not stop pairing or unregister receivers pause!
|
||||||
|
// Bonding process can pause the activity and you might miss broadcasts
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterBroadcastReceivers() {
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), pairingReceiver);
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(this, bondingReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBroadcastReceivers() {
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(pairingReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
|
||||||
|
registerReceiver(bondingReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
Gobbetti
|
Gobbetti, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ import android.os.Bundle;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -47,218 +48,210 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceDao;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
public class PebblePairingActivity extends AbstractGBActivity {
|
import static nodomain.freeyourgadget.gadgetbridge.util.BondingUtil.STATE_DEVICE_CANDIDATE;
|
||||||
|
|
||||||
|
|
||||||
|
public class PebblePairingActivity extends AbstractGBActivity implements BondingInterface {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PebblePairingActivity.class);
|
||||||
|
private final BroadcastReceiver pairingReceiver = BondingUtil.getPairingReceiver(this);
|
||||||
|
private final BroadcastReceiver bondingReceiver = BondingUtil.getBondingReceiver(this);
|
||||||
|
|
||||||
private TextView message;
|
private TextView message;
|
||||||
private boolean isPairing;
|
private boolean isPairing;
|
||||||
private boolean isLEPebble;
|
|
||||||
private String macAddress;
|
|
||||||
private BluetoothDevice mBtDevice;
|
|
||||||
|
|
||||||
private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() {
|
private GBDeviceCandidate deviceCandidate;
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
|
||||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
|
||||||
LOG.debug("pairing activity: device changed: " + device);
|
|
||||||
if (macAddress.equals(device.getAddress()) || macAddress.equals(device.getVolatileAddress())) {
|
|
||||||
if (device.isInitialized()) {
|
|
||||||
pairingFinished(true);
|
|
||||||
} else if (device.isConnecting() || device.isInitializing()) {
|
|
||||||
LOG.info("still connecting/initializing device...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BroadcastReceiver mBondingReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
|
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
||||||
LOG.info("Bond state changed: " + device + ", state: " + device.getBondState() + ", expected address: " + macAddress);
|
|
||||||
if (macAddress != null && macAddress.equals(device.getAddress())) {
|
|
||||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
LOG.info("Bonded with " + device.getAddress());
|
|
||||||
if (!isLEPebble) {
|
|
||||||
performConnect(null);
|
|
||||||
}
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_BONDING) {
|
|
||||||
LOG.info("Bonding in progress with " + device.getAddress());
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
|
||||||
LOG.info("Not bonded with " + device.getAddress() + ", attempting to connect anyway.");
|
|
||||||
} else {
|
|
||||||
LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState);
|
|
||||||
pairingFinished(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_pebble_pairing);
|
setContentView(R.layout.activity_pebble_pairing);
|
||||||
|
|
||||||
message = (TextView) findViewById(R.id.pebble_pair_message);
|
message = findViewById(R.id.pebble_pair_message);
|
||||||
Intent intent = getIntent();
|
deviceCandidate = getIntent().getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
||||||
GBDeviceCandidate candidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
|
||||||
if (candidate != null) {
|
String macAddress = null;
|
||||||
macAddress = candidate.getMacAddress();
|
if (deviceCandidate != null) {
|
||||||
|
macAddress = deviceCandidate.getMacAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (macAddress == null) {
|
if (macAddress == null) {
|
||||||
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mBtDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress);
|
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress);
|
||||||
if (mBtDevice == null) {
|
if (btDevice == null) {
|
||||||
GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(this, "No such Bluetooth Device: " + macAddress, Toast.LENGTH_LONG, GB.ERROR);
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isLEPebble = mBtDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE;
|
startBonding(btDevice);
|
||||||
|
}
|
||||||
|
|
||||||
GBDevice gbDevice = null;
|
private void startBonding(BluetoothDevice btDevice) {
|
||||||
if (isLEPebble) {
|
isPairing = true;
|
||||||
if (mBtDevice.getName().startsWith("Pebble-LE ") || mBtDevice.getName().startsWith("Pebble Time LE ")) {
|
message.setText(getString(R.string.pairing, btDevice.getAddress()));
|
||||||
|
|
||||||
|
GBDevice device;
|
||||||
|
if (BondingUtil.isLePebble(btDevice)) {
|
||||||
if (!GBApplication.getPrefs().getBoolean("pebble_force_le", false)) {
|
if (!GBApplication.getPrefs().getBoolean("pebble_force_le", false)) {
|
||||||
GB.toast(this, "Please switch on \"Always prefer BLE\" option in Pebble settings before pairing you Pebble LE", Toast.LENGTH_LONG, GB.ERROR);
|
GB.toast(this, "Please switch on \"Always prefer BLE\" option in Pebble settings before pairing you Pebble LE", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return;
|
|
||||||
}
|
|
||||||
gbDevice = getMatchingParentDeviceFromDB(mBtDevice);
|
|
||||||
if (gbDevice == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startPairing(gbDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
try {
|
|
||||||
// just to be sure, remove the receivers -- might actually be already unregistered
|
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver);
|
|
||||||
unregisterReceiver(mBondingReceiver);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
// already unregistered, ignore
|
|
||||||
}
|
|
||||||
if (isPairing) {
|
|
||||||
stopPairing();
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startPairing(GBDevice gbDevice) {
|
|
||||||
isPairing = true;
|
|
||||||
message.setText(getString(R.string.pairing, macAddress));
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED);
|
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter);
|
|
||||||
filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
|
|
||||||
registerReceiver(mBondingReceiver, filter);
|
|
||||||
|
|
||||||
performPair(gbDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pairingFinished(boolean pairedSuccessfully) {
|
|
||||||
LOG.debug("pairingFinished: " + pairedSuccessfully);
|
|
||||||
if (!isPairing) {
|
|
||||||
// already gone?
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPairing = false;
|
device = getMatchingParentDeviceFromDBAndSetVolatileAddress(btDevice);
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver);
|
if (device == null) {
|
||||||
unregisterReceiver(mBondingReceiver);
|
onBondingComplete(false);
|
||||||
|
|
||||||
if (pairedSuccessfully) {
|
|
||||||
Intent intent = new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopPairing() {
|
|
||||||
// TODO
|
|
||||||
isPairing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void performPair(GBDevice gbDevice) {
|
|
||||||
int bondState = mBtDevice.getBondState();
|
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
GB.toast(getString(R.string.pairing_already_bonded, mBtDevice.getName(), mBtDevice.getAddress()), Toast.LENGTH_SHORT, GB.INFO);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bondState == BluetoothDevice.BOND_BONDING) {
|
removeBroadcastReceivers();
|
||||||
GB.toast(this, getString(R.string.pairing_in_progress, mBtDevice.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO);
|
BondingUtil.connectThenComplete(this, device);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB.toast(this, getString(R.string.pairing_creating_bond_with, mBtDevice.getName(), macAddress), Toast.LENGTH_LONG, GB.INFO);
|
if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED ||
|
||||||
GBApplication.deviceService().disconnect(); // just to make sure...
|
btDevice.getBondState() == BluetoothDevice.BOND_BONDING) {
|
||||||
|
BondingUtil.connectThenComplete(this, deviceCandidate);
|
||||||
if (isLEPebble) {
|
|
||||||
performConnect(gbDevice);
|
|
||||||
} else {
|
} else {
|
||||||
mBtDevice.createBond();
|
BondingUtil.tryBondThenComplete(this, deviceCandidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performConnect(GBDevice gbDevice) {
|
private void stopBonding() {
|
||||||
if (gbDevice == null) {
|
isPairing = false;
|
||||||
gbDevice = new GBDevice(mBtDevice.getAddress(), mBtDevice.getName(), null, DeviceType.PEBBLE);
|
BondingUtil.stopBluetoothBonding(deviceCandidate.getDevice());
|
||||||
}
|
|
||||||
GBApplication.deviceService().connect(gbDevice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void returnToPairingActivity() {
|
private GBDevice getMatchingParentDeviceFromDBAndSetVolatileAddress(BluetoothDevice btDevice) {
|
||||||
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private GBDevice getMatchingParentDeviceFromDB(BluetoothDevice btDevice) {
|
|
||||||
String expectedSuffix = btDevice.getName();
|
String expectedSuffix = btDevice.getName();
|
||||||
expectedSuffix = expectedSuffix.replace("Pebble-LE ", "");
|
expectedSuffix = expectedSuffix.replace("Pebble-LE ", "");
|
||||||
expectedSuffix = expectedSuffix.replace("Pebble Time LE ", "");
|
expectedSuffix = expectedSuffix.replace("Pebble Time LE ", "");
|
||||||
expectedSuffix = expectedSuffix.substring(0, 2) + ":" + expectedSuffix.substring(2);
|
expectedSuffix = expectedSuffix.substring(0, 2) + ":" + expectedSuffix.substring(2);
|
||||||
LOG.info("will try to find a Pebble with BT address suffix " + expectedSuffix);
|
LOG.info("Trying to find a Pebble with BT address suffix " + expectedSuffix);
|
||||||
GBDevice gbDevice = null;
|
|
||||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||||
DaoSession session = dbHandler.getDaoSession();
|
DaoSession session = dbHandler.getDaoSession();
|
||||||
DeviceDao deviceDao = session.getDeviceDao();
|
DeviceDao deviceDao = session.getDeviceDao();
|
||||||
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Type.eq(1), DeviceDao.Properties.Identifier.like("%" + expectedSuffix)).build();
|
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Type.eq(1), DeviceDao.Properties.Identifier.like("%" + expectedSuffix)).build();
|
||||||
|
|
||||||
List<Device> devices = query.list();
|
List<Device> devices = query.list();
|
||||||
if (devices.size() == 0) {
|
if (devices.size() == 0) {
|
||||||
GB.toast("Please pair your non-LE Pebble before pairing the LE one", Toast.LENGTH_SHORT, GB.INFO);
|
GB.toast("Please pair your non-LE Pebble before pairing the LE one", Toast.LENGTH_SHORT, GB.INFO);
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return null;
|
return null;
|
||||||
} else if (devices.size() > 1) {
|
} else if (devices.size() > 1) {
|
||||||
GB.toast("Can not match this Pebble LE to a unique device", Toast.LENGTH_SHORT, GB.INFO);
|
GB.toast("Can not match this Pebble LE to a unique device", Toast.LENGTH_SHORT, GB.INFO);
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
DeviceHelper deviceHelper = DeviceHelper.getInstance();
|
|
||||||
gbDevice = deviceHelper.toGBDevice(devices.get(0));
|
GBDevice gbDevice = DeviceHelper.getInstance().toGBDevice(devices.get(0));
|
||||||
gbDevice.setVolatileAddress(btDevice.getAddress());
|
gbDevice.setVolatileAddress(btDevice.getAddress());
|
||||||
|
return gbDevice;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
GB.toast(getString(R.string.error_retrieving_devices_database), Toast.LENGTH_SHORT, GB.ERROR, e);
|
GB.toast(getString(R.string.error_retrieving_devices_database), Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||||
returnToPairingActivity();
|
onBondingComplete(false);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return gbDevice;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBondingComplete(boolean success) {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
|
if (success) {
|
||||||
|
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
|
} else {
|
||||||
|
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not a LE Pebble, initiate a connection when bonding is complete
|
||||||
|
if (!BondingUtil.isLePebble(getCurrentTarget()) && success) {
|
||||||
|
BondingUtil.attemptToFirstConnect(getCurrentTarget());
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BluetoothDevice getCurrentTarget() {
|
||||||
|
return this.deviceCandidate.getDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putParcelable(STATE_DEVICE_CANDIDATE, deviceCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
|
if (isPairing) {
|
||||||
|
stopBonding();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
|
if (isPairing) {
|
||||||
|
stopBonding();
|
||||||
|
}
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
// WARN: Do not stop bonding process during pause!
|
||||||
|
// Bonding process can pause the activity and you might miss broadcasts
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterBroadcastReceivers() {
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), pairingReceiver);
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(this, bondingReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBroadcastReceivers() {
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(pairingReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
|
||||||
|
registerReceiver(bondingReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2018-2020 Daniele Gobbetti, maxirnilian
|
/* Copyright (C) 2018-2020 Daniele Gobbetti, maxirnilian, Taavi Eomäe
|
||||||
|
|
||||||
This file is part of Gadgetbridge.
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
@ -16,6 +16,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -24,11 +25,11 @@ import android.os.Bundle;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
||||||
@ -37,52 +38,38 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingInterface;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
|
||||||
|
|
||||||
public class Watch9PairingActivity extends AbstractGBActivity {
|
import static nodomain.freeyourgadget.gadgetbridge.util.BondingUtil.STATE_DEVICE_CANDIDATE;
|
||||||
|
|
||||||
|
public class Watch9PairingActivity extends AbstractGBActivity implements BondingInterface {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Watch9PairingActivity.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Watch9PairingActivity.class);
|
||||||
|
|
||||||
private static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate";
|
private final BroadcastReceiver pairingReceiver = BondingUtil.getPairingReceiver(this);
|
||||||
|
|
||||||
private TextView message;
|
private TextView message;
|
||||||
private GBDeviceCandidate deviceCandidate;
|
private GBDeviceCandidate deviceCandidate;
|
||||||
|
|
||||||
private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
|
||||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
|
||||||
LOG.debug("pairing activity: device changed: " + device);
|
|
||||||
if (deviceCandidate.getMacAddress().equals(device.getAddress())) {
|
|
||||||
if (device.isInitialized()) {
|
|
||||||
pairingFinished();
|
|
||||||
} else if (device.isConnecting() || device.isInitializing()) {
|
|
||||||
LOG.info("still connecting/initializing device...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_watch9_pairing);
|
setContentView(R.layout.activity_watch9_pairing);
|
||||||
|
|
||||||
message = findViewById(R.id.watch9_pair_message);
|
message = findViewById(R.id.watch9_pair_message);
|
||||||
Intent intent = getIntent();
|
|
||||||
deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
deviceCandidate = getIntent().getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
||||||
if (deviceCandidate == null && savedInstanceState != null) {
|
if (deviceCandidate == null && savedInstanceState != null) {
|
||||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceCandidate == null) {
|
if (deviceCandidate == null) {
|
||||||
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
||||||
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
startPairing();
|
|
||||||
|
startConnecting(deviceCandidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -97,33 +84,73 @@ public class Watch9PairingActivity extends AbstractGBActivity {
|
|||||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startConnecting(GBDeviceCandidate deviceCandidate) {
|
||||||
|
message.setText(getString(R.string.pairing, deviceCandidate));
|
||||||
|
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
BondingUtil.connectThenComplete(this, deviceCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBondingComplete(boolean success) {
|
||||||
|
startActivity(new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BluetoothDevice getCurrentTarget() {
|
||||||
|
return this.deviceCandidate.getDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
BondingUtil.handleActivityResult(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterBroadcastReceivers() {
|
||||||
|
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), pairingReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeBroadcastReceivers() {
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(pairingReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
removeBroadcastReceivers();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
unregisterBroadcastReceivers();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPairing() {
|
@Override
|
||||||
message.setText(getString(R.string.pairing, deviceCandidate));
|
protected void onStop() {
|
||||||
|
unregisterBroadcastReceivers();
|
||||||
IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED);
|
super.onStop();
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter);
|
|
||||||
|
|
||||||
GBApplication.deviceService().disconnect();
|
|
||||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
|
||||||
if (device != null) {
|
|
||||||
GBApplication.deviceService().connect(device, true);
|
|
||||||
} else {
|
|
||||||
GB.toast(this, "Unable to connect, can't recognize the device type: " + deviceCandidate, Toast.LENGTH_LONG, GB.ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pairingFinished() {
|
@Override
|
||||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
protected void onPause() {
|
||||||
|
// WARN: Do not remove listeners on pause
|
||||||
Intent intent = new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
// Bonding process can pause the activity and you might miss broadcasts
|
||||||
startActivity(intent);
|
super.onPause();
|
||||||
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jpbarraca on 13/04/2017.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
|
public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class);
|
private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class);
|
||||||
|
|
||||||
final DeviceCommunicationService service;
|
final DeviceCommunicationService service;
|
||||||
@ -56,8 +50,9 @@ public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
GBDevice gbDevice = service.getGBDevice();
|
GBDevice gbDevice = service.getGBDevice();
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
if (gbDevice == null || device == null)
|
if (gbDevice == null || device == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
||||||
try {
|
try {
|
||||||
@ -67,7 +62,6 @@ public class BluetoothPairingRequestReceiver extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Could not abort pairing request process");
|
LOG.warn("Could not abort pairing request process");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -103,12 +103,12 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
public static final String ACTION_REPLY
|
public static final String ACTION_REPLY
|
||||||
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply";
|
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply";
|
||||||
|
|
||||||
private LimitedQueue mActionLookup = new LimitedQueue(32);
|
private final LimitedQueue mActionLookup = new LimitedQueue(32);
|
||||||
private LimitedQueue mPackageLookup = new LimitedQueue(64);
|
private final LimitedQueue mPackageLookup = new LimitedQueue(64);
|
||||||
private LimitedQueue mNotificationHandleLookup = new LimitedQueue(128);
|
private final LimitedQueue mNotificationHandleLookup = new LimitedQueue(128);
|
||||||
|
|
||||||
private HashMap<String, Long> notificationBurstPrevention = new HashMap<>();
|
private final HashMap<String, Long> notificationBurstPrevention = new HashMap<>();
|
||||||
private HashMap<String, Long> notificationOldRepeatPrevention = new HashMap<>();
|
private final HashMap<String, Long> notificationOldRepeatPrevention = new HashMap<>();
|
||||||
|
|
||||||
private static final Set<String> GROUP_SUMMARY_WHITELIST = Collections.singleton(
|
private static final Set<String> GROUP_SUMMARY_WHITELIST = Collections.singleton(
|
||||||
"mikado.bizcalpro"
|
"mikado.bizcalpro"
|
||||||
@ -119,7 +119,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
private long activeCallPostTime;
|
private long activeCallPostTime;
|
||||||
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
|
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
|
||||||
|
|
||||||
private Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
private Runnable mSetMusicInfoRunnable = null;
|
private Runnable mSetMusicInfoRunnable = null;
|
||||||
private Runnable mSetMusicStateRunnable = null;
|
private Runnable mSetMusicStateRunnable = null;
|
||||||
|
|
||||||
@ -516,8 +516,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
GBApplication.deviceService().onSetCallState(callSpec);
|
GBApplication.deviceService().onSetCallState(callSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldContinueAfterFilter(@NonNull String body, @NonNull List<String> wordsList, @NonNull NotificationFilter notificationFilter) {
|
boolean shouldContinueAfterFilter(String body, @NonNull List<String> wordsList, @NonNull NotificationFilter notificationFilter) {
|
||||||
|
|
||||||
LOG.debug("Mode: '{}' Submode: '{}' WordsList: '{}'", notificationFilter.getNotificationFilterMode(), notificationFilter.getNotificationFilterSubMode(), wordsList);
|
LOG.debug("Mode: '{}' Submode: '{}' WordsList: '{}'", notificationFilter.getNotificationFilterMode(), notificationFilter.getNotificationFilterSubMode(), wordsList);
|
||||||
|
|
||||||
boolean allMode = notificationFilter.getNotificationFilterSubMode() == NOTIFICATION_FILTER_SUBMODE_ALL;
|
boolean allMode = notificationFilter.getNotificationFilterSubMode() == NOTIFICATION_FILTER_SUBMODE_ALL;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||||
@ -139,7 +140,7 @@ public interface DeviceService extends EventHandler {
|
|||||||
|
|
||||||
void connect(@Nullable GBDevice device);
|
void connect(@Nullable GBDevice device);
|
||||||
|
|
||||||
void connect(@Nullable GBDevice device, boolean performPair);
|
void connect(@Nullable GBDevice device, boolean firstTime);
|
||||||
|
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
|
@ -29,6 +29,10 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.telephony.SmsManager;
|
import android.telephony.SmsManager;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -40,14 +44,10 @@ import java.util.Date;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.FindPhoneActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.FindPhoneActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
|
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsHost;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||||
@ -186,12 +186,11 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleGBDeviceEventFindPhoneStart() {
|
private void handleGBDeviceEventFindPhoneStart() {
|
||||||
if ( Build.VERSION.SDK_INT < 29 ) { // this could be used if app in foreground
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // this could be used if app in foreground // TODO: Below Q?
|
||||||
Intent startIntent = new Intent(getContext(), FindPhoneActivity.class);
|
Intent startIntent = new Intent(getContext(), FindPhoneActivity.class);
|
||||||
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
context.startActivity(startIntent);
|
context.startActivity(startIntent);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
handleGBDeviceEventFindPhoneStartNotification();
|
handleGBDeviceEventFindPhoneStartNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +230,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
|||||||
LOG.info("Got event for CALL_CONTROL");
|
LOG.info("Got event for CALL_CONTROL");
|
||||||
if(callEvent.event == GBDeviceEventCallControl.Event.IGNORE) {
|
if(callEvent.event == GBDeviceEventCallControl.Event.IGNORE) {
|
||||||
LOG.info("Sending intent for mute");
|
LOG.info("Sending intent for mute");
|
||||||
Intent broadcastIntent = new Intent("nodomain.freeyourgadget.gadgetbridge.MUTE_CALL");
|
Intent broadcastIntent = new Intent(context.getPackageName() + ".MUTE_CALL");
|
||||||
broadcastIntent.setPackage(context.getPackageName());
|
broadcastIntent.setPackage(context.getPackageName());
|
||||||
context.sendBroadcast(broadcastIntent);
|
context.sendBroadcast(broadcastIntent);
|
||||||
return;
|
return;
|
||||||
|
@ -146,6 +146,8 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_SELECTION_BROADCAST;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_SELECTION_OFF;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_START_NON_WEAR_BROADCAST;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_START_NON_WEAR_BROADCAST;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_START_NON_WEAR_SELECTION;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_START_NON_WEAR_SELECTION;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_WOKE_UP_BROADCAST;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_WOKE_UP_BROADCAST;
|
||||||
@ -157,8 +159,6 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VI
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefIntValue;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefIntValue;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefStringValue;
|
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefStringValue;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL;
|
import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_SELECTION_BROADCAST;
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_SELECTION_OFF;
|
|
||||||
|
|
||||||
public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
|||||||
@Override
|
@Override
|
||||||
public boolean connectFirstTime() {
|
public boolean connectFirstTime() {
|
||||||
needsAuth = true;
|
needsAuth = true;
|
||||||
return super.connect();
|
return connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
private HuamiSupport sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
private HuamiSupport sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
||||||
|
@ -202,6 +202,7 @@ class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
final UUID UuidSDP = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
|
final UUID UuidSDP = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
|
||||||
mBtSocket = btDevice.createRfcommSocketToServiceRecord(UuidSDP);
|
mBtSocket = btDevice.createRfcommSocketToServiceRecord(UuidSDP);
|
||||||
|
|
||||||
|
// TODO: Why is this comment here?
|
||||||
//mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid());
|
//mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid());
|
||||||
mBtSocket.connect();
|
mBtSocket.connect();
|
||||||
mInStream = mBtSocket.getInputStream();
|
mInStream = mBtSocket.getInputStream();
|
||||||
|
@ -73,7 +73,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
|
|||||||
public void onInstallApp(Uri uri) {
|
public void onInstallApp(Uri uri) {
|
||||||
PebbleProtocol pebbleProtocol = (PebbleProtocol) getDeviceProtocol();
|
PebbleProtocol pebbleProtocol = (PebbleProtocol) getDeviceProtocol();
|
||||||
PebbleIoThread pebbleIoThread = getDeviceIOThread();
|
PebbleIoThread pebbleIoThread = getDeviceIOThread();
|
||||||
// catch fake urls first
|
// Catch fake URLs first
|
||||||
if (uri.equals(Uri.parse("fake://health"))) {
|
if (uri.equals(Uri.parse("fake://health"))) {
|
||||||
getDeviceIOThread().write(pebbleProtocol.encodeActivateHealth(true));
|
getDeviceIOThread().write(pebbleProtocol.encodeActivateHealth(true));
|
||||||
String units = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, getContext().getString(R.string.p_unit_metric));
|
String units = GBApplication.getPrefs().getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, getContext().getString(R.string.p_unit_metric));
|
||||||
|
@ -59,8 +59,8 @@ class PebbleGATTClient extends BluetoothGattCallback {
|
|||||||
private final PebbleLESupport mPebbleLESupport;
|
private final PebbleLESupport mPebbleLESupport;
|
||||||
|
|
||||||
private boolean oldPebble = false;
|
private boolean oldPebble = false;
|
||||||
private boolean doPairing = true;
|
private final boolean doPairing = true;
|
||||||
private boolean removeBond = false;
|
private final boolean removeBond = false;
|
||||||
private BluetoothGatt mBluetoothGatt;
|
private BluetoothGatt mBluetoothGatt;
|
||||||
|
|
||||||
private CountDownLatch mWaitWriteCompleteLatch;
|
private CountDownLatch mWaitWriteCompleteLatch;
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/* Copyright (C) 2020 Taavi Eomäe
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public interface BondingInterface {
|
||||||
|
/**
|
||||||
|
* Called when pairing is complete
|
||||||
|
**/
|
||||||
|
void onBondingComplete(boolean success);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the device that is currently being paired
|
||||||
|
**/
|
||||||
|
BluetoothDevice getCurrentTarget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This forces bonding activities to encapsulate the removal
|
||||||
|
* of all broadcast receivers on demand
|
||||||
|
**/
|
||||||
|
void unregisterBroadcastReceivers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This forces bonding activities to handle the addition
|
||||||
|
* of all broadcast receivers in the same place
|
||||||
|
**/
|
||||||
|
void removeBroadcastReceivers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just returns the Context
|
||||||
|
*/
|
||||||
|
Context getContext();
|
||||||
|
}
|
@ -0,0 +1,386 @@
|
|||||||
|
/* Copyright (C) 2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti, Taavi Eomäe
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.companion.AssociationRequest;
|
||||||
|
import android.companion.BluetoothDeviceFilter;
|
||||||
|
import android.companion.CompanionDeviceManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||||
|
|
||||||
|
import static androidx.core.app.ActivityCompat.startIntentSenderForResult;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast;
|
||||||
|
|
||||||
|
public class BondingUtil {
|
||||||
|
public static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate";
|
||||||
|
|
||||||
|
private static final int REQUEST_CODE = 1;
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BondingUtil.class);
|
||||||
|
private static final long DELAY_AFTER_BONDING = 1000; // 1s
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BroadcastReceiver that handles Gadgetbridge's device changed broadcasts
|
||||||
|
*/
|
||||||
|
public static BroadcastReceiver getPairingReceiver(final BondingInterface activity) {
|
||||||
|
return new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
||||||
|
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||||
|
LOG.debug("Pairing receiver: device changed: " + device);
|
||||||
|
if (activity.getCurrentTarget().getAddress().equals(device.getAddress())) {
|
||||||
|
if (device.isInitialized()) {
|
||||||
|
LOG.info("Device is initialized, finish things up");
|
||||||
|
activity.onBondingComplete(true);
|
||||||
|
} else if (device.isConnecting() || device.isInitializing()) {
|
||||||
|
LOG.info("Still connecting/initializing device...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BroadcastReceiver that handles Bluetooth chance broadcasts
|
||||||
|
*/
|
||||||
|
public static BroadcastReceiver getBondingReceiver(final BondingInterface bondingInterface) {
|
||||||
|
return new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
String bondingMacAddress = bondingInterface.getCurrentTarget().getAddress();
|
||||||
|
|
||||||
|
LOG.info("Bond state changed: " + device + ", state: " + device.getBondState() + ", expected address: " + bondingMacAddress);
|
||||||
|
if (bondingMacAddress != null && bondingMacAddress.equals(device.getAddress())) {
|
||||||
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
|
||||||
|
switch (bondState) {
|
||||||
|
case BluetoothDevice.BOND_BONDED: {
|
||||||
|
LOG.info("Bonded with " + device.getAddress());
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if (isLePebble(device)) {
|
||||||
|
// Do not initiate connection to LE Pebble!
|
||||||
|
} else {
|
||||||
|
attemptToFirstConnect(bondingInterface.getCurrentTarget());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case BluetoothDevice.BOND_NONE: {
|
||||||
|
LOG.info("Not bonded with " + device.getAddress() + ", attempting to connect anyway.");
|
||||||
|
attemptToFirstConnect(bondingInterface.getCurrentTarget());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case BluetoothDevice.BOND_BONDING: {
|
||||||
|
LOG.info("Bonding in progress with " + device.getAddress());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
LOG.warn("Unknown bond state for device " + device.getAddress() + ": " + bondState);
|
||||||
|
bondingInterface.onBondingComplete(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to candidate after a certain delay
|
||||||
|
*
|
||||||
|
* @param candidate the device to connect to
|
||||||
|
*/
|
||||||
|
public static void attemptToFirstConnect(final BluetoothDevice candidate) {
|
||||||
|
Looper mainLooper = Looper.getMainLooper();
|
||||||
|
new Handler(mainLooper).postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
GBApplication.deviceService().disconnect();
|
||||||
|
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(candidate);
|
||||||
|
connectToGBDevice(device);
|
||||||
|
}
|
||||||
|
}, DELAY_AFTER_BONDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just calls DeviceService connect with the "first time" flag
|
||||||
|
*/
|
||||||
|
private static void connectToGBDevice(GBDevice device) {
|
||||||
|
if (device != null) {
|
||||||
|
GBApplication.deviceService().connect(device, true);
|
||||||
|
} else {
|
||||||
|
GB.toast("Unable to connect, can't recognize the device type", Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if GB should pair
|
||||||
|
*/
|
||||||
|
public static boolean shouldUseBonding() {
|
||||||
|
// TODO: Migrate to generic "should even try bonding" preference key
|
||||||
|
|
||||||
|
// There are connection problems on certain Galaxy S devices at least
|
||||||
|
// try to connect without BT pairing (bonding)
|
||||||
|
Prefs prefs = GBApplication.getPrefs();
|
||||||
|
return prefs.getPreferences().getBoolean(MiBandConst.PREF_MIBAND_SETUP_BT_PAIRING, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the device and calls callback
|
||||||
|
*/
|
||||||
|
public static void connectThenComplete(BondingInterface bondingInterface, GBDeviceCandidate deviceCandidate) {
|
||||||
|
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
||||||
|
connectThenComplete(bondingInterface, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the device and calls callback
|
||||||
|
*/
|
||||||
|
public static void connectThenComplete(BondingInterface bondingInterface, GBDevice device) {
|
||||||
|
toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.discovery_trying_to_connect_to, device.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
||||||
|
// Disconnect when LE Pebble so that the user can manually initiate a connection
|
||||||
|
GBApplication.deviceService().disconnect();
|
||||||
|
GBApplication.deviceService().connect(device);
|
||||||
|
bondingInterface.onBondingComplete(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the type of bonding needed for the device and continues accordingly
|
||||||
|
*/
|
||||||
|
public static void initiateCorrectBonding(final BondingInterface bondingInterface, final GBDeviceCandidate deviceCandidate) {
|
||||||
|
int bondingStyle = DeviceHelper.getInstance().getCoordinator(deviceCandidate).getBondingStyle();
|
||||||
|
if (bondingStyle == DeviceCoordinator.BONDING_STYLE_NONE) {
|
||||||
|
// Do nothing
|
||||||
|
return;
|
||||||
|
} else if (bondingStyle == DeviceCoordinator.BONDING_STYLE_ASK) {
|
||||||
|
new AlertDialog.Builder(bondingInterface.getContext())
|
||||||
|
.setCancelable(true)
|
||||||
|
.setTitle(bondingInterface.getContext().getString(R.string.discovery_pair_title, deviceCandidate.getName()))
|
||||||
|
.setMessage(bondingInterface.getContext().getString(R.string.discovery_pair_question))
|
||||||
|
.setPositiveButton(bondingInterface.getContext().getString(R.string.discovery_yes_pair), new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
BondingUtil.tryBondThenComplete(bondingInterface, deviceCandidate);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.discovery_dont_pair, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
BondingUtil.connectThenComplete(bondingInterface, deviceCandidate);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
BondingUtil.tryBondThenComplete(bondingInterface, deviceCandidate);
|
||||||
|
}
|
||||||
|
LOG.debug("Bonding initiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to create a BluetoothDevice bond
|
||||||
|
* Do not call directly, use createBond(Activity, GBDeviceCandidate) instead!
|
||||||
|
*/
|
||||||
|
private static void bluetoothBond(BondingInterface context, BluetoothDevice device) {
|
||||||
|
if (device.createBond()) {
|
||||||
|
// Async, results will be delivered via a broadcast
|
||||||
|
LOG.info("Bonding in progress...");
|
||||||
|
} else {
|
||||||
|
toast(context.getContext(), context.getContext().getString(R.string.discovery_bonding_failed_immediately, device.getName()), Toast.LENGTH_SHORT, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the activity result and checks if there's anything CompanionDeviceManager-related going on
|
||||||
|
*/
|
||||||
|
public static void handleActivityResult(BondingInterface bondingInterface, int requestCode, int resultCode, Intent data) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||||
|
requestCode == BondingUtil.REQUEST_CODE &&
|
||||||
|
resultCode == Activity.RESULT_OK) {
|
||||||
|
|
||||||
|
BluetoothDevice deviceToPair =
|
||||||
|
data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE);
|
||||||
|
|
||||||
|
if (deviceToPair != null) {
|
||||||
|
if (bondingInterface.getCurrentTarget().getAddress().equals(deviceToPair.getAddress())) {
|
||||||
|
if (deviceToPair.getBondState() != BluetoothDevice.BOND_BONDED) {
|
||||||
|
BondingUtil.bluetoothBond(bondingInterface, bondingInterface.getCurrentTarget());
|
||||||
|
} else {
|
||||||
|
bondingInterface.onBondingComplete(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bondingInterface.onBondingComplete(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if device is LE Pebble
|
||||||
|
*/
|
||||||
|
public static boolean isLePebble(BluetoothDevice device) {
|
||||||
|
return (device.getType() == BluetoothDevice.DEVICE_TYPE_DUAL || device.getType() == BluetoothDevice.DEVICE_TYPE_LE) &&
|
||||||
|
(device.getName().startsWith("Pebble-LE ") || device.getName().startsWith("Pebble Time LE "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the CompanionDeviceManager bonding method
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private static void companionDeviceManagerBond(BondingInterface bondingInterface,
|
||||||
|
final GBDeviceCandidate deviceCandidate) {
|
||||||
|
BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder()
|
||||||
|
.setAddress(deviceCandidate.getMacAddress())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
AssociationRequest pairingRequest = new AssociationRequest.Builder()
|
||||||
|
.addDeviceFilter(deviceFilter)
|
||||||
|
.setSingleDevice(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
CompanionDeviceManager manager = (CompanionDeviceManager) bondingInterface.getContext().getSystemService(Context.COMPANION_DEVICE_SERVICE);
|
||||||
|
for (String association : manager.getAssociations()) {
|
||||||
|
if (association.equals(deviceCandidate.getMacAddress())) {
|
||||||
|
LOG.info("The device has already been bonded through CompanionDeviceManager, using regular");
|
||||||
|
// If it's already "associated", we should immediately pair
|
||||||
|
// because the callback is never called (AFAIK?)
|
||||||
|
BondingUtil.bluetoothBond(bondingInterface, deviceCandidate.getDevice());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.associate(pairingRequest,
|
||||||
|
getCompanionDeviceManagerCallback(bondingInterface),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a bit hacky, but it does stop a bonding that might be otherwise stuck,
|
||||||
|
* use with some caution
|
||||||
|
*/
|
||||||
|
public static void stopBluetoothBonding(BluetoothDevice device) {
|
||||||
|
try {
|
||||||
|
//noinspection JavaReflectionMemberAccess
|
||||||
|
device.getClass().getMethod("cancelBondProcess").invoke(device);
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalizes bonded device
|
||||||
|
*/
|
||||||
|
public static void handleDeviceBonded(BondingInterface bondingInterface, GBDeviceCandidate deviceCandidate) {
|
||||||
|
if (deviceCandidate == null) {
|
||||||
|
LOG.error("deviceCandidate was null! Can't handle bonded device!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.discovery_successfully_bonded, deviceCandidate.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
||||||
|
connectThenComplete(bondingInterface, deviceCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to initiate bonding to a GBDeviceCandidate
|
||||||
|
*/
|
||||||
|
public static void tryBondThenComplete(BondingInterface bondingInterface, GBDeviceCandidate deviceCandidate) {
|
||||||
|
bondingInterface.removeBroadcastReceivers();
|
||||||
|
BluetoothDevice device = deviceCandidate.getDevice();
|
||||||
|
|
||||||
|
int bondState = device.getBondState();
|
||||||
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
|
GB.toast(bondingInterface.getContext().getString(R.string.pairing_already_bonded, device.getName(), device.getAddress()), Toast.LENGTH_SHORT, GB.INFO);
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if (GBApplication.getPrefs().getBoolean("enable_companiondevice_pairing", true) &&
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
// If CompanionDeviceManager is enabled, skip connection and go bond
|
||||||
|
// TODO: It would theoretically be nice to check if it's already been granted,
|
||||||
|
// but re-bond works
|
||||||
|
} else {
|
||||||
|
attemptToFirstConnect(bondingInterface.getCurrentTarget());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (bondState == BluetoothDevice.BOND_BONDING) {
|
||||||
|
GB.toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.pairing_in_progress, device.getName(), device.getAddress()), Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB.toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.pairing_creating_bond_with, device.getName(), device.getAddress()), Toast.LENGTH_LONG, GB.INFO);
|
||||||
|
toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.discovery_attempting_to_pair, deviceCandidate.getName()), Toast.LENGTH_SHORT, GB.INFO);
|
||||||
|
if (GBApplication.getPrefs().getBoolean("enable_companiondevice_pairing", true) &&
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
companionDeviceManagerBond(bondingInterface, deviceCandidate);
|
||||||
|
} else {
|
||||||
|
bluetoothBond(bondingInterface, deviceCandidate.getDevice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a callback for CompanionDeviceManager
|
||||||
|
*
|
||||||
|
* @param bondingInterface the activity that started the CDM bonding process
|
||||||
|
* @return CompanionDeviceManager.Callback that handles the CompanionDeviceManager bonding process results
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private static CompanionDeviceManager.Callback getCompanionDeviceManagerCallback(final BondingInterface bondingInterface) {
|
||||||
|
return new CompanionDeviceManager.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(CharSequence error) {
|
||||||
|
toast(bondingInterface.getContext(), bondingInterface.getContext().getString(R.string.discovery_bonding_failed_immediately), Toast.LENGTH_SHORT, GB.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceFound(IntentSender chooserLauncher) {
|
||||||
|
try {
|
||||||
|
startIntentSenderForResult((Activity) bondingInterface.getContext(),
|
||||||
|
chooserLauncher,
|
||||||
|
REQUEST_CODE,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
null);
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
LOG.error(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user