mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Xiaomi: Introduce XiaomiConnectionSupport
Co-Authored-By: José Rebelo <joserebelo@outlook.com>
This commit is contained in:
parent
25dcba23c3
commit
98e8ec2329
@ -57,12 +57,10 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiAuthService.class);
|
||||
|
||||
public static final byte[] PAYLOAD_HEADER_AUTH = new byte[]{0, 0, 2, 2};
|
||||
|
||||
public static final int COMMAND_TYPE = 1;
|
||||
|
||||
public static final int CMD_SEND_USERID = 5;
|
||||
|
||||
public static final int CMD_NONCE = 26;
|
||||
public static final int CMD_AUTH = 27;
|
||||
|
||||
@ -83,7 +81,8 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
return encryptionInitialized;
|
||||
}
|
||||
|
||||
protected void startEncryptedHandshake(final TransactionBuilder builder) {
|
||||
// TODO also implement for spp
|
||||
protected void startEncryptedHandshake(final XiaomiBleSupport support, final TransactionBuilder builder) {
|
||||
encryptionInitialized = false;
|
||||
|
||||
builder.add(new SetDeviceStateAction(getSupport().getDevice(), GBDevice.State.AUTHENTICATING, getSupport().getContext()));
|
||||
@ -91,10 +90,10 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
System.arraycopy(getSecretKey(getSupport().getDevice()), 0, secretKey, 0, 16);
|
||||
new SecureRandom().nextBytes(nonce);
|
||||
|
||||
getSupport().sendCommand(builder, buildNonceCommand(nonce));
|
||||
support.sendCommand(builder, buildNonceCommand(nonce));
|
||||
}
|
||||
|
||||
protected void startClearTextHandshake(final TransactionBuilder builder) {
|
||||
protected void startClearTextHandshake(final XiaomiBleSupport support, final TransactionBuilder builder) {
|
||||
builder.add(new SetDeviceStateAction(getSupport().getDevice(), GBDevice.State.AUTHENTICATING, getSupport().getContext()));
|
||||
|
||||
final XiaomiProto.Auth auth = XiaomiProto.Auth.newBuilder()
|
||||
@ -107,7 +106,7 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
.setAuth(auth)
|
||||
.build();
|
||||
|
||||
getSupport().sendCommand(builder, command);
|
||||
support.sendCommand(builder, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,33 +120,27 @@ public class XiaomiAuthService extends AbstractXiaomiService {
|
||||
LOG.debug("Got watch nonce");
|
||||
|
||||
// Watch nonce
|
||||
final XiaomiProto.Command reply = handleWatchNonce(cmd.getAuth().getWatchNonce());
|
||||
if (reply == null) {
|
||||
final XiaomiProto.Command command = handleWatchNonce(cmd.getAuth().getWatchNonce());
|
||||
if (command == null) {
|
||||
getSupport().disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("auth step 2");
|
||||
// TODO use sendCommand
|
||||
builder.write(
|
||||
getSupport().getCharacteristic(getSupport().characteristicCommandWrite.getCharacteristicUUID()),
|
||||
ArrayUtils.addAll(PAYLOAD_HEADER_AUTH, reply.toByteArray())
|
||||
);
|
||||
builder.queue(getSupport().getQueue());
|
||||
getSupport().sendCommand("auth step 2", command);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_AUTH:
|
||||
case CMD_SEND_USERID: {
|
||||
if (cmd.getSubtype() == CMD_AUTH || cmd.getAuth().getStatus() == 1) {
|
||||
LOG.info("Authenticated!");
|
||||
|
||||
encryptionInitialized = cmd.getSubtype() == CMD_AUTH;
|
||||
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("phase 2 initialize");
|
||||
builder.add(new SetDeviceStateAction(getSupport().getDevice(), GBDevice.State.INITIALIZED, getSupport().getContext()));
|
||||
getSupport().phase2Initialize();
|
||||
builder.queue(getSupport().getQueue());
|
||||
LOG.info("Authenticated, further communications are {}", encryptionInitialized ? "encrypted" : "in plaintext");
|
||||
|
||||
getSupport().getDevice().setState(GBDevice.State.INITIALIZED);
|
||||
getSupport().getDevice().sendDeviceUpdateIntent(getSupport().getContext(), GBDevice.DeviceUpdateSubject.DEVICE_STATE);
|
||||
|
||||
getSupport().onAuthSuccess();
|
||||
} else {
|
||||
LOG.warn("could not authenticate");
|
||||
}
|
||||
|
@ -0,0 +1,242 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class XiaomiBleSupport extends XiaomiConnectionSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiBleSupport.class);
|
||||
|
||||
private XiaomiCharacteristic characteristicCommandRead;
|
||||
private XiaomiCharacteristic characteristicCommandWrite;
|
||||
private XiaomiCharacteristic characteristicActivityData;
|
||||
private XiaomiCharacteristic characteristicDataUpload;
|
||||
|
||||
private final XiaomiSupport mXiaomiSupport;
|
||||
|
||||
final AbstractBTLEDeviceSupport commsSupport = new AbstractBTLEDeviceSupport(LOG) {
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return mXiaomiSupport.useAutoConnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<UUID> getSupportedServices() {
|
||||
return XiaomiBleUuids.UUIDS.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final TransactionBuilder initializeDevice(final TransactionBuilder builder) {
|
||||
XiaomiBleUuids.XiaomiBleUuidSet uuidSet = null;
|
||||
BluetoothGattCharacteristic btCharacteristicCommandRead = null;
|
||||
BluetoothGattCharacteristic btCharacteristicCommandWrite = null;
|
||||
BluetoothGattCharacteristic btCharacteristicActivityData = null;
|
||||
BluetoothGattCharacteristic btCharacteristicDataUpload = null;
|
||||
|
||||
// Attempt to find a known xiaomi service
|
||||
for (Map.Entry<UUID, XiaomiBleUuids.XiaomiBleUuidSet> xiaomiUuid : XiaomiBleUuids.UUIDS.entrySet()) {
|
||||
if (getSupportedServices().contains(xiaomiUuid.getKey())) {
|
||||
LOG.debug("Found Xiaomi service: {}", xiaomiUuid.getKey());
|
||||
uuidSet = xiaomiUuid.getValue();
|
||||
|
||||
btCharacteristicCommandRead = getCharacteristic(uuidSet.getCharacteristicCommandRead());
|
||||
btCharacteristicCommandWrite = getCharacteristic(uuidSet.getCharacteristicCommandWrite());
|
||||
btCharacteristicActivityData = getCharacteristic(uuidSet.getCharacteristicActivityData());
|
||||
btCharacteristicDataUpload = getCharacteristic(uuidSet.getCharacteristicDataUpload());
|
||||
if (btCharacteristicCommandRead == null) {
|
||||
LOG.warn("btCharacteristicCommandRead characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicCommandWrite == null) {
|
||||
LOG.warn("btCharacteristicCommandWrite characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicActivityData == null) {
|
||||
LOG.warn("btCharacteristicActivityData characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicDataUpload == null) {
|
||||
LOG.warn("btCharacteristicDataUpload characteristicc is null");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uuidSet == null) {
|
||||
GB.toast(getContext(), "Failed to find known Xiaomi service", Toast.LENGTH_LONG, GB.ERROR);
|
||||
LOG.warn("Failed to find known Xiaomi service");
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
// FIXME unsetDynamicState unsets the fw version, which causes problems..
|
||||
if (getDevice().getFirmwareVersion() == null && mXiaomiSupport.getCachedFirmwareVersion() != null) {
|
||||
getDevice().setFirmwareVersion(mXiaomiSupport.getCachedFirmwareVersion());
|
||||
}
|
||||
|
||||
if (btCharacteristicCommandRead == null || btCharacteristicCommandWrite == null) {
|
||||
LOG.warn("Characteristics are null, will attempt to reconnect");
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.WAITING_FOR_RECONNECT, getContext()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
XiaomiBleSupport.this.characteristicCommandRead = new XiaomiCharacteristic(XiaomiBleSupport.this, btCharacteristicCommandRead, mXiaomiSupport.getAuthService());
|
||||
XiaomiBleSupport.this.characteristicCommandRead.setEncrypted(uuidSet.isEncrypted());
|
||||
XiaomiBleSupport.this.characteristicCommandRead.setChannelHandler(mXiaomiSupport::handleCommandBytes);
|
||||
XiaomiBleSupport.this.characteristicCommandWrite = new XiaomiCharacteristic(XiaomiBleSupport.this, btCharacteristicCommandWrite, mXiaomiSupport.getAuthService());
|
||||
XiaomiBleSupport.this.characteristicCommandWrite.setEncrypted(uuidSet.isEncrypted());
|
||||
XiaomiBleSupport.this.characteristicActivityData = new XiaomiCharacteristic(XiaomiBleSupport.this, btCharacteristicActivityData, mXiaomiSupport.getAuthService());
|
||||
XiaomiBleSupport.this.characteristicActivityData.setChannelHandler(mXiaomiSupport.getHealthService().getActivityFetcher()::addChunk);
|
||||
XiaomiBleSupport.this.characteristicActivityData.setEncrypted(uuidSet.isEncrypted());
|
||||
XiaomiBleSupport.this.characteristicDataUpload = new XiaomiCharacteristic(XiaomiBleSupport.this, btCharacteristicDataUpload, mXiaomiSupport.getAuthService());
|
||||
XiaomiBleSupport.this.characteristicDataUpload.setEncrypted(uuidSet.isEncrypted());
|
||||
XiaomiBleSupport.this.characteristicDataUpload.setIncrementNonce(false);
|
||||
|
||||
mXiaomiSupport.getDataUploadService().setDataUploadCharacteristic(XiaomiBleSupport.this.characteristicDataUpload);
|
||||
|
||||
builder.requestMtu(247);
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
builder.notify(btCharacteristicCommandWrite, true);
|
||||
builder.notify(btCharacteristicCommandRead, true);
|
||||
builder.notify(btCharacteristicActivityData, true);
|
||||
builder.notify(btCharacteristicDataUpload, true);
|
||||
|
||||
if (uuidSet.isEncrypted()) {
|
||||
mXiaomiSupport.getAuthService().startEncryptedHandshake(XiaomiBleSupport.this, builder);
|
||||
} else {
|
||||
mXiaomiSupport.getAuthService().startClearTextHandshake(XiaomiBleSupport.this, builder);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
if (super.onCharacteristicChanged(gatt, characteristic)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final UUID characteristicUUID = characteristic.getUuid();
|
||||
final byte[] value = characteristic.getValue();
|
||||
|
||||
if (characteristicCommandRead.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicCommandRead.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicCommandWrite.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicCommandWrite.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicActivityData.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicActivityData.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicDataUpload.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicDataUpload.onCharacteristicChanged(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Unhandled characteristic changed: {} {}", characteristicUUID, GB.hexdump(value));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getImplicitCallbackModify() {
|
||||
return mXiaomiSupport.getImplicitCallbackModify();
|
||||
}
|
||||
};
|
||||
|
||||
public XiaomiBleSupport(final XiaomiSupport xiaomiSupport) {
|
||||
this.mXiaomiSupport = xiaomiSupport;
|
||||
}
|
||||
|
||||
public void onAuthSuccess() {
|
||||
characteristicCommandRead.reset();
|
||||
characteristicCommandWrite.reset();
|
||||
characteristicActivityData.reset();
|
||||
characteristicDataUpload.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(GBDevice device, BluetoothAdapter adapter, Context context) {
|
||||
this.commsSupport.setContext(device, adapter, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
this.commsSupport.disconnect();
|
||||
}
|
||||
|
||||
public void sendCommand(final String taskName, final XiaomiProto.Command command) {
|
||||
if (this.characteristicCommandWrite == null) {
|
||||
// Can sometimes happen in race conditions when connecting + receiving calendar event or weather updates
|
||||
LOG.warn("characteristicCommandWrite is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.characteristicCommandWrite.write(taskName, command.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Realistically, this function should only be used during auth, as we must schedule the command after
|
||||
* notifications were enabled on the characteristics, and for that we need the builder to guarantee the
|
||||
* order.
|
||||
*/
|
||||
public void sendCommand(final TransactionBuilder builder, final XiaomiProto.Command command) {
|
||||
if (this.characteristicCommandWrite == null) {
|
||||
// Can sometimes happen in race conditions when connecting + receiving calendar event or weather updates
|
||||
LOG.warn("characteristicCommandWrite is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.characteristicCommandWrite.write(builder, command.toByteArray());
|
||||
}
|
||||
|
||||
public TransactionBuilder createTransactionBuilder(String taskName) {
|
||||
return commsSupport.createTransactionBuilder(taskName);
|
||||
}
|
||||
|
||||
public BtLEQueue getQueue() {
|
||||
return commsSupport.getQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUploadProgress(int textRsrc, int progressPercent) {
|
||||
try {
|
||||
final TransactionBuilder builder = commsSupport.createTransactionBuilder("send data upload progress");
|
||||
builder.add(new SetProgressAction(
|
||||
commsSupport.getContext().getString(textRsrc),
|
||||
true,
|
||||
progressPercent,
|
||||
commsSupport.getContext()
|
||||
));
|
||||
builder.queue(commsSupport.getQueue());
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to update progress notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
return commsSupport.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
commsSupport.dispose();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
|
||||
|
||||
public interface XiaomiChannelHandler {
|
||||
void handle(final byte[] payload);
|
||||
}
|
@ -45,7 +45,7 @@ public class XiaomiCharacteristic {
|
||||
// max chunk size, including headers
|
||||
public static final int MAX_WRITE_SIZE = 242;
|
||||
|
||||
private final XiaomiSupport mSupport;
|
||||
private final XiaomiBleSupport mSupport;
|
||||
|
||||
private final BluetoothGattCharacteristic bluetoothGattCharacteristic;
|
||||
private final UUID characteristicUUID;
|
||||
@ -68,11 +68,11 @@ public class XiaomiCharacteristic {
|
||||
private boolean sendingChunked = false;
|
||||
private Payload currentPayload = null;
|
||||
|
||||
private Handler handler = null;
|
||||
private XiaomiChannelHandler channelHandler = null;
|
||||
|
||||
private SendCallback callback;
|
||||
|
||||
public XiaomiCharacteristic(final XiaomiSupport support,
|
||||
public XiaomiCharacteristic(final XiaomiBleSupport support,
|
||||
final BluetoothGattCharacteristic bluetoothGattCharacteristic,
|
||||
@Nullable final XiaomiAuthService authService) {
|
||||
this.mSupport = support;
|
||||
@ -86,8 +86,8 @@ public class XiaomiCharacteristic {
|
||||
return characteristicUUID;
|
||||
}
|
||||
|
||||
public void setHandler(final Handler handler) {
|
||||
this.handler = handler;
|
||||
public void setChannelHandler(final XiaomiChannelHandler handler) {
|
||||
this.channelHandler = handler;
|
||||
}
|
||||
|
||||
public void setCallback(final SendCallback callback) {
|
||||
@ -162,11 +162,15 @@ public class XiaomiCharacteristic {
|
||||
if (chunk == numChunks) {
|
||||
sendChunkEndAck();
|
||||
|
||||
if (isEncrypted) {
|
||||
// chunks are always encrypted if an auth service is available
|
||||
handler.handle(authService.decrypt(chunkBuffer.toByteArray()));
|
||||
if (channelHandler != null) {
|
||||
if (isEncrypted) {
|
||||
// chunks are always encrypted if an auth service is available
|
||||
channelHandler.handle(authService.decrypt(chunkBuffer.toByteArray()));
|
||||
} else {
|
||||
channelHandler.handle(chunkBuffer.toByteArray());
|
||||
}
|
||||
} else {
|
||||
handler.handle(chunkBuffer.toByteArray());
|
||||
LOG.warn("Channel handler for char {} is null!", characteristicUUID);
|
||||
}
|
||||
|
||||
currentChunk = 0;
|
||||
@ -249,7 +253,10 @@ public class XiaomiCharacteristic {
|
||||
buf.get(plainValue);
|
||||
}
|
||||
|
||||
handler.handle(plainValue);
|
||||
if (channelHandler != null)
|
||||
channelHandler.handle(plainValue);
|
||||
else
|
||||
LOG.warn("Channel handler for char {} is null!", characteristicUUID);
|
||||
|
||||
return;
|
||||
case 3:
|
||||
@ -362,10 +369,6 @@ public class XiaomiCharacteristic {
|
||||
builder.queue(mSupport.getQueue());
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void handle(final byte[] payload);
|
||||
}
|
||||
|
||||
private static class Payload {
|
||||
private final String taskName;
|
||||
private final byte[] bytes;
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* Copyright (C) 2023 José Rebelo, Yoran Vulker
|
||||
|
||||
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.devices.xiaomi;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
|
||||
public abstract class XiaomiConnectionSupport {
|
||||
public abstract boolean connect();
|
||||
public abstract void onAuthSuccess();
|
||||
public abstract void onUploadProgress(int textRsrc, int progressPercent);
|
||||
public abstract void dispose();
|
||||
public abstract void setContext(final GBDevice device, final BluetoothAdapter adapter, final Context context);
|
||||
public abstract void disconnect();
|
||||
public abstract void sendCommand(final String taskName, final XiaomiProto.Command command);
|
||||
}
|
@ -18,12 +18,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi;
|
||||
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
@ -42,6 +39,7 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -57,9 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Reminder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityFileId;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity.XiaomiActivityParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services.AbstractXiaomiService;
|
||||
@ -77,27 +73,23 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
public class XiaomiSupport extends AbstractDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XiaomiSupport.class);
|
||||
|
||||
protected XiaomiCharacteristic characteristicCommandRead;
|
||||
protected XiaomiCharacteristic characteristicCommandWrite;
|
||||
protected XiaomiCharacteristic characteristicActivityData;
|
||||
protected XiaomiCharacteristic characteristicDataUpload;
|
||||
private final XiaomiAuthService authService = new XiaomiAuthService(this);
|
||||
private final XiaomiMusicService musicService = new XiaomiMusicService(this);
|
||||
private final XiaomiHealthService healthService = new XiaomiHealthService(this);
|
||||
private final XiaomiNotificationService notificationService = new XiaomiNotificationService(this);
|
||||
private final XiaomiScheduleService scheduleService = new XiaomiScheduleService(this);
|
||||
private final XiaomiWeatherService weatherService = new XiaomiWeatherService(this);
|
||||
private final XiaomiSystemService systemService = new XiaomiSystemService(this);
|
||||
private final XiaomiCalendarService calendarService = new XiaomiCalendarService(this);
|
||||
private final XiaomiWatchfaceService watchfaceService = new XiaomiWatchfaceService(this);
|
||||
private final XiaomiDataUploadService dataUploadService = new XiaomiDataUploadService(this);
|
||||
private final XiaomiPhonebookService phonebookService = new XiaomiPhonebookService(this);
|
||||
|
||||
protected final XiaomiAuthService authService = new XiaomiAuthService(this);
|
||||
protected final XiaomiMusicService musicService = new XiaomiMusicService(this);
|
||||
protected final XiaomiHealthService healthService = new XiaomiHealthService(this);
|
||||
protected final XiaomiNotificationService notificationService = new XiaomiNotificationService(this);
|
||||
protected final XiaomiScheduleService scheduleService = new XiaomiScheduleService(this);
|
||||
protected final XiaomiWeatherService weatherService = new XiaomiWeatherService(this);
|
||||
protected final XiaomiSystemService systemService = new XiaomiSystemService(this);
|
||||
protected final XiaomiCalendarService calendarService = new XiaomiCalendarService(this);
|
||||
protected final XiaomiWatchfaceService watchfaceService = new XiaomiWatchfaceService(this);
|
||||
protected final XiaomiDataUploadService dataUploadService = new XiaomiDataUploadService(this);
|
||||
protected final XiaomiPhonebookService phonebookService = new XiaomiPhonebookService(this);
|
||||
|
||||
private String mFirmwareVersion = null;
|
||||
private String cachedFirmwareVersion = null;
|
||||
private XiaomiConnectionSupport connectionSupport = null;
|
||||
|
||||
private final Map<Integer, AbstractXiaomiService> mServiceMap = new LinkedHashMap<Integer, AbstractXiaomiService>() {{
|
||||
put(XiaomiAuthService.COMMAND_TYPE, authService);
|
||||
@ -113,98 +105,6 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
put(XiaomiPhonebookService.COMMAND_TYPE, phonebookService);
|
||||
}};
|
||||
|
||||
public XiaomiSupport() {
|
||||
super(LOG);
|
||||
for (final UUID uuid : XiaomiBleUuids.UUIDS.keySet()) {
|
||||
addSupportedService(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final TransactionBuilder initializeDevice(final TransactionBuilder builder) {
|
||||
XiaomiBleUuids.XiaomiBleUuidSet uuidSet = null;
|
||||
BluetoothGattCharacteristic btCharacteristicCommandRead = null;
|
||||
BluetoothGattCharacteristic btCharacteristicCommandWrite = null;
|
||||
BluetoothGattCharacteristic btCharacteristicActivityData = null;
|
||||
BluetoothGattCharacteristic btCharacteristicDataUpload = null;
|
||||
|
||||
// Attempt to find a known xiaomi service
|
||||
for (Map.Entry<UUID, XiaomiBleUuids.XiaomiBleUuidSet> xiaomiUuid : XiaomiBleUuids.UUIDS.entrySet()) {
|
||||
if (getSupportedServices().contains(xiaomiUuid.getKey())) {
|
||||
LOG.debug("Found Xiaomi service: {}", xiaomiUuid.getKey());
|
||||
uuidSet = xiaomiUuid.getValue();
|
||||
|
||||
btCharacteristicCommandRead = getCharacteristic(uuidSet.getCharacteristicCommandRead());
|
||||
btCharacteristicCommandWrite = getCharacteristic(uuidSet.getCharacteristicCommandWrite());
|
||||
btCharacteristicActivityData = getCharacteristic(uuidSet.getCharacteristicActivityData());
|
||||
btCharacteristicDataUpload = getCharacteristic(uuidSet.getCharacteristicDataUpload());
|
||||
if (btCharacteristicCommandRead == null) {
|
||||
LOG.warn("btCharacteristicCommandRead characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicCommandWrite == null) {
|
||||
LOG.warn("btCharacteristicCommandWrite characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicActivityData == null) {
|
||||
LOG.warn("btCharacteristicActivityData characteristicc is null");
|
||||
continue;
|
||||
} else if (btCharacteristicDataUpload == null) {
|
||||
LOG.warn("btCharacteristicDataUpload characteristicc is null");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uuidSet == null) {
|
||||
GB.toast(getContext(), "Failed to find known Xiaomi service", Toast.LENGTH_LONG, GB.ERROR);
|
||||
LOG.warn("Failed to find known Xiaomi service");
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
// FIXME unsetDynamicState unsets the fw version, which causes problems..
|
||||
if (getDevice().getFirmwareVersion() == null && mFirmwareVersion != null) {
|
||||
getDevice().setFirmwareVersion(mFirmwareVersion);
|
||||
}
|
||||
|
||||
if (btCharacteristicCommandRead == null || btCharacteristicCommandWrite == null) {
|
||||
LOG.warn("Characteristics are null, will attempt to reconnect");
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.WAITING_FOR_RECONNECT, getContext()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
this.characteristicCommandRead = new XiaomiCharacteristic(this, btCharacteristicCommandRead, authService);
|
||||
this.characteristicCommandRead.setEncrypted(uuidSet.isEncrypted());
|
||||
this.characteristicCommandRead.setHandler(this::handleCommandBytes);
|
||||
this.characteristicCommandWrite = new XiaomiCharacteristic(this, btCharacteristicCommandWrite, authService);
|
||||
this.characteristicCommandWrite.setEncrypted(uuidSet.isEncrypted());
|
||||
this.characteristicActivityData = new XiaomiCharacteristic(this, btCharacteristicActivityData, authService);
|
||||
this.characteristicActivityData.setHandler(healthService.getActivityFetcher()::addChunk);
|
||||
this.characteristicActivityData.setEncrypted(uuidSet.isEncrypted());
|
||||
this.characteristicDataUpload = new XiaomiCharacteristic(this, btCharacteristicDataUpload, authService);
|
||||
this.characteristicDataUpload.setEncrypted(uuidSet.isEncrypted());
|
||||
this.characteristicDataUpload.setIncrementNonce(false);
|
||||
this.dataUploadService.setDataUploadCharacteristic(this.characteristicDataUpload);
|
||||
|
||||
builder.requestMtu(247);
|
||||
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
|
||||
builder.notify(btCharacteristicCommandWrite, true);
|
||||
builder.notify(btCharacteristicCommandRead, true);
|
||||
builder.notify(btCharacteristicActivityData, true);
|
||||
builder.notify(btCharacteristicDataUpload, true);
|
||||
|
||||
if (uuidSet.isEncrypted()) {
|
||||
authService.startEncryptedHandshake(builder);
|
||||
} else {
|
||||
authService.startClearTextHandshake(builder);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return true;
|
||||
@ -215,46 +115,84 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(final GBDevice gbDevice, final BluetoothAdapter btAdapter, final Context context) {
|
||||
// FIXME unsetDynamicState unsets the fw version, which causes problems..
|
||||
if (mFirmwareVersion == null && gbDevice.getFirmwareVersion() != null) {
|
||||
mFirmwareVersion = gbDevice.getFirmwareVersion();
|
||||
private XiaomiConnectionSupport createConnectionSpecificSupport() {
|
||||
DeviceCoordinator.ConnectionType connType = getCoordinator().getConnectionType();
|
||||
|
||||
switch (connType) {
|
||||
case BLE:
|
||||
case BOTH:
|
||||
return new XiaomiBleSupport(this);
|
||||
}
|
||||
|
||||
super.setContext(gbDevice, btAdapter, context);
|
||||
for (final AbstractXiaomiService service : mServiceMap.values()) {
|
||||
service.setContext(context);
|
||||
LOG.error("Cannot create connection-specific support, unhanded {} connection type", connType);
|
||||
return null;
|
||||
}
|
||||
|
||||
public XiaomiConnectionSupport getConnectionSpecificSupport() {
|
||||
if (connectionSupport == null) {
|
||||
connectionSupport = createConnectionSpecificSupport();
|
||||
}
|
||||
|
||||
return connectionSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
if (super.onCharacteristicChanged(gatt, characteristic)) {
|
||||
return true;
|
||||
}
|
||||
public boolean connect() {
|
||||
if (getConnectionSpecificSupport() != null)
|
||||
return getConnectionSpecificSupport().connect();
|
||||
|
||||
final UUID characteristicUUID = characteristic.getUuid();
|
||||
final byte[] value = characteristic.getValue();
|
||||
|
||||
if (characteristicCommandRead.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicCommandRead.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicCommandWrite.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicCommandWrite.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicActivityData.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicActivityData.onCharacteristicChanged(value);
|
||||
return true;
|
||||
} else if (characteristicDataUpload.getCharacteristicUUID().equals(characteristicUUID)) {
|
||||
characteristicDataUpload.onCharacteristicChanged(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.warn("Unhandled characteristic changed: {} {}", characteristicUUID, GB.hexdump(value));
|
||||
LOG.error("getConnectionSpecificSupport returned null, could not connect");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onUploadProgress(int textRsrc, int progressPercent) {
|
||||
if (getConnectionSpecificSupport() == null) {
|
||||
LOG.error("onUploadProgress called but connection specific unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
getConnectionSpecificSupport().onUploadProgress(textRsrc, progressPercent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (getConnectionSpecificSupport() != null) {
|
||||
getConnectionSpecificSupport().dispose();
|
||||
connectionSupport = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setContext(final GBDevice device, final BluetoothAdapter adapter, final Context context) {
|
||||
// FIXME unsetDynamicState unsets the fw version, which causes problems..
|
||||
if (getCachedFirmwareVersion() == null && device.getFirmwareVersion() != null) {
|
||||
setCachedFirmwareVersion(device.getFirmwareVersion());
|
||||
}
|
||||
|
||||
super.setContext(device, adapter, context);
|
||||
|
||||
for (AbstractXiaomiService service : mServiceMap.values()) {
|
||||
service.setContext(context);
|
||||
}
|
||||
|
||||
if (getConnectionSpecificSupport() != null) {
|
||||
getConnectionSpecificSupport().setContext(device, adapter, context);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCachedFirmwareVersion() {
|
||||
return this.cachedFirmwareVersion;
|
||||
}
|
||||
|
||||
public void setCachedFirmwareVersion(String version) {
|
||||
this.cachedFirmwareVersion = version;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (getConnectionSpecificSupport() != null) {
|
||||
getConnectionSpecificSupport().disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCommandBytes(final byte[] plainValue) {
|
||||
LOG.debug("Got command: {}", GB.hexdump(plainValue));
|
||||
|
||||
@ -459,13 +397,10 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
return (XiaomiCoordinator) gbDevice.getDeviceCoordinator();
|
||||
}
|
||||
|
||||
protected void phase2Initialize() {
|
||||
LOG.info("phase2Initialize");
|
||||
protected void onAuthSuccess() {
|
||||
LOG.info("onAuthSuccess");
|
||||
|
||||
characteristicCommandRead.reset();
|
||||
characteristicCommandWrite.reset();
|
||||
characteristicActivityData.reset();
|
||||
characteristicDataUpload.reset();
|
||||
getConnectionSpecificSupport().onAuthSuccess();
|
||||
|
||||
if (GBApplication.getPrefs().getBoolean("datetime_synconconnect", true)) {
|
||||
systemService.setCurrentTime();
|
||||
@ -477,28 +412,7 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
public void sendCommand(final String taskName, final XiaomiProto.Command command) {
|
||||
if (this.characteristicCommandWrite == null) {
|
||||
// Can sometimes happen in race conditions when connecting + receiving calendar event or weather updates
|
||||
LOG.warn("characteristicCommandWrite is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.characteristicCommandWrite.write(taskName, command.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Realistically, this function should only be used during auth, as we must schedule the command after
|
||||
* notifications were enabled on the characteristics, and for that we need the builder to guarantee the
|
||||
* order.
|
||||
*/
|
||||
public void sendCommand(final TransactionBuilder builder, final XiaomiProto.Command command) {
|
||||
if (this.characteristicCommandWrite == null) {
|
||||
// Can sometimes happen in race conditions when connecting + receiving calendar event or weather updates
|
||||
LOG.warn("characteristicCommandWrite is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.characteristicCommandWrite.write(builder, command.toByteArray());
|
||||
getConnectionSpecificSupport().sendCommand(taskName, command);
|
||||
}
|
||||
|
||||
public void sendCommand(final String taskName, final int type, final int subtype) {
|
||||
@ -511,10 +425,18 @@ public class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
);
|
||||
}
|
||||
|
||||
public XiaomiDataUploadService getDataUploader() {
|
||||
public XiaomiAuthService getAuthService() {
|
||||
return this.authService;
|
||||
}
|
||||
|
||||
public XiaomiDataUploadService getDataUploadService() {
|
||||
return this.dataUploadService;
|
||||
}
|
||||
|
||||
public XiaomiHealthService getHealthService() {
|
||||
return this.healthService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String customStringFilter(final String inputString) {
|
||||
return StringUtils.replaceEach(inputString, EMOJI_SOURCE, EMOJI_TARGET);
|
||||
|
@ -538,15 +538,15 @@ public class XiaomiNotificationService extends AbstractXiaomiService implements
|
||||
}
|
||||
}
|
||||
|
||||
getSupport().getDataUploader().setCallback(this);
|
||||
getSupport().getDataUploader().requestUpload(XiaomiDataUploadService.TYPE_NOTIFICATION_ICON, buf.array());
|
||||
getSupport().getDataUploadService().setCallback(this);
|
||||
getSupport().getDataUploadService().requestUpload(XiaomiDataUploadService.TYPE_NOTIFICATION_ICON, buf.array());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUploadFinish(final boolean success) {
|
||||
LOG.debug("Notification icon upload finished: {}", success);
|
||||
|
||||
getSupport().getDataUploader().setCallback(null);
|
||||
getSupport().getDataUploadService().setCallback(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,8 +53,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SleepState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WearingState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiPreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
|
||||
@ -143,8 +141,8 @@ public class XiaomiSystemService extends AbstractXiaomiService implements Xiaomi
|
||||
|
||||
LOG.debug("Firmware install status 0, uploading");
|
||||
setDeviceBusy();
|
||||
getSupport().getDataUploader().setCallback(this);
|
||||
getSupport().getDataUploader().requestUpload(XiaomiDataUploadService.TYPE_FIRMWARE, fwHelper.getBytes());
|
||||
getSupport().getDataUploadService().setCallback(this);
|
||||
getSupport().getDataUploadService().requestUpload(XiaomiDataUploadService.TYPE_FIRMWARE, fwHelper.getBytes());
|
||||
return;
|
||||
case CMD_PASSWORD_GET:
|
||||
handlePassword(cmd.getSystem().getPassword());
|
||||
@ -315,9 +313,9 @@ public class XiaomiSystemService extends AbstractXiaomiService implements Xiaomi
|
||||
gbDeviceEventVersionInfo.fwVersion = deviceInfo.getFirmware();
|
||||
//gbDeviceEventVersionInfo.fwVersion2 = "N/A";
|
||||
gbDeviceEventVersionInfo.hwVersion = deviceInfo.getModel();
|
||||
final GBDeviceEventUpdateDeviceInfo gbDeviceEventUpdateDeviceInfo = new GBDeviceEventUpdateDeviceInfo("SERIAL: ", deviceInfo.getSerialNumber());
|
||||
|
||||
getSupport().evaluateGBDeviceEvent(gbDeviceEventVersionInfo);
|
||||
|
||||
final GBDeviceEventUpdateDeviceInfo gbDeviceEventUpdateDeviceInfo = new GBDeviceEventUpdateDeviceInfo("SERIAL: ", deviceInfo.getSerialNumber());
|
||||
getSupport().evaluateGBDeviceEvent(gbDeviceEventUpdateDeviceInfo);
|
||||
}
|
||||
|
||||
@ -937,7 +935,7 @@ public class XiaomiSystemService extends AbstractXiaomiService implements Xiaomi
|
||||
public void onUploadFinish(final boolean success) {
|
||||
LOG.debug("Firmware upload finished: {}", success);
|
||||
|
||||
getSupport().getDataUploader().setCallback(null);
|
||||
getSupport().getDataUploadService().setCallback(null);
|
||||
|
||||
final String notificationMessage = success ?
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_complete) :
|
||||
@ -952,17 +950,6 @@ public class XiaomiSystemService extends AbstractXiaomiService implements Xiaomi
|
||||
|
||||
@Override
|
||||
public void onUploadProgress(final int progressPercent) {
|
||||
try {
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("send data upload progress");
|
||||
builder.add(new SetProgressAction(
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_in_progress),
|
||||
true,
|
||||
progressPercent,
|
||||
getSupport().getContext()
|
||||
));
|
||||
builder.queue(getSupport().getQueue());
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to update progress notification", e);
|
||||
}
|
||||
getSupport().onUploadProgress(R.string.updatefirmwareoperation_update_in_progress, progressPercent);
|
||||
}
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ public class XiaomiWatchfaceService extends AbstractXiaomiService implements Xia
|
||||
|
||||
LOG.debug("Watchface install status 0, uploading");
|
||||
setDeviceBusy();
|
||||
getSupport().getDataUploader().setCallback(this);
|
||||
getSupport().getDataUploader().requestUpload(XiaomiDataUploadService.TYPE_WATCHFACE, fwHelper.getBytes());
|
||||
getSupport().getDataUploadService().setCallback(this);
|
||||
getSupport().getDataUploadService().requestUpload(XiaomiDataUploadService.TYPE_WATCHFACE, fwHelper.getBytes());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,11 +230,11 @@ public class XiaomiWatchfaceService extends AbstractXiaomiService implements Xia
|
||||
public void onUploadFinish(final boolean success) {
|
||||
LOG.debug("Watchface upload finished: {}", success);
|
||||
|
||||
getSupport().getDataUploader().setCallback(null);
|
||||
getSupport().getDataUploadService().setCallback(null);
|
||||
|
||||
final String notificationMessage = success ?
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_complete) :
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_write_failed);
|
||||
getSupport().getContext().getString(R.string.uploadwatchfaceoperation_complete) :
|
||||
getSupport().getContext().getString(R.string.uploadwatchfaceoperation_failed);
|
||||
|
||||
GB.updateInstallNotification(notificationMessage, false, 100, getSupport().getContext());
|
||||
|
||||
@ -250,23 +250,12 @@ public class XiaomiWatchfaceService extends AbstractXiaomiService implements Xia
|
||||
|
||||
@Override
|
||||
public void onUploadProgress(final int progressPercent) {
|
||||
try {
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("send data upload progress");
|
||||
builder.add(new SetProgressAction(
|
||||
getSupport().getContext().getString(R.string.updatefirmwareoperation_update_in_progress),
|
||||
true,
|
||||
progressPercent,
|
||||
getSupport().getContext()
|
||||
));
|
||||
builder.queue(getSupport().getQueue());
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to update progress notification", e);
|
||||
}
|
||||
getSupport().onUploadProgress(R.string.uploadwatchfaceoperation_in_progress, progressPercent);
|
||||
}
|
||||
|
||||
private void setDeviceBusy() {
|
||||
final GBDevice device = getSupport().getDevice();
|
||||
device.setBusyTask(getSupport().getContext().getString(R.string.updating_firmware));
|
||||
device.setBusyTask(getSupport().getContext().getString(R.string.uploading_watchface));
|
||||
device.sendDeviceUpdateIntent(getSupport().getContext());
|
||||
}
|
||||
|
||||
|
@ -2572,4 +2572,8 @@
|
||||
<string name="pref_title_fossil_hr_nav_vibrate">Vibrate on new instruction</string>
|
||||
<string name="pref_summary_fossil_hr_nav_vibrate">Whether the watch should vibrate on every new or changed navigation instruction (only when the app is in the foreground)</string>
|
||||
<string name="notification_channel_connection_status_name">Connection Status</string>
|
||||
<string name="uploading_watchface">Uploading watchface…</string>
|
||||
<string name="uploadwatchfaceoperation_in_progress">Uploading watchface</string>
|
||||
<string name="uploadwatchfaceoperation_complete">Watchface installation completed</string>
|
||||
<string name="uploadwatchfaceoperation_failed">Watchface installation failed</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user