Skip to content
This repository has been archived by the owner on Dec 23, 2024. It is now read-only.

Improve Casio device support #1417

Closed
wants to merge 12 commits into from
7 changes: 7 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,13 @@
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
</receiver>
<receiver
android:name=".externalevents.BluetoothScanCallbackReceiver"
android:exported="false">
<intent-filter>
<action android:name="nodomain.freeyourgadget.gadgetbridge.blescancallback" />
</intent-filter>
</receiver>
<receiver
android:name=".service.receivers.GBMusicControlReceiver"
android:exported="false">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* Copyright (C) 2019 Andreas Böhler

This file is part of Gadgetbridge.

Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.externalevents;

import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;


import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;

public class BluetoothScanCallbackReceiver extends BroadcastReceiver {

private static final Logger LOG = LoggerFactory.getLogger(BluetoothScanCallbackReceiver.class);
private String mSeenScanCallbackUUID = "";

@TargetApi(Build.VERSION_CODES.O)
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(!action.equals("nodomain.freeyourgadget.gadgetbridge.blescancallback") || !intent.hasExtra("address") || !intent.hasExtra("uuid")) {
return;
}

String wantedAddress = intent.getExtras().getString("address");
String uuid = intent.getExtras().getString("uuid");

int bleCallbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1);
if(bleCallbackType != -1) {
//LOG.debug("Passive background scan callback type: " + bleCallbackType);
ArrayList<ScanResult> scanResults = intent.getParcelableArrayListExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT);
for(ScanResult result: scanResults) {
BluetoothDevice device = result.getDevice();
if(device.getAddress().equals(wantedAddress) && !mSeenScanCallbackUUID.equals(uuid)) {
mSeenScanCallbackUUID = uuid;
LOG.info("ScanCallbackReceiver has found " + device.getAddress() + "(" + device.getName() + ")");
BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner().stopScan(getScanCallbackIntent(GBApplication.getContext(), wantedAddress, uuid));
GBApplication.deviceService().connect(DeviceHelper.getInstance().toSupportedDevice(device));

}
}
}
}

@TargetApi(Build.VERSION_CODES.O)
public static PendingIntent getScanCallbackIntent(Context context, String address, String uuid) {
Intent intent = new Intent(context, BluetoothScanCallbackReceiver.class);
intent.setAction("nodomain.freeyourgadget.gadgetbridge.blescancallback");
intent.putExtra("address", address);
intent.putExtra("uuid", uuid);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.btle;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
Expand All @@ -33,27 +34,30 @@
import java.util.Set;
import java.util.UUID;

import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;

/**
* Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka
* Bluetooth Smart.
* <p/>
* The connection to the device and all communication is made with a generic {@link BtLEQueue}.
* Messages to the device are encoded as {@link BtLEAction actions} that are grouped with a
* {@link Transaction} and sent via {@link BtLEQueue}.
* Messages to the device are encoded as {@link BtLEAction actions} or {@link BtLEServerAction actions}
* that are grouped with a {@link Transaction} or {@link ServerTransaction} and sent via {@link BtLEQueue}.
*
* @see TransactionBuilder
* @see BtLEQueue
*/
public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback {
public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport implements GattCallback, GattServerCallback {
private BtLEQueue mQueue;
private Map<UUID, BluetoothGattCharacteristic> mAvailableCharacteristics;
private final Set<UUID> mSupportedServices = new HashSet<>(4);
private final Set<BluetoothGattService> mSupportedServerServices = new HashSet<>(4);
private Logger logger;

private final List<AbstractBleProfile<?>> mSupportedProfiles = new ArrayList<>();
Expand All @@ -70,8 +74,16 @@ public AbstractBTLEDeviceSupport(Logger logger) {
@Override
public boolean connect() {
if (mQueue == null) {
mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, getContext());
mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, this, getContext(), mSupportedServerServices);
mQueue.setAutoReconnect(getAutoReconnect());
GBPrefs prefs = GBApplication.getGBPrefs();
boolean autoReconnectScan = GBPrefs.AUTO_RECONNECT_SCAN_DEFAULT;
if (prefs != null) {
autoReconnectScan = prefs.getAutoReconnectScan();
}
// Override the user preference if required by the device
autoReconnectScan = autoReconnectScan || useBleScannerForReconnect();
mQueue.setBleScannerForReconnect(autoReconnectScan);
}
return mQueue.connect();
}
Expand Down Expand Up @@ -136,6 +148,19 @@ public TransactionBuilder performInitialized(String taskName) throws IOException
return createTransactionBuilder(taskName);
}

public ServerTransactionBuilder createServerTransactionBuilder(String taskName) {
return new ServerTransactionBuilder(taskName);
}

public ServerTransactionBuilder performServer(String taskName) throws IOException {
if (!isConnected()) {
if(!connect()) {
throw new IOException("1: Unable to connect to device: " + getDevice());
}
}
return createServerTransactionBuilder(taskName);
}

/**
* Ensures that the device is connected and (only then) performs the actions of the given
* transaction builder.
Expand Down Expand Up @@ -187,6 +212,14 @@ protected void addSupportedProfile(AbstractBleProfile<?> profile) {
mSupportedProfiles.add(profile);
}

/**
* Subclasses should call this method to add server services they support.
* @param service
*/
protected void addSupportedServerService(BluetoothGattService service) {
mSupportedServerServices.add(service);
}

/**
* Returns the characteristic matching the given UUID. Only characteristics
* are returned whose service is marked as supported.
Expand Down Expand Up @@ -337,4 +370,33 @@ public void onSetFmFrequency(float frequency) {
public void onSetLedColor(int color) {

}

@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {

}

@Override
public boolean onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
return false;
}

@Override
public boolean onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
return false;
}

@Override
public boolean onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
return false;
}

@Override
public boolean onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
return false;
}

public boolean useBleScannerForReconnect() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Andreas Boehler

This file is part of Gadgetbridge.

Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.btle;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

public abstract class AbstractTransaction {
private final String mName;
private final long creationTimestamp = System.currentTimeMillis();

public AbstractTransaction(String taskName) {
this.mName = taskName;
}

public String getTaskName() {
return mName;
}

protected String getCreationTime() {
return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new Date(creationTimestamp));
}

public abstract int getActionCount();

@Override
public String toString() {
return String.format(Locale.US, "%s: Transaction task: %s with %d actions", getCreationTime(), getTaskName(), getActionCount());
}

}
Loading