mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-06-21 04:20:27 +02:00
Add BTBR service
This commit is contained in:
parent
b2aa61e182
commit
be575af314
|
@ -0,0 +1,155 @@
|
||||||
|
/* Copyright (C) 2022 Damien Gaignon
|
||||||
|
|
||||||
|
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.btbr;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.CheckInitializedAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for devices connected through a serial protocol, like RFCOMM BT or TCP socket.
|
||||||
|
* <p/>
|
||||||
|
* The connection to the device and all communication is made with a generic {@link BtClassicIo}.
|
||||||
|
* Messages to the device are encoded
|
||||||
|
* sent via {@link BtClassicIo}.
|
||||||
|
*
|
||||||
|
* @see BtClassicIo
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBTBRDeviceSupport extends AbstractDeviceSupport implements SocketCallback {
|
||||||
|
private BtBRQueue mQueue;
|
||||||
|
private UUID mSupportedService = null;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public AbstractBTBRDeviceSupport(Logger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
if (logger == null) {
|
||||||
|
throw new IllegalArgumentException("logger must not be null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean connect() {
|
||||||
|
if (mQueue == null) {
|
||||||
|
mQueue = new BtBRQueue(getBluetoothAdapter(), getDevice(), getContext(), this, getSupportedService());
|
||||||
|
}
|
||||||
|
return mQueue.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses should populate the given builder to initialize the device (if necessary).
|
||||||
|
*
|
||||||
|
* @return the same builder as passed as the argument
|
||||||
|
*/
|
||||||
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
if (mQueue != null) {
|
||||||
|
mQueue.dispose();
|
||||||
|
mQueue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||||
|
return new TransactionBuilder(taskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the device is connected and (only then) performs the actions of the given
|
||||||
|
* transaction builder.
|
||||||
|
*
|
||||||
|
* In contrast to {@link #performInitialized(String)}, no initialization sequence is performed
|
||||||
|
* with the device, only the actions of the given builder are executed.
|
||||||
|
* @param transaction
|
||||||
|
* @throws IOException
|
||||||
|
* @see {@link #performInitialized(String)}
|
||||||
|
*/
|
||||||
|
public void performConnected(Transaction transaction) throws IOException {
|
||||||
|
if (!isConnected()) {
|
||||||
|
if (!connect()) {
|
||||||
|
throw new IOException("2: Unable to connect to device: " + getDevice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getQueue().add(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BtBRQueue getQueue() {
|
||||||
|
return mQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses should call this method to add services they support.
|
||||||
|
* Only supported services will be queried for characteristics.
|
||||||
|
*
|
||||||
|
* @param aSupportedService
|
||||||
|
*/
|
||||||
|
protected void addSupportedService(UUID aSupportedService) {
|
||||||
|
mSupportedService = aSupportedService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UUID getSupportedService() {
|
||||||
|
return mSupportedService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method that may be used to log incoming messages when we don't know how to deal with them yet.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void logMessageContent(byte[] value) {
|
||||||
|
logger.info("RECEIVED DATA WITH LENGTH: " + ((value != null) ? value.length : "(null)"));
|
||||||
|
Logging.logBytes(logger, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onConnectionEstablished() {
|
||||||
|
initializeDevice(createTransactionBuilder("Initializing device")).queue(getQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFmFrequency(float frequency) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetLedColor(int color) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetGpsLocation(Location location) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetWorldClocks(ArrayList<? extends WorldClock> clocks) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPowerOff() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetPhoneVolume(final float volume) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetReminders(ArrayList<? extends Reminder> reminders) {}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Böhler, Andreas Shimokawa, Carsten
|
||||||
|
Pfeiffer, Daniele Gobbetti
|
||||||
|
|
||||||
|
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.btbr;
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btbr;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows performing one socket request at a time.
|
||||||
|
* As they are asynchronous anyway, we encapsulate every socket request (only write)
|
||||||
|
* inside a runnable action.
|
||||||
|
* <p/>
|
||||||
|
* These actions are then executed one after another, ensuring that every action's result
|
||||||
|
* has been posted before invoking the next action.
|
||||||
|
* <p/>
|
||||||
|
* As there is only write action, this class will be needed for later usage.
|
||||||
|
*/
|
||||||
|
public abstract class BtBRAction {
|
||||||
|
private final long creationTimestamp;
|
||||||
|
|
||||||
|
public BtBRAction() {
|
||||||
|
creationTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this action expects an (async) result which must
|
||||||
|
* be waited for, before continuing with other actions.
|
||||||
|
* <p/>
|
||||||
|
*/
|
||||||
|
public abstract boolean expectsResult();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes this action, e.g. reads or write a Socket characteristic.
|
||||||
|
*
|
||||||
|
* @param socket the characteristic to manipulate, or null if none.
|
||||||
|
* @return true if the action was successful, false otherwise
|
||||||
|
*/
|
||||||
|
public abstract boolean run(BluetoothSocket socket);
|
||||||
|
|
||||||
|
protected String getCreationTime() {
|
||||||
|
return DateTimeUtils.formatDateTime(new Date(creationTimestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return getCreationTime() + ": " + getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
/* Copyright (C) 2022 Damien Gaignon
|
||||||
|
*
|
||||||
|
* 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.btbr;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||||
|
|
||||||
|
public final class BtBRQueue {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BtBRQueue.class);
|
||||||
|
|
||||||
|
private BluetoothAdapter mBtAdapter = null;
|
||||||
|
private BluetoothSocket mBtSocket = null;
|
||||||
|
private GBDevice mGbDevice;
|
||||||
|
private SocketCallback mCallback;
|
||||||
|
private UUID mService;
|
||||||
|
|
||||||
|
private final BlockingQueue<AbstractTransaction> mTransactions = new LinkedBlockingQueue<>();
|
||||||
|
private volatile boolean mDisposed;
|
||||||
|
private volatile boolean mCrashed;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private CountDownLatch mConnectionLatch;
|
||||||
|
private CountDownLatch mAvailableData;
|
||||||
|
|
||||||
|
private Thread writeThread = new Thread("Gadgetbridge IO writeThread") {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LOG.debug("Socket Write Thread started.");
|
||||||
|
|
||||||
|
while (!mDisposed && !mCrashed) {
|
||||||
|
try {
|
||||||
|
AbstractTransaction qTransaction = mTransactions.take();
|
||||||
|
if (!isConnected()) {
|
||||||
|
LOG.debug("Not connected, waiting for connection...");
|
||||||
|
setDeviceConnectionState(GBDevice.State.NOT_CONNECTED);
|
||||||
|
// wait until the connection succeeds before running the actions
|
||||||
|
// Note that no automatic connection is performed. This has to be triggered
|
||||||
|
// on the outside typically by the DeviceSupport. The reason is that
|
||||||
|
// devices have different kinds of initializations and this class has no
|
||||||
|
// idea about them.
|
||||||
|
mConnectionLatch = new CountDownLatch(1);
|
||||||
|
mConnectionLatch.await();
|
||||||
|
mConnectionLatch = null;
|
||||||
|
}
|
||||||
|
LOG.info("Ready for a new message exchange.");
|
||||||
|
Transaction transaction = (Transaction)qTransaction;
|
||||||
|
for (BtBRAction action : transaction.getActions()) {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("About to run action: " + action);
|
||||||
|
}
|
||||||
|
if (action.run(mBtSocket)) {
|
||||||
|
LOG.debug("Action ok: " + action);
|
||||||
|
} else {
|
||||||
|
LOG.error("Action returned false: " + action);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
mConnectionLatch = null;
|
||||||
|
LOG.debug("Thread interrupted");
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
LOG.error("IO Write Thread died: " + ex.getMessage(), ex);
|
||||||
|
mCrashed = true;
|
||||||
|
mConnectionLatch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Thread readThread = new Thread("Gadgetbridge IO readThread") {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LOG.debug("Queue Read Thread started.");
|
||||||
|
while (!mDisposed && !mCrashed) {
|
||||||
|
try {
|
||||||
|
if (!isConnected()) {
|
||||||
|
LOG.debug("not connected, waiting for connection...");
|
||||||
|
// wait until the connection succeeds before running the actions
|
||||||
|
// Note that no automatic connection is performed. This has to be triggered
|
||||||
|
// on the outside typically by the DeviceSupport. The reason is that
|
||||||
|
// devices have different kinds of initializations and this class has no
|
||||||
|
// idea about them.
|
||||||
|
mConnectionLatch = new CountDownLatch(1);
|
||||||
|
mConnectionLatch.await();
|
||||||
|
mConnectionLatch = null;
|
||||||
|
}
|
||||||
|
if (mAvailableData != null) {
|
||||||
|
if (mBtSocket.getInputStream().available() == 0) {
|
||||||
|
mAvailableData.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
int len = mBtSocket.getInputStream().read(data);
|
||||||
|
LOG.debug("Received data: " + StringUtils.bytesToHex(data));
|
||||||
|
mCallback.onSocketRead(Arrays.copyOf(data, len));
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
mConnectionLatch = null;
|
||||||
|
LOG.debug("Thread interrupted");
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
LOG.error("IO Read Thread died: " + ex.getMessage(), ex);
|
||||||
|
mCrashed = true;
|
||||||
|
mConnectionLatch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public BtBRQueue(BluetoothAdapter btAdapter, GBDevice gbDevice, Context context, SocketCallback socketCallback, UUID supportedService) {
|
||||||
|
mBtAdapter = btAdapter;
|
||||||
|
mGbDevice = gbDevice;
|
||||||
|
mContext = context;
|
||||||
|
mCallback = socketCallback;
|
||||||
|
mService = supportedService;
|
||||||
|
|
||||||
|
writeThread.start();
|
||||||
|
readThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to the given remote device. Note that this does not perform any device
|
||||||
|
* specific initialization. This should be done in the specific {@link DeviceSupport}
|
||||||
|
* class.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> whether the connection attempt was successfully triggered and <code>false</code> if that failed or if there is already a connection
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected boolean connect() {
|
||||||
|
if (isConnected()) {
|
||||||
|
LOG.warn("Ignoring connect() because already connected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Attemping to connect to " + mGbDevice.getName());
|
||||||
|
GBDevice.State originalState = mGbDevice.getState();
|
||||||
|
setDeviceConnectionState(GBDevice.State.CONNECTING);
|
||||||
|
|
||||||
|
try {
|
||||||
|
BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(mGbDevice.getAddress());
|
||||||
|
// UUID should be in a BluetoothSocket class and not in BluetoothSocketCharacteristic
|
||||||
|
mBtSocket = btDevice.createRfcommSocketToServiceRecord(mService);
|
||||||
|
mBtSocket.connect();
|
||||||
|
if (mBtSocket.isConnected()) {
|
||||||
|
setDeviceConnectionState(GBDevice.State.CONNECTED);
|
||||||
|
} else {
|
||||||
|
LOG.debug("Connection not established");
|
||||||
|
}
|
||||||
|
if (mConnectionLatch != null) {
|
||||||
|
mConnectionLatch.countDown();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Server socket cannot be started.", e);
|
||||||
|
setDeviceConnectionState(originalState);
|
||||||
|
mBtSocket = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnectionEstablished();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onConnectionEstablished() {
|
||||||
|
mCallback.onConnectionEstablished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
if (mBtSocket != null) {
|
||||||
|
try {
|
||||||
|
mAvailableData = new CountDownLatch(1);
|
||||||
|
mAvailableData.await();
|
||||||
|
mAvailableData = null;
|
||||||
|
mBtSocket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isConnected() {
|
||||||
|
return mGbDevice.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a transaction to the end of the queue.
|
||||||
|
*
|
||||||
|
* @param transaction
|
||||||
|
*/
|
||||||
|
public void add(Transaction transaction) {
|
||||||
|
LOG.debug("about to add: " + transaction);
|
||||||
|
if (!transaction.isEmpty()) {
|
||||||
|
mTransactions.add(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDeviceConnectionState(GBDevice.State newState) {
|
||||||
|
LOG.debug("New device connection state: " + newState);
|
||||||
|
mGbDevice.setState(newState);
|
||||||
|
mGbDevice.sendDeviceUpdateIntent(mContext, GBDevice.DeviceUpdateSubject.DEVICE_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
if (mDisposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDisposed = true;
|
||||||
|
disconnect();
|
||||||
|
writeThread.interrupt();
|
||||||
|
writeThread = null;
|
||||||
|
readThread.interrupt();
|
||||||
|
readThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/** Copyright (C) 2022 Damien Gaignon
|
||||||
|
*
|
||||||
|
* 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.btbr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface handling io events of a BluetoothSocket.
|
||||||
|
* It is the counterpart of GattCallback interface designed for GB.
|
||||||
|
*/
|
||||||
|
public interface SocketCallback {
|
||||||
|
|
||||||
|
void onConnectionEstablished();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from InputStream of BluetoothSocket
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
void onSocketRead(byte[] data);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Böhler, Andreas Shimokawa, Carsten
|
||||||
|
Pfeiffer, Daniele Gobbetti
|
||||||
|
|
||||||
|
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.btbr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups a bunch of {@link BtBRAction actions} together, making sure
|
||||||
|
* that upon failure of one action, all subsequent actions are discarded.
|
||||||
|
*
|
||||||
|
* @author TREND
|
||||||
|
*/
|
||||||
|
public class Transaction extends AbstractTransaction {
|
||||||
|
private final List<BtBRAction> mActions = new ArrayList<>(4);
|
||||||
|
private
|
||||||
|
@Nullable
|
||||||
|
SocketCallback socketCallback;
|
||||||
|
|
||||||
|
public Transaction(String taskName) {
|
||||||
|
super(taskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(BtBRAction action) {
|
||||||
|
mActions.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BtBRAction> getActions() {
|
||||||
|
return Collections.unmodifiableList(mActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return mActions.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(@Nullable SocketCallback callback) {
|
||||||
|
socketCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the GattCallback for this transaction, or null if none.
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
@Nullable
|
||||||
|
SocketCallback getSocketCallback() {
|
||||||
|
return socketCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getActionCount() {
|
||||||
|
return mActions.size();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* Copyright (C) 2022 Damien Gaignon
|
||||||
|
|
||||||
|
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.btbr;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.WaitAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.WriteAction;
|
||||||
|
|
||||||
|
public class TransactionBuilder {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TransactionBuilder.class);
|
||||||
|
|
||||||
|
private final Transaction mTransaction;
|
||||||
|
private boolean mQueued;
|
||||||
|
|
||||||
|
public TransactionBuilder(String taskName) {
|
||||||
|
mTransaction = new Transaction(taskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransactionBuilder write(byte[] data) {
|
||||||
|
WriteAction action = new WriteAction(data);
|
||||||
|
return add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the queue to sleep for the specified time.
|
||||||
|
* Note that this is usually a bad idea, since it will not be able to process messages
|
||||||
|
* during that time. It is also likely to cause race conditions.
|
||||||
|
* @param millis the number of milliseconds to sleep
|
||||||
|
*/
|
||||||
|
public TransactionBuilder wait(int millis) {
|
||||||
|
WaitAction action = new WaitAction(millis);
|
||||||
|
return add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransactionBuilder add(BtBRAction action) {
|
||||||
|
mTransaction.add(action);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a SocketCallback instance that will be called when the transaction is executed,
|
||||||
|
* resulting in SocketCallback events.
|
||||||
|
*
|
||||||
|
* @param callback the callback to set, may be null
|
||||||
|
*/
|
||||||
|
public void setCallback(@Nullable SocketCallback callback) {
|
||||||
|
mTransaction.setCallback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be used as the final step to execute the transaction by the given queue.
|
||||||
|
*
|
||||||
|
* @param queue
|
||||||
|
*/
|
||||||
|
public void queue(BtBRQueue queue) {
|
||||||
|
if (mQueued) {
|
||||||
|
throw new IllegalStateException("This builder had already been queued. You must not reuse it.");
|
||||||
|
}
|
||||||
|
mQueued = true;
|
||||||
|
queue.add(mTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction getTransaction() {
|
||||||
|
return mTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special action that checks for an abort-condition, and if met, the currently
|
||||||
|
* executing transaction will be aborted by returning false.
|
||||||
|
*/
|
||||||
|
public abstract class AbortTransactionAction extends PlainAction {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AbortTransactionAction.class);
|
||||||
|
|
||||||
|
public AbortTransactionAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothSocket socket) {
|
||||||
|
if (shouldAbort()) {
|
||||||
|
LOG.info("Aborting transaction because abort criteria met.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean shouldAbort();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getCreationTime() + ": " + getClass().getSimpleName() + ": aborting? " + shouldAbort();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special action that is executed at the very front of the initialization
|
||||||
|
* sequence (transaction). It will abort the entire initialization sequence
|
||||||
|
* by returning false, when the device is already initialized.
|
||||||
|
*/
|
||||||
|
public class CheckInitializedAction extends AbortTransactionAction {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CheckInitializedAction.class);
|
||||||
|
|
||||||
|
private final GBDevice device;
|
||||||
|
|
||||||
|
public CheckInitializedAction(GBDevice gbDevice) {
|
||||||
|
device = gbDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldAbort() {
|
||||||
|
boolean abort = device.isInitialized();
|
||||||
|
if (abort) {
|
||||||
|
LOG.info("Aborting device initialization, because already initialized: " + device);
|
||||||
|
}
|
||||||
|
return abort;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btbr.BtBRAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract non-BTBR action. It performs no bluetooth operation,
|
||||||
|
* does not have a BluetoothSocketCharacteristic instance and expects no result.
|
||||||
|
*/
|
||||||
|
public abstract class PlainAction extends BtBRAction {
|
||||||
|
|
||||||
|
public PlainAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean expectsResult() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getCreationTime() + ": " + getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
public class SetDeviceBusyAction extends PlainAction {
|
||||||
|
private final GBDevice device;
|
||||||
|
private final Context context;
|
||||||
|
private final String busyTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When run, will mark the device as busy (or not busy).
|
||||||
|
*
|
||||||
|
* @param device the device to mark
|
||||||
|
* @param busyTask the task name to set as busy task, or null to mark as not busy
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
public SetDeviceBusyAction(GBDevice device, String busyTask, Context context) {
|
||||||
|
this.device = device;
|
||||||
|
this.busyTask = busyTask;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothSocket socket) {
|
||||||
|
device.setBusyTask(busyTask);
|
||||||
|
device.sendDeviceUpdateIntent(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getCreationTime() + ": " + getClass().getName() + ": " + busyTask;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
public class SetDeviceStateAction extends PlainAction {
|
||||||
|
private final GBDevice device;
|
||||||
|
private final GBDevice.State deviceState;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public SetDeviceStateAction(GBDevice device, GBDevice.State deviceState, Context context) {
|
||||||
|
this.device = device;
|
||||||
|
this.deviceState = deviceState;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothSocket socket) {
|
||||||
|
device.setState(deviceState);
|
||||||
|
device.sendDeviceUpdateIntent(getContext(), GBDevice.DeviceUpdateSubject.DEVICE_STATE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " to " + deviceState;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
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.btbr.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that will cause the queue to sleep for the specified time.
|
||||||
|
* Note that this is usually a bad idea, since it will not be able to process messages
|
||||||
|
* during that time. It is also likely to cause race conditions.
|
||||||
|
*/
|
||||||
|
public class WaitAction extends PlainAction {
|
||||||
|
|
||||||
|
private final int mMillis;
|
||||||
|
|
||||||
|
public WaitAction(int millis) {
|
||||||
|
mMillis = millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothSocket socket) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(mMillis);
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* Copyright (C) 2015-2021 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||||
|
Gobbetti, Uwe Hermann
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.btbr.actions;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.Logging;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.btbr.BtBRAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes a write operation on a given socket.
|
||||||
|
* The result status will be made available asynchronously through the
|
||||||
|
* {@link SocketCallback}
|
||||||
|
*/
|
||||||
|
public class WriteAction extends BtBRAction {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(WriteAction.class);
|
||||||
|
|
||||||
|
private final byte[] value;
|
||||||
|
private OutputStream mOutputStream = null;
|
||||||
|
|
||||||
|
public WriteAction(byte[] value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean run(BluetoothSocket socket) {
|
||||||
|
try {
|
||||||
|
mOutputStream = socket.getOutputStream();
|
||||||
|
if (mOutputStream == null) {
|
||||||
|
LOG.error("mOutStream is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return writeValue(value);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Can not get the output stream");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean writeValue(byte[] value) {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("writing to socket: " + Logging.formatBytes(value));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mOutputStream.write(value);
|
||||||
|
mOutputStream.flush();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Error writing to socket: ", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final byte[] getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean expectsResult() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,13 +105,13 @@ public abstract class AbstractBTLEOperation<T extends AbstractBTLEDeviceSupport>
|
||||||
*/
|
*/
|
||||||
public TransactionBuilder performInitialized(String taskName) throws IOException {
|
public TransactionBuilder performInitialized(String taskName) throws IOException {
|
||||||
TransactionBuilder builder = mSupport.performInitialized(taskName);
|
TransactionBuilder builder = mSupport.performInitialized(taskName);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionBuilder createTransactionBuilder(String taskName) {
|
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
|
TransactionBuilder builder = getSupport().createTransactionBuilder(taskName);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class ServerTransaction extends AbstractTransaction {
|
||||||
return String.format(Locale.US, "%s: Transaction task: %s with %d actions", getCreationTime(), getTaskName(), mActions.size());
|
return String.format(Locale.US, "%s: Transaction task: %s with %d actions", getCreationTime(), getTaskName(), mActions.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGattCallback(@Nullable GattServerCallback callback) {
|
public void setCallback(@Nullable GattServerCallback callback) {
|
||||||
gattCallback = callback;
|
gattCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@ public class ServerTransactionBuilder {
|
||||||
*
|
*
|
||||||
* @param callback the callback to set, may be null
|
* @param callback the callback to set, may be null
|
||||||
*/
|
*/
|
||||||
public void setGattCallback(@Nullable GattServerCallback callback) {
|
public void setCallback(@Nullable GattServerCallback callback) {
|
||||||
mTransaction.setGattCallback(callback);
|
mTransaction.setCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class Transaction extends AbstractTransaction {
|
||||||
return mActions.isEmpty();
|
return mActions.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGattCallback(@Nullable GattCallback callback) {
|
public void setCallback(@Nullable GattCallback callback) {
|
||||||
gattCallback = callback;
|
gattCallback = callback;
|
||||||
modifyGattCallback = true;
|
modifyGattCallback = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,8 +108,8 @@ public class TransactionBuilder {
|
||||||
*
|
*
|
||||||
* @param callback the callback to set, may be null
|
* @param callback the callback to set, may be null
|
||||||
*/
|
*/
|
||||||
public void setGattCallback(@Nullable GattCallback callback) {
|
public void setCallback(@Nullable GattCallback callback) {
|
||||||
mTransaction.setGattCallback(callback);
|
mTransaction.setCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
|
@ -88,7 +88,7 @@ public abstract class AbstractBleProfile<T extends AbstractBTLEDeviceSupport> ex
|
||||||
*/
|
*/
|
||||||
public TransactionBuilder performInitialized(String taskName) throws IOException {
|
public TransactionBuilder performInitialized(String taskName) throws IOException {
|
||||||
TransactionBuilder builder = mSupport.performInitialized(taskName);
|
TransactionBuilder builder = mSupport.performInitialized(taskName);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
rxCharacteristic = getCharacteristic(BangleJSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX);
|
rxCharacteristic = getCharacteristic(BangleJSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX);
|
||||||
txCharacteristic = getCharacteristic(BangleJSConstants.UUID_CHARACTERISTIC_NORDIC_UART_TX);
|
txCharacteristic = getCharacteristic(BangleJSConstants.UUID_CHARACTERISTIC_NORDIC_UART_TX);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(rxCharacteristic, true);
|
builder.notify(rxCharacteristic, true);
|
||||||
|
|
||||||
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
|
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
|
||||||
|
|
|
@ -171,7 +171,7 @@ public class CasioGB6900DeviceSupport extends CasioSupport {
|
||||||
getDevice().setFirmwareVersion2("N/A");
|
getDevice().setFirmwareVersion2("N/A");
|
||||||
|
|
||||||
|
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
|
|
||||||
configureWatch(builder);
|
configureWatch(builder);
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class FetchStepCountDataOperation extends AbstractBTLEOperation<CasioGBX
|
||||||
private void enableRequiredNotifications(boolean enable) {
|
private void enableRequiredNotifications(boolean enable) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("enableRequiredNotifications");
|
TransactionBuilder builder = performInitialized("enableRequiredNotifications");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), enable);
|
builder.notify(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), enable);
|
||||||
builder.notify(getCharacteristic(CasioConstants.CASIO_CONVOY_CHARACTERISTIC_UUID), enable);
|
builder.notify(getCharacteristic(CasioConstants.CASIO_CONVOY_CHARACTERISTIC_UUID), enable);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
|
@ -65,7 +65,7 @@ public class FetchStepCountDataOperation extends AbstractBTLEOperation<CasioGBX
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("requestStepCountDate");
|
TransactionBuilder builder = performInitialized("requestStepCountDate");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.write(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), command);
|
builder.write(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
@ -78,7 +78,7 @@ public class FetchStepCountDataOperation extends AbstractBTLEOperation<CasioGBX
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("writeStepCountAck");
|
TransactionBuilder builder = performInitialized("writeStepCountAck");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.write(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), command);
|
builder.write(getCharacteristic(CasioConstants.CASIO_DATA_REQUEST_SP_CHARACTERISTIC_UUID), command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
@ -110,7 +110,7 @@ public class FetchStepCountDataOperation extends AbstractBTLEOperation<CasioGBX
|
||||||
if (getDevice() != null) {
|
if (getDevice() != null) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("finished operation");
|
TransactionBuilder builder = performInitialized("finished operation");
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.wait(0);
|
builder.wait(0);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class GetConfigurationOperation extends AbstractBTLEOperation<CasioGBX100
|
||||||
byte[] command = new byte[1];
|
byte[] command = new byte[1];
|
||||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
||||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, command);
|
support.writeAllFeaturesRequest(builder, command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ public class GetConfigurationOperation extends AbstractBTLEOperation<CasioGBX100
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("finished operation");
|
TransactionBuilder builder = performInitialized("finished operation");
|
||||||
builder.wait(0);
|
builder.wait(0);
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOG.info("Error resetting Gatt callback: " + ex.getMessage());
|
LOG.info("Error resetting Gatt callback: " + ex.getMessage());
|
||||||
|
@ -88,7 +88,7 @@ public class GetConfigurationOperation extends AbstractBTLEOperation<CasioGBX100
|
||||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_BASIC");
|
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_BASIC");
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, command);
|
support.writeAllFeaturesRequest(builder, command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
|
|
@ -42,14 +42,14 @@ public class InitOperationGB6900 extends AbstractBTLEOperation<CasioGB6900Device
|
||||||
public InitOperationGB6900(CasioGB6900DeviceSupport support, TransactionBuilder builder) {
|
public InitOperationGB6900(CasioGB6900DeviceSupport support, TransactionBuilder builder) {
|
||||||
super(support);
|
super(support);
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPerform() throws IOException {
|
protected void doPerform() throws IOException {
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder("readBleSettings");
|
TransactionBuilder builder = getSupport().createTransactionBuilder("readBleSettings");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.read(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID));
|
builder.read(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID));
|
||||||
getSupport().performImmediately(builder);
|
getSupport().performImmediately(builder);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ public class InitOperationGB6900 extends AbstractBTLEOperation<CasioGB6900Device
|
||||||
private void writeBleSettings() {
|
private void writeBleSettings() {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder("writeBleInit");
|
TransactionBuilder builder = getSupport().createTransactionBuilder("writeBleInit");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.write(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID), mBleSettings);
|
builder.write(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID), mBleSettings);
|
||||||
getSupport().performImmediately(builder);
|
getSupport().performImmediately(builder);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
|
|
@ -48,13 +48,13 @@ public class InitOperationGBX100 extends AbstractBTLEOperation<CasioGBX100Device
|
||||||
this.support = support;
|
this.support = support;
|
||||||
this.mFirstConnect = firstConnect;
|
this.mFirstConnect = firstConnect;
|
||||||
LOG.info("mFirstConnect: " + mFirstConnect);
|
LOG.info("mFirstConnect: " + mFirstConnect);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeAllFeaturesRequest(byte[] arr) {
|
private void writeAllFeaturesRequest(byte[] arr) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = createTransactionBuilder("writeAllFeaturesRequest");
|
TransactionBuilder builder = createTransactionBuilder("writeAllFeaturesRequest");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, arr);
|
support.writeAllFeaturesRequest(builder, arr);
|
||||||
support.performImmediately(builder);
|
support.performImmediately(builder);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
@ -65,7 +65,7 @@ public class InitOperationGBX100 extends AbstractBTLEOperation<CasioGBX100Device
|
||||||
private void writeAllFeatures(byte[] arr) {
|
private void writeAllFeatures(byte[] arr) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = createTransactionBuilder("writeAllFeatures");
|
TransactionBuilder builder = createTransactionBuilder("writeAllFeatures");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeatures(builder, arr);
|
support.writeAllFeatures(builder, arr);
|
||||||
support.performImmediately(builder);
|
support.performImmediately(builder);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
@ -252,7 +252,7 @@ public class InitOperationGBX100 extends AbstractBTLEOperation<CasioGBX100Device
|
||||||
private void enableAllFeatures(boolean enable) {
|
private void enableAllFeatures(boolean enable) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = createTransactionBuilder("notifyAllFeatures");
|
TransactionBuilder builder = createTransactionBuilder("notifyAllFeatures");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
enableAllFeatures(builder, enable);
|
enableAllFeatures(builder, enable);
|
||||||
support.performImmediately(builder);
|
support.performImmediately(builder);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class SetAlarmOperation extends AbstractBTLEOperation<CasioGB6900DeviceSu
|
||||||
if (getDevice() != null) {
|
if (getDevice() != null) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("getSettingForAlarm");
|
TransactionBuilder builder = performInitialized("getSettingForAlarm");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.read(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_ALM_CHARACTERISTIC_UUID));
|
builder.read(getCharacteristic(CasioConstants.CASIO_SETTING_FOR_ALM_CHARACTERISTIC_UUID));
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
@ -74,7 +74,7 @@ public class SetAlarmOperation extends AbstractBTLEOperation<CasioGB6900DeviceSu
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("finished operation");
|
TransactionBuilder builder = performInitialized("finished operation");
|
||||||
builder.wait(0);
|
builder.wait(0);
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOG.info("Error resetting Gatt callback: " + ex.getMessage());
|
LOG.info("Error resetting Gatt callback: " + ex.getMessage());
|
||||||
|
|
|
@ -67,7 +67,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
byte[] command = new byte[1];
|
byte[] command = new byte[1];
|
||||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_USER_PROFILE");
|
||||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, command);
|
support.writeAllFeaturesRequest(builder, command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
// Target settings will be requested in write callback
|
// Target settings will be requested in write callback
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("setConfiguration");
|
TransactionBuilder builder = performInitialized("setConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeatures(builder, data);
|
support.writeAllFeatures(builder, data);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -191,7 +191,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
// Basic settings will be requested in Gatt callback
|
// Basic settings will be requested in Gatt callback
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("setConfiguration");
|
TransactionBuilder builder = performInitialized("setConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeatures(builder, data);
|
support.writeAllFeatures(builder, data);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -239,7 +239,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
// Operation will be finished in Gatt callback
|
// Operation will be finished in Gatt callback
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("setConfiguration");
|
TransactionBuilder builder = performInitialized("setConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeatures(builder, data);
|
support.writeAllFeatures(builder, data);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -263,7 +263,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
if (getDevice() != null) {
|
if (getDevice() != null) {
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("finished operation");
|
TransactionBuilder builder = performInitialized("finished operation");
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.wait(0);
|
builder.wait(0);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
@ -277,7 +277,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_BASIC");
|
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_BASIC");
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, command);
|
support.writeAllFeaturesRequest(builder, command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
@ -290,7 +290,7 @@ public class SetConfigurationOperation extends AbstractBTLEOperation<CasioGBX10
|
||||||
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_TARGET_VALUE");
|
command[0] = CasioConstants.characteristicToByte.get("CASIO_SETTING_FOR_TARGET_VALUE");
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("getConfiguration");
|
TransactionBuilder builder = performInitialized("getConfiguration");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
support.writeAllFeaturesRequest(builder, command);
|
support.writeAllFeaturesRequest(builder, command);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class FitProDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
builder.notify(getCharacteristic(UUID_CHARACTERISTIC_RX), true);
|
builder.notify(getCharacteristic(UUID_CHARACTERISTIC_RX), true);
|
||||||
builder.notify(getCharacteristic(GattService.UUID_SERVICE_BATTERY_SERVICE), true);
|
builder.notify(getCharacteristic(GattService.UUID_SERVICE_BATTERY_SERVICE), true);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
|
|
||||||
deviceInfoProfile.requestDeviceInfo(builder);
|
deviceInfoProfile.requestDeviceInfo(builder);
|
||||||
batteryInfoProfile.requestBatteryInfo(builder);
|
batteryInfoProfile.requestBatteryInfo(builder);
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
|
|
||||||
builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true);
|
builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(measureCharacteristic, true);
|
builder.notify(measureCharacteristic, true);
|
||||||
//Initialize device
|
//Initialize device
|
||||||
sendUserInfo(builder); //Sync preferences
|
sendUserInfo(builder); //Sync preferences
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
|
||||||
this.authFlags = authFlags;
|
this.authFlags = authFlags;
|
||||||
this.cryptFlags = cryptFlags;
|
this.cryptFlags = cryptFlags;
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -62,7 +62,7 @@ public abstract class AbstractID115Operation extends AbstractBTLEOperation<ID115
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("reenabling disabled notifications");
|
TransactionBuilder builder = performInitialized("reenabling disabled notifications");
|
||||||
enableNotifications(builder, false);
|
enableNotifications(builder, false);
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
GB.toast(getContext(), "Error enabling ID115 notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
GB.toast(getContext(), "Error enabling ID115 notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
|
|
|
@ -114,7 +114,7 @@ public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
measureCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_NOTIFY);
|
measureCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_NOTIFY);
|
||||||
ctrlCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_WRITE);
|
ctrlCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_WRITE);
|
||||||
|
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(measureCharacteristic, true);
|
builder.notify(measureCharacteristic, true);
|
||||||
|
|
||||||
syncSettings(builder);
|
syncSettings(builder);
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class JYouSupport extends AbstractBTLEDeviceSupport {
|
||||||
BluetoothGattCharacteristic measureCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_MEASURE);
|
BluetoothGattCharacteristic measureCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_MEASURE);
|
||||||
ctrlCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_CONTROL);
|
ctrlCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_CONTROL);
|
||||||
|
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(measureCharacteristic, true);
|
builder.notify(measureCharacteristic, true);
|
||||||
|
|
||||||
syncSettings(builder);
|
syncSettings(builder);
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
|
|
||||||
// Enable notification
|
// Enable notification
|
||||||
|
|
|
@ -63,7 +63,7 @@ public abstract class MultiFetchRequest extends Request {
|
||||||
try {
|
try {
|
||||||
super.operationFinished();
|
super.operationFinished();
|
||||||
TransactionBuilder builder = performInitialized("Finishing operation");
|
TransactionBuilder builder = performInitialized("Finishing operation");
|
||||||
builder.setGattCallback(null);
|
builder.setCallback(null);
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GB.toast(getContext(), "Failed to reset callback", Toast.LENGTH_SHORT,
|
GB.toast(getContext(), "Failed to reset callback", Toast.LENGTH_SHORT,
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class InitOperation extends AbstractBTLEOperation<WatchXPlusDeviceSupport
|
||||||
super(support);
|
super(support);
|
||||||
this.needsAuth = needsAuth;
|
this.needsAuth = needsAuth;
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,7 +72,7 @@ public class InitOperation extends AbstractBTLEOperation<WatchXPlusDeviceSupport
|
||||||
getSupport().logMessageContent(value);
|
getSupport().logMessageContent(value);
|
||||||
if (ArrayUtils.equals(value, Watch9Constants.RESP_AUTHORIZATION_TASK, 5) && value[8] == 0x01) {
|
if (ArrayUtils.equals(value, Watch9Constants.RESP_AUTHORIZATION_TASK, 5) && value[8] == 0x01) {
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder("authInit");
|
TransactionBuilder builder = getSupport().createTransactionBuilder("authInit");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
getSupport().initialize(builder).performImmediately(builder);
|
getSupport().initialize(builder).performImmediately(builder);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -551,7 +551,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
.checkInitTime(builder)
|
.checkInitTime(builder)
|
||||||
.syncPreferences(builder); // read preferences from app and set them to watch
|
.syncPreferences(builder); // read preferences from app and set them to watch
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -498,7 +498,7 @@ public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implement
|
||||||
this.mReportCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT);
|
this.mReportCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT);
|
||||||
|
|
||||||
builder.notify(this.mReportCharacteristic, true);
|
builder.notify(this.mReportCharacteristic, true);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
|
|
||||||
|
|
||||||
// Allow modifications
|
// Allow modifications
|
||||||
|
|
|
@ -48,7 +48,7 @@ public abstract class AbstractMiBandOperation<T extends AbstractBTLEDeviceSuppor
|
||||||
try {
|
try {
|
||||||
TransactionBuilder builder = performInitialized("reenabling disabled notifications");
|
TransactionBuilder builder = performInitialized("reenabling disabled notifications");
|
||||||
handleFinished(builder);
|
handleFinished(builder);
|
||||||
builder.setGattCallback(null); // unset ourselves from being the queue's gatt callback
|
builder.setCallback(null); // unset ourselves from being the queue's gatt callback
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
GB.toast(getContext(), "Error enabling Mi Band notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
GB.toast(getContext(), "Error enabling Mi Band notifications, you may need to connect and disconnect", Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class MiScale2DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||||
|
|
||||||
// Weight and body composition
|
// Weight and body composition
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT), true);
|
builder.notify(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_BODY_COMPOSITION_MEASUREMENT), true);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
|
||||||
measureCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_MEASURE);
|
measureCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_MEASURE);
|
||||||
ctrlCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_CONTROL);
|
ctrlCharacteristic = getCharacteristic(No1F1Constants.UUID_CHARACTERISTIC_CONTROL);
|
||||||
|
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(measureCharacteristic, true);
|
builder.notify(measureCharacteristic, true);
|
||||||
|
|
||||||
setTime(builder);
|
setTime(builder);
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class SuperCarsSupport extends AbstractBTLEDeviceSupport {
|
||||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
builder.notify(getCharacteristic(SuperCarsConstants.CHARACTERISTIC_UUID_FFF4), true); //for battery
|
builder.notify(getCharacteristic(SuperCarsConstants.CHARACTERISTIC_UUID_FFF4), true); //for battery
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
|
|
|
@ -94,7 +94,7 @@ public class TLW64Support extends AbstractBTLEDeviceSupport {
|
||||||
ctrlCharacteristic = getCharacteristic(TLW64Constants.UUID_CHARACTERISTIC_CONTROL);
|
ctrlCharacteristic = getCharacteristic(TLW64Constants.UUID_CHARACTERISTIC_CONTROL);
|
||||||
notifyCharacteristic = getCharacteristic(TLW64Constants.UUID_CHARACTERISTIC_NOTIFY);
|
notifyCharacteristic = getCharacteristic(TLW64Constants.UUID_CHARACTERISTIC_NOTIFY);
|
||||||
|
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(notifyCharacteristic, true);
|
builder.notify(notifyCharacteristic, true);
|
||||||
|
|
||||||
setTime(builder);
|
setTime(builder);
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class WaspOSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
rxCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX);
|
rxCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX);
|
||||||
txCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_TX);
|
txCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_TX);
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.notify(rxCharacteristic, true);
|
builder.notify(rxCharacteristic, true);
|
||||||
|
|
||||||
uartTx(builder, " \u0003"); // clear active line
|
uartTx(builder, " \u0003"); // clear active line
|
||||||
|
|
|
@ -314,7 +314,7 @@ public class Watch9DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||||
.enableDoNotDisturb(builder, false)
|
.enableDoNotDisturb(builder, false)
|
||||||
.setFitnessGoal(builder);
|
.setFitnessGoal(builder);
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class InitOperation extends AbstractBTLEOperation<Watch9DeviceSupport>{
|
||||||
super(support);
|
super(support);
|
||||||
this.needsAuth = needsAuth;
|
this.needsAuth = needsAuth;
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,7 +73,7 @@ public class InitOperation extends AbstractBTLEOperation<Watch9DeviceSupport>{
|
||||||
getSupport().logMessageContent(value);
|
getSupport().logMessageContent(value);
|
||||||
if (ArrayUtils.equals(value, Watch9Constants.RESP_AUTHORIZATION_TASK, 5) && value[8] == 0x01) {
|
if (ArrayUtils.equals(value, Watch9Constants.RESP_AUTHORIZATION_TASK, 5) && value[8] == 0x01) {
|
||||||
TransactionBuilder builder = getSupport().createTransactionBuilder("authInit");
|
TransactionBuilder builder = getSupport().createTransactionBuilder("authInit");
|
||||||
builder.setGattCallback(this);
|
builder.setCallback(this);
|
||||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||||
getSupport().initialize(builder).performImmediately(builder);
|
getSupport().initialize(builder).performImmediately(builder);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user