mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-06-08 06:08:04 +02:00
4d0d9e298e
Co-authored-by: Vitaliy Tomin <highwaystar.ru@gmail.com> Co-committed-by: Vitaliy Tomin <highwaystar.ru@gmail.com>
1903 lines
87 KiB
Java
1903 lines
87 KiB
Java
/* Copyright (C) 2024 Damien Gaignon, Martin.JM, Vitalii Tomin
|
|
|
|
This file is part of Gadgetbridge.
|
|
|
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Gadgetbridge is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
|
|
|
|
import android.bluetooth.BluetoothGattCharacteristic;
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.location.Location;
|
|
import android.net.Uri;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import java.util.UUID;
|
|
|
|
import de.greenrobot.dao.query.QueryBuilder;
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier.HuaweiDeviceType;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSampleProvider;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySample;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSampleDao;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSample;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutPaceSampleDao;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySample;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleDao;
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationProviderType;
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationService;
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AcceptAgreementsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetEventAlarmList;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetGpsParameterRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationConstraintsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSmartAlarmList;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfaceParams;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendExtendedAccountRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsAndTimeToDeviceRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsDataRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadInfo;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherCurrentRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotifyHeartRateCapabilityRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotifyRestHeartRateCapabilityRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherExtendedSupportRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherForecastRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherStartRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherSunMoonSupportRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherSupportRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherUnitRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetAutomaticHeartrateRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetAutomaticSpoRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetDisconnectNotification;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetMediumToStrengthThresholdRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.StopFindPhoneRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.StopNotificationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetFitnessTotalsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetHiChainRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSleepDataCountRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetStepDataCountRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWorkoutCountRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotificationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetMusicRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AlarmsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.DebugRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetActivityTypeRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request.RequestCallback;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAuthRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetBatteryLevelRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetBondParamsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetBondRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetConnectStatusRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetDeviceStatusRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetDndLiftWristTypeRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetExpandCapabilityRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetLinkParamsRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetPincodeRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetProductInformationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSecurityNegotiationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSettingRelatedRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSupportedServicesRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWearStatusRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendDndAddRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFactoryResetRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFitnessGoalRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendMenstrualCapabilityRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendDndDeleteRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendSetUpDeviceStatusRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetActivateOnLiftRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetActivityReminderRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetDateFormatRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetLanguageSettingRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetNotificationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetNavigateOnRotateRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetTimeRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetTimeZoneIdRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetTruSleepRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetWearLocationRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetWearMessagePushRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationCapabilitiesRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FitnessData;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetWorkModeRequest;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
|
|
|
public class HuaweiSupportProvider {
|
|
private static final Logger LOG = LoggerFactory.getLogger(HuaweiSupportProvider.class);
|
|
|
|
private HuaweiBRSupport brSupport;
|
|
private HuaweiLESupport leSupport;
|
|
|
|
private GBDevice gbDevice;
|
|
private Context context;
|
|
private HuaweiCoordinatorSupplier.HuaweiDeviceType huaweiType;
|
|
|
|
private boolean firstConnection = false;
|
|
protected byte protocolVersion;
|
|
public String deviceMac; //get it from GB
|
|
protected String macAddress;
|
|
protected String androidID;
|
|
protected short msgId = 0;
|
|
|
|
private MusicStateSpec musicStateSpec = null;
|
|
private MusicSpec musicSpec = null;
|
|
|
|
private GpsAndTime.GpsParameters.Response gpsParametersResponse = null;
|
|
|
|
private final HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider();
|
|
|
|
protected ResponseManager responseManager = new ResponseManager(this);
|
|
protected HuaweiUploadManager huaweiUploadManager = new HuaweiUploadManager(this);
|
|
|
|
protected HuaweiWatchfaceManager huaweiWatchfaceManager = new HuaweiWatchfaceManager(this);
|
|
|
|
public HuaweiCoordinatorSupplier getCoordinator() {
|
|
return ((HuaweiCoordinatorSupplier) this.gbDevice.getDeviceCoordinator());
|
|
}
|
|
|
|
public HuaweiCoordinator getHuaweiCoordinator() {
|
|
return getCoordinator().getHuaweiCoordinator();
|
|
}
|
|
public HuaweiWatchfaceManager getHuaweiWatchfaceManager() {
|
|
return huaweiWatchfaceManager;
|
|
}
|
|
|
|
public HuaweiSupportProvider(HuaweiBRSupport support) {
|
|
this.brSupport = support;
|
|
}
|
|
|
|
public HuaweiSupportProvider(HuaweiLESupport support) {
|
|
this.leSupport = support;
|
|
}
|
|
|
|
public boolean isBLE() {
|
|
return huaweiType == HuaweiDeviceType.AW || huaweiType == HuaweiDeviceType.BLE || huaweiType == HuaweiDeviceType.SMART;
|
|
}
|
|
|
|
public nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder createLeTransactionBuilder(String taskName) {
|
|
return leSupport.createTransactionBuilder(taskName);
|
|
}
|
|
|
|
public nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder createBrTransactionBuilder(String taskName) {
|
|
return brSupport.createTransactionBuilder(taskName);
|
|
}
|
|
|
|
public BluetoothGattCharacteristic getLeCharacteristic(UUID uuid) {
|
|
return leSupport.getCharacteristic(uuid);
|
|
}
|
|
|
|
public void performConnected(nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction transaction) throws IOException {
|
|
leSupport.performConnected(transaction);
|
|
}
|
|
|
|
public void performConnected(nodomain.freeyourgadget.gadgetbridge.service.btbr.Transaction transaction) throws IOException {
|
|
brSupport.performConnected(transaction);
|
|
}
|
|
|
|
public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) {
|
|
if (isBLE()) {
|
|
leSupport.evaluateGBDeviceEvent(deviceEvent);
|
|
} else {
|
|
brSupport.evaluateGBDeviceEvent(deviceEvent);
|
|
}
|
|
}
|
|
|
|
public void setGps(boolean start) {
|
|
if (start) {
|
|
if (!GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_SEND_GPS_TO_BAND, false))
|
|
return;
|
|
if (gpsParametersResponse == null) {
|
|
GetGpsParameterRequest gpsParameterRequest = new GetGpsParameterRequest(this);
|
|
gpsParameterRequest.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
GBLocationService.start(getContext(), getDevice(), GBLocationProviderType.GPS, 1000);
|
|
}
|
|
});
|
|
try {
|
|
gpsParameterRequest.doPerform();
|
|
} catch (IOException e) {
|
|
GB.toast(context, "Failed to get GPS parameters", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to get GPS parameters", e);
|
|
}
|
|
} else
|
|
GBLocationService.start(getContext(), getDevice(), GBLocationProviderType.GPS, 1000);
|
|
} else
|
|
GBLocationService.stop(getContext(), getDevice());
|
|
}
|
|
|
|
public void setGpsParametersResponse(GpsAndTime.GpsParameters.Response response) {
|
|
this.gpsParametersResponse = response;
|
|
}
|
|
|
|
protected nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder initializeDevice(nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder builder) {
|
|
this.gbDevice = leSupport.getDevice();
|
|
this.context = leSupport.getContext();
|
|
this.huaweiType = getCoordinator().getHuaweiType();
|
|
this.paramsProvider.setTransactionsCrypted(this.getHuaweiCoordinator().isTransactionCrypted());
|
|
builder.setCallback(leSupport);
|
|
builder.notify(leSupport.getCharacteristic(HuaweiConstants.UUID_CHARACTERISTIC_HUAWEI_READ), true);
|
|
builder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction(getDevice(), GBDevice.State.AUTHENTICATING, getContext()));
|
|
final GetLinkParamsRequest linkParamsReq = new GetLinkParamsRequest(this, builder);
|
|
initializeDevice(linkParamsReq);
|
|
getCoordinator().setDevice(this.gbDevice);
|
|
return builder;
|
|
}
|
|
|
|
protected nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder initializeDevice(nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder builder) {
|
|
this.gbDevice = brSupport.getDevice();
|
|
this.context = brSupport.getContext();
|
|
this.huaweiType = getCoordinator().getHuaweiType();
|
|
this.paramsProvider.setTransactionsCrypted(this.getHuaweiCoordinator().isTransactionCrypted());
|
|
builder.setCallback(brSupport);
|
|
builder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetDeviceStateAction(getDevice(), GBDevice.State.AUTHENTICATING, getContext()));
|
|
final GetLinkParamsRequest linkParamsReq = new GetLinkParamsRequest(this, builder);
|
|
initializeDevice(linkParamsReq);
|
|
getCoordinator().setDevice(this.gbDevice);
|
|
return builder;
|
|
}
|
|
|
|
protected void initializeDevice(final Request linkParamsReq) {
|
|
deviceMac = this.gbDevice.getAddress();
|
|
createRandomMacAddress();
|
|
createAndroidID();
|
|
try {
|
|
RequestCallback finalizeReq = new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
initializeDeviceCheckStatus(linkParamsReq);
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Link params TLV exception", e);
|
|
}
|
|
};
|
|
linkParamsReq.setFinalizeReq(finalizeReq);
|
|
linkParamsReq.doPerform();
|
|
} catch (IOException e) {
|
|
GB.toast(context, "Initialization of authenticating to Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Initialization of authenticating to Huawei device failed", e);
|
|
}
|
|
|
|
/* This is to have the setting match the default Huawei behaviour */
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(getDeviceMac());
|
|
if (!sharedPrefs.contains(DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED)) {
|
|
sharedPrefs.edit().putBoolean(DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED, true).apply();
|
|
}
|
|
}
|
|
|
|
protected void initializeDeviceCheckStatus(final Request linkParamsReq) {
|
|
try {
|
|
final GetDeviceStatusRequest deviceStatusReq = new GetDeviceStatusRequest(this, true);
|
|
RequestCallback finalizeReq = new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
int status = (int)deviceStatusReq.status;
|
|
if (status == -0x01 || status == 0x00 || status == 0x01) {
|
|
initializeDeviceDealHiChain(linkParamsReq);
|
|
} else {
|
|
initializeDeviceNotify();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Status TLV exception", e);
|
|
}
|
|
};
|
|
if (huaweiType == HuaweiDeviceType.BLE) { //Only BLE known, check later for AW and SMART
|
|
initializeDeviceDealHiChain(linkParamsReq);
|
|
} else {
|
|
deviceStatusReq.setFinalizeReq(finalizeReq);
|
|
deviceStatusReq.doPerform();
|
|
}
|
|
} catch (IOException e) {
|
|
GB.toast(context, "Status of authenticating to Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
protected boolean isHiChain() {
|
|
// In HH
|
|
// HiChain : 1 || 3
|
|
// HiChainLite : 2 || 3 || 8
|
|
// HiChain3 : 4 & API>=23 - API is always >=23
|
|
// For GB we will consider for authMode
|
|
// 0 : No HiChain
|
|
// 1 or 3 : HiChain
|
|
// 2 or 8 : HiChainLite -> normal mode
|
|
// 4 : HiChain3
|
|
byte authMode = paramsProvider.getDeviceSupportType();
|
|
return authMode == 0x01 || authMode == 0x03 || authMode == 0x04 || isHiChainLite();
|
|
}
|
|
|
|
protected boolean isHiChainLite() {
|
|
byte authMode = paramsProvider.getDeviceSupportType();
|
|
return authMode == 0x02;
|
|
}
|
|
|
|
protected boolean isHiChain3(int authType) {
|
|
return (authType ^ 0x01) == 0x04 || (authType ^ 0x02) == 0x04;
|
|
}
|
|
|
|
protected void initializeDeviceDealHiChain(final Request linkParamsReq) {
|
|
try {
|
|
if (isHiChain()) {
|
|
final GetSecurityNegotiationRequest securityNegoReq = new GetSecurityNegotiationRequest(this);
|
|
RequestCallback securityFinalizeReq = new RequestCallback(this) {
|
|
@Override
|
|
public void call() {
|
|
if (securityNegoReq.authType == 0x0186A0 || isHiChain3(securityNegoReq.authType)) {
|
|
LOG.debug("HiChain mode");
|
|
initializeDeviceHiChainMode(securityNegoReq.authType);
|
|
} else if (securityNegoReq.authType == 0x01 || securityNegoReq.authType == 0x02) {
|
|
LOG.debug("HiChain Lite mode");
|
|
// Keep track the gadget is connected
|
|
initializeDeviceHiChainLiteMode(linkParamsReq);
|
|
}
|
|
}
|
|
};
|
|
securityNegoReq.setFinalizeReq(securityFinalizeReq);
|
|
securityNegoReq.doPerform();
|
|
} else {
|
|
LOG.debug("Normal mode");
|
|
initializeDeviceNormalMode(linkParamsReq);
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: use translatable string
|
|
GB.toast(context, "init Deal with HiChain of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Step of authenticating to Huawei device failed", e);
|
|
}
|
|
}
|
|
|
|
protected void initializeDeviceNotify() {} //TODO
|
|
|
|
RequestCallback configureReq = new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
initializeDeviceConfigure();
|
|
}
|
|
};
|
|
|
|
protected void initializeDeviceHiChainMode(int authType) {
|
|
try {
|
|
GetHiChainRequest hiChainReq = new GetHiChainRequest(this, firstConnection);
|
|
hiChainReq.setFinalizeReq(configureReq);
|
|
if (firstConnection) {
|
|
GetPincodeRequest pincodeReq = new GetPincodeRequest(this);
|
|
pincodeReq.nextRequest(hiChainReq);
|
|
pincodeReq.doPerform();
|
|
} else
|
|
hiChainReq.doPerform();
|
|
} catch (IOException e) {
|
|
GB.toast(context, "init HiCHain Mode of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
protected void initializeDeviceHiChainLiteMode(Request linkParamsReq) {
|
|
try {
|
|
createSecretKey();
|
|
GetAuthRequest authReq = new GetAuthRequest(this, linkParamsReq);
|
|
GetBondParamsRequest bondParamsReq = new GetBondParamsRequest(this);
|
|
GetBondRequest bondReq = new GetBondRequest(this);
|
|
authReq.nextRequest(bondParamsReq);
|
|
bondParamsReq.nextRequest(bondReq);
|
|
bondParamsReq.setFinalizeReq(configureReq);
|
|
bondReq.setFinalizeReq(configureReq);
|
|
if (paramsProvider.getPinCode() == null & paramsProvider.getAuthVersion() != 0x02) {
|
|
GetPincodeRequest pinCodeReq = new GetPincodeRequest(this);
|
|
pinCodeReq.nextRequest(authReq);
|
|
pinCodeReq.doPerform();
|
|
} else {
|
|
authReq.doPerform();
|
|
}
|
|
} catch (IOException e) {
|
|
GB.toast(context, "init HiCHainLite Mode Mode of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
protected void initializeDeviceNormalMode(Request linkParamsReq) {
|
|
try {
|
|
createSecretKey();
|
|
GetAuthRequest authReq = new GetAuthRequest(this, linkParamsReq);
|
|
if (getHuaweiType() == HuaweiDeviceType.BLE || getHuaweiType() == HuaweiDeviceType.AW) {
|
|
GetBondParamsRequest bondParamsReq = new GetBondParamsRequest(this);
|
|
GetBondRequest bondReq = new GetBondRequest(this);
|
|
authReq.nextRequest(bondParamsReq);
|
|
bondParamsReq.nextRequest(bondReq);
|
|
bondParamsReq.setFinalizeReq(configureReq);
|
|
bondReq.setFinalizeReq(configureReq);
|
|
} else {
|
|
authReq.setFinalizeReq(configureReq);
|
|
}
|
|
authReq.doPerform();
|
|
} catch (IOException e) {
|
|
GB.toast(context, "init Normal Mode of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
e.printStackTrace();
|
|
}
|
|
|
|
}
|
|
|
|
protected void initializeDeviceConfigure() {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder leBuilder = null;
|
|
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder brBuilder = null;
|
|
if (isBLE()) {
|
|
leBuilder = createLeTransactionBuilder("Initializing");
|
|
leBuilder.setCallback(leSupport);
|
|
leBuilder.notify(leSupport.getCharacteristic(HuaweiConstants.UUID_CHARACTERISTIC_HUAWEI_READ), true);
|
|
leBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction(gbDevice, GBDevice.State.INITIALIZING, context));
|
|
} else {
|
|
brBuilder = createBrTransactionBuilder("Initializing");
|
|
brBuilder.setCallback(brSupport);
|
|
brBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetDeviceStateAction(gbDevice, GBDevice.State.INITIALIZING, context));
|
|
}
|
|
try {
|
|
GetProductInformationRequest productInformationReq = new GetProductInformationRequest(this);
|
|
Request setTimeReq = setTime();
|
|
GetSupportedServicesRequest supportedServicesReq = new GetSupportedServicesRequest(this);
|
|
productInformationReq.nextRequest(setTimeReq);
|
|
setTimeReq.nextRequest(supportedServicesReq);
|
|
productInformationReq.doPerform();
|
|
if (firstConnection) {
|
|
// Workaround to enable PREF_HUAWEI_ROTATE_WRIST_TO_SWITCH_INFO preference
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
SharedPreferences.Editor editor = sharedPrefs.edit();
|
|
editor.putString(DeviceSettingsPreferenceConst.PREF_ACTIVATE_DISPLAY_ON_LIFT, "p_on");
|
|
editor.apply();
|
|
}
|
|
onSetTime();
|
|
getBatteryLevel();
|
|
if (isBLE()) {
|
|
assert leBuilder != null;
|
|
leBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction(gbDevice, GBDevice.State.INITIALIZED, context));
|
|
leSupport.performConnected(leBuilder.getTransaction());
|
|
} else {
|
|
assert brBuilder != null;
|
|
brBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetDeviceStateAction(gbDevice, GBDevice.State.INITIALIZED, context));
|
|
brSupport.performConnected(brBuilder.getTransaction());
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Final initialization of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Final initialization of Huawei device failed", e);
|
|
}
|
|
}
|
|
|
|
public void createSecretKey() {
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
|
|
String authKey = sharedPrefs.getString("authkey", null);
|
|
if (authKey == null || authKey.isEmpty()) {
|
|
SharedPreferences.Editor editor = sharedPrefs.edit();
|
|
|
|
authKey = StringUtils.bytesToHex(HuaweiCrypto.generateNonce());
|
|
editor.putString("authkey", authKey);
|
|
editor.apply();
|
|
}
|
|
paramsProvider.setSecretKey(GB.hexStringToByteArray(authKey));
|
|
}
|
|
|
|
public byte[] getSecretKey() {
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
|
|
String authKey = sharedPrefs.getString("authkey", null);
|
|
return GB.hexStringToByteArray(authKey);
|
|
}
|
|
|
|
public void setSecretKey(byte[] authKey) {
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
|
|
SharedPreferences.Editor editor = sharedPrefs.edit();
|
|
|
|
editor.putString("authkey", StringUtils.bytesToHex(authKey));
|
|
editor.apply();
|
|
paramsProvider.setSecretKey(authKey);
|
|
}
|
|
|
|
public HuaweiCoordinatorSupplier.HuaweiDeviceType getHuaweiType() {
|
|
return this.huaweiType;
|
|
}
|
|
|
|
public HuaweiPacket.ParamsProvider getParamsProvider() {
|
|
return paramsProvider;
|
|
}
|
|
|
|
public void setFirstConnection(boolean firstConnection) {
|
|
this.firstConnection = firstConnection;
|
|
}
|
|
|
|
protected void createRandomMacAddress() {
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
|
|
macAddress = sharedPrefs.getString(HuaweiConstants.PREF_HUAWEI_ADDRESS, null);
|
|
if (macAddress == null || macAddress.isEmpty()) {
|
|
StringBuilder mac = new StringBuilder("FF:FF:FF");
|
|
Random r = new Random();
|
|
for (int i = 0; i < 3; i++) {
|
|
int n = r.nextInt(255);
|
|
mac.append(String.format(":%02x", n));
|
|
}
|
|
macAddress = mac.toString().toUpperCase();
|
|
SharedPreferences.Editor editor = sharedPrefs.edit();
|
|
editor.putString(HuaweiConstants.PREF_HUAWEI_ADDRESS, macAddress);
|
|
editor.apply();
|
|
}
|
|
}
|
|
|
|
public byte[] getMacAddress() {
|
|
return macAddress.getBytes(StandardCharsets.UTF_8);
|
|
}
|
|
|
|
public byte[] getSerial() {
|
|
return macAddress.replace(":", "").substring(6, 12).getBytes(StandardCharsets.UTF_8);
|
|
}
|
|
|
|
public String getDeviceMac() {
|
|
return deviceMac;
|
|
}
|
|
|
|
protected void createAndroidID() {
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
|
|
androidID = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_FAKE_ANDROID_ID, null);
|
|
if (androidID == null || androidID.isEmpty()) {
|
|
androidID = StringUtils.bytesToHex(HuaweiCrypto.generateNonce());
|
|
LOG.debug("Created androidID: " + androidID);
|
|
SharedPreferences.Editor editor = sharedPrefs.edit();
|
|
editor.putString(DeviceSettingsPreferenceConst.PREF_FAKE_ANDROID_ID, androidID);
|
|
editor.apply();
|
|
}
|
|
}
|
|
|
|
public byte[] getAndroidId() {
|
|
return androidID.getBytes(StandardCharsets.UTF_8);
|
|
}
|
|
|
|
public Context getContext() {
|
|
return context;
|
|
}
|
|
|
|
public GBDevice getDevice() {
|
|
return gbDevice;
|
|
}
|
|
|
|
/**
|
|
* Initialize the services that may or may not be supported on the device
|
|
* To be called after the commandsPerService is filled in the coordinator
|
|
*/
|
|
public void initializeDynamicServices() {
|
|
|
|
// Setup the alarms
|
|
if (!getHuaweiCoordinator().supportsChangingAlarm()) {
|
|
if (firstConnection) {
|
|
// TODO: not really sure if this is necessary, but it probably won't do any harm
|
|
initializeAlarms();
|
|
}
|
|
} else {
|
|
getAlarms();
|
|
}
|
|
try {
|
|
if (getHuaweiCoordinator().supportsExpandCapability()) {
|
|
GetExpandCapabilityRequest expandCapabilityReq = new GetExpandCapabilityRequest(this);
|
|
expandCapabilityReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsAccountJudgment() && getHuaweiCoordinator().supportsAccountSwitch()) {
|
|
SendExtendedAccountRequest sendExtendedAccountRequest = new SendExtendedAccountRequest(this);
|
|
sendExtendedAccountRequest.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsSettingRelated()) { // GetSettingRelated
|
|
GetSettingRelatedRequest getSettingRelatedReq = new GetSettingRelatedRequest(this);
|
|
getSettingRelatedReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsAcceptAgreement()) {
|
|
AcceptAgreementsRequest acceptAgreementsRequest = new AcceptAgreementsRequest(this);
|
|
acceptAgreementsRequest.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsActivityType()) {
|
|
GetActivityTypeRequest activityTypeReq = new GetActivityTypeRequest(this);
|
|
activityTypeReq.doPerform();
|
|
}
|
|
|
|
if (getHuaweiCoordinator().supportsConnectStatus()) {
|
|
GetConnectStatusRequest getConnectStatusReq = new GetConnectStatusRequest(this);
|
|
getConnectStatusReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsActivateOnLift()) {
|
|
setActivateOnLift();
|
|
}
|
|
if (getHuaweiCoordinator().supportsWearLocation(getDevice())) {
|
|
setWearLocation();
|
|
}
|
|
if (getHuaweiCoordinator().supportsRotateToCycleInfo()) {
|
|
setNavigateOnRotate();
|
|
}
|
|
if (getHuaweiCoordinator().supportsQueryDndLiftWristDisturbType()) {
|
|
GetDndLiftWristTypeRequest getDndLiftWristTypeReq = new GetDndLiftWristTypeRequest(this);
|
|
getDndLiftWristTypeReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsDoNotDisturb(gbDevice)) {
|
|
SendDndDeleteRequest sendDndDeleteReq = new SendDndDeleteRequest(this);
|
|
SendDndAddRequest sendDndAddReq = new SendDndAddRequest(this);
|
|
sendDndDeleteReq.nextRequest(sendDndAddReq);
|
|
sendDndDeleteReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsNotification()) { // 0x02 - 0x04
|
|
setNotificationStatus();
|
|
}
|
|
if (getHuaweiCoordinator().supportsDoNotDisturb(gbDevice) && getHuaweiCoordinator().supportsWearMessagePush()) {
|
|
setDndNotWear();
|
|
}
|
|
if (getHuaweiCoordinator().supportsTimeAndZoneId()) {
|
|
setTimeZoneId();
|
|
}
|
|
// Nothing usefull yet with this requests
|
|
if (getHuaweiCoordinator().supportsMultiDevice()) {
|
|
SendSetUpDeviceStatusRequest sendSetUpDeviceStatusReq = new SendSetUpDeviceStatusRequest(this);
|
|
sendSetUpDeviceStatusReq.doPerform();
|
|
GetWearStatusRequest getWearStatusReq = new GetWearStatusRequest(this);
|
|
getWearStatusReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsMenstrual()) {
|
|
SendMenstrualCapabilityRequest sendMenstrualCapabilityReq = new SendMenstrualCapabilityRequest(this);
|
|
sendMenstrualCapabilityReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsLanguageSetting()) { // 0x0c - 0x01
|
|
setLanguageSetting();
|
|
}
|
|
if (getHuaweiCoordinator().supportsWorkoutsTrustHeartRate()) {
|
|
SendNotifyHeartRateCapabilityRequest sendNotifyHeartRateCapabilityReq = new SendNotifyHeartRateCapabilityRequest(this);
|
|
sendNotifyHeartRateCapabilityReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsFitnessRestHeartRate()) {
|
|
SendNotifyRestHeartRateCapabilityRequest sendNotifyRestHeartRateCapabilityReq = new SendNotifyRestHeartRateCapabilityRequest(this);
|
|
sendNotifyRestHeartRateCapabilityReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsFitnessThresholdValue()) {
|
|
SetMediumToStrengthThresholdRequest setMediumToStrengthThresholdReq = new SetMediumToStrengthThresholdRequest(this);
|
|
setMediumToStrengthThresholdReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsDateFormat()) { //0x01 - 0x04
|
|
setDateFormat();
|
|
}
|
|
if (getHuaweiCoordinator().supportsMotionGoal()) {
|
|
SendFitnessGoalRequest sendFitnessGoalReq = new SendFitnessGoalRequest(this);
|
|
sendFitnessGoalReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsActivityReminder()) {
|
|
setActivityReminder();
|
|
}
|
|
if (getHuaweiCoordinator().supportsTruSleep()) {
|
|
setTrusleep();
|
|
}
|
|
if (getHuaweiCoordinator().supportsPromptPushMessage() && getProtocolVersion() == 2) {
|
|
GetNotificationCapabilitiesRequest getNotificationCapabilitiesReq = new GetNotificationCapabilitiesRequest(this);
|
|
getNotificationCapabilitiesReq.doPerform();
|
|
}
|
|
if (getHuaweiCoordinator().supportsNotificationAlert() && getProtocolVersion() == 2) {
|
|
GetNotificationConstraintsRequest getNotificationConstraintsReq = new GetNotificationConstraintsRequest(this);
|
|
getNotificationConstraintsReq.doPerform();
|
|
}
|
|
|
|
if (getHuaweiCoordinator().supportsWatchfaceParams()) {
|
|
GetWatchfaceParams getWatchfaceParams = new GetWatchfaceParams(this);
|
|
getWatchfaceParams.doPerform();
|
|
}
|
|
} catch (IOException e) {
|
|
GB.toast(getContext(), "Initialize dynamic services of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR,
|
|
e);
|
|
e.printStackTrace();
|
|
}
|
|
|
|
// Properly update the device card
|
|
gbDevice.sendDeviceUpdateIntent(GBApplication.getContext());
|
|
GB.signalActivityDataFinish();
|
|
}
|
|
|
|
public void setProtocolVersion(byte protocolVersion) {
|
|
this.protocolVersion = protocolVersion;
|
|
}
|
|
|
|
public byte getProtocolVersion() {
|
|
return this.protocolVersion;
|
|
}
|
|
|
|
private void initializeAlarms() {
|
|
// TODO: check for smart alarm && overwrite for smart alarm
|
|
// note that lowering the alarm count shouldn't delete the alarm of course...
|
|
|
|
// Populate alarms in order to specify important data
|
|
List<Alarm> alarms = DBHelper.getAlarms(gbDevice);
|
|
DeviceCoordinator coordinator = this.gbDevice.getDeviceCoordinator();
|
|
int supportedNumAlarms = coordinator.getAlarmSlotCount(gbDevice);
|
|
if (alarms.size() == 0) {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
DaoSession daoSession = db.getDaoSession();
|
|
Device device = DBHelper.getDevice(gbDevice, daoSession);
|
|
User user = DBHelper.getUser(daoSession);
|
|
for (int position = 0; position < supportedNumAlarms; position++) {
|
|
LOG.info("adding missing alarm at position " + position);
|
|
DBHelper.store(createDefaultAlarm(device, user, position));
|
|
}
|
|
} catch (Exception e) {
|
|
// TODO: show user?
|
|
// TODO: What exceptions can happen here?
|
|
LOG.error("Error accessing database", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Alarm createDefaultAlarm(@NonNull Device device, @NonNull User user, int position) {
|
|
boolean smartWakeup = false;
|
|
String title = context.getString(R.string.menuitem_alarm);
|
|
String description = context.getString(R.string.huawei_alarm_event_description);
|
|
if (position == 0) {
|
|
smartWakeup = true;
|
|
title = context.getString(R.string.alarm_smart_wakeup);
|
|
description = context.getString(R.string.huawei_alarm_smart_description);
|
|
}
|
|
return new Alarm(device.getId(), user.getId(), position, false, smartWakeup, null, false, 0, 6, 30, true, title, description);
|
|
}
|
|
|
|
private void getAlarms() {
|
|
if (!getHuaweiCoordinator().supportsChangingAlarm())
|
|
return;
|
|
|
|
GetEventAlarmList getEventAlarmList = new GetEventAlarmList(this);
|
|
responseManager.addHandler(getEventAlarmList);
|
|
getEventAlarmList.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
if (!getHuaweiCoordinator().supportsSmartAlarm(getDevice()))
|
|
return; // Don't get smart alarms when not supported
|
|
|
|
GetSmartAlarmList getSmartAlarmList = new GetSmartAlarmList(HuaweiSupportProvider.this);
|
|
responseManager.addHandler(getSmartAlarmList);
|
|
try {
|
|
getSmartAlarmList.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Error sending smart alarm list request", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Error sending smart alarm list request", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Error parsing event list", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Error parsing event list", e);
|
|
}
|
|
});
|
|
try {
|
|
getEventAlarmList.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Error sending event alarm list request", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Error sending event alarm list request", e);
|
|
}
|
|
}
|
|
|
|
public void saveAlarms(Alarm[] alarms) {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
DaoSession daoSession = db.getDaoSession();
|
|
Device device = DBHelper.getDevice(gbDevice, daoSession);
|
|
User user = DBHelper.getUser(daoSession);
|
|
for (Alarm alarm : alarms) {
|
|
alarm.setDeviceId(device.getId());
|
|
alarm.setUserId(user.getId());
|
|
DBHelper.store(alarm);
|
|
}
|
|
} catch (Exception e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Error saving alarms", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Error saving alarms", e);
|
|
}
|
|
}
|
|
|
|
public boolean onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
|
|
byte[] data = characteristic.getValue();
|
|
responseManager.handleData(data);
|
|
return true;
|
|
}
|
|
|
|
public void onSocketRead(byte[] data) {
|
|
//Check multiple packet in data
|
|
ByteBuffer bData = ByteBuffer.wrap(data);
|
|
while (bData.remaining() != 0x00) {
|
|
int dataLen = bData.getShort(bData.position() + 1) + 0x05; // magic + len + CRC
|
|
byte[] newData = new byte[dataLen];
|
|
bData.get(newData, 0, dataLen);
|
|
responseManager.handleData(newData);
|
|
}
|
|
}
|
|
|
|
public void removeInProgressRequests(Request req) {
|
|
responseManager.removeHandler(req);
|
|
}
|
|
|
|
public void onSendConfiguration(String config) {
|
|
try {
|
|
switch (config) {
|
|
case DeviceSettingsPreferenceConst.PREF_DATEFORMAT:
|
|
case DeviceSettingsPreferenceConst.PREF_TIMEFORMAT: {
|
|
setDateFormat();
|
|
break;
|
|
}
|
|
case SettingsActivity.PREF_MEASUREMENT_SYSTEM:
|
|
case DeviceSettingsPreferenceConst.PREF_LANGUAGE: {
|
|
setLanguageSetting();
|
|
break;
|
|
}
|
|
case DeviceSettingsPreferenceConst.PREF_WEARLOCATION: {
|
|
setWearLocation();
|
|
break;
|
|
}
|
|
case DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED: {
|
|
setActivateOnLift();
|
|
break;
|
|
}
|
|
case MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO: {
|
|
setNavigateOnRotate();
|
|
break;
|
|
}
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_ENABLE:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_THRESHOLD:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_START:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_END:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_MO:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_TU:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_WE:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_TH:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_FR:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_SA:
|
|
case DeviceSettingsPreferenceConst.PREF_INACTIVITY_SU: {
|
|
setActivityReminder();
|
|
break;
|
|
}
|
|
case HuaweiConstants.PREF_HUAWEI_TRUSLEEP: {
|
|
setTrusleep();
|
|
break;
|
|
}
|
|
case DeviceSettingsPreferenceConst.PREF_NOTIFICATION_ENABLE: {
|
|
setNotificationStatus();
|
|
break;
|
|
}
|
|
case HuaweiConstants.PREF_HUAWEI_WORKMODE:
|
|
SetWorkModeRequest setWorkModeReq = new SetWorkModeRequest(this);
|
|
setWorkModeReq.doPerform();
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_START:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_END:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_LIFT_WRIST:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_MO:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_TU:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_WE:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_TH:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_FR:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_SA:
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_SU: {
|
|
setDnd();
|
|
break;
|
|
}
|
|
case DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOT_WEAR:
|
|
setDndNotWear();
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_FIND_PHONE:
|
|
case DeviceSettingsPreferenceConst.PREF_FIND_PHONE_DURATION:
|
|
// TODO: enable/disable the find phone applet on band
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED:
|
|
setDisconnectNotification();
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_HEARTRATE_AUTOMATIC_ENABLE:
|
|
setHeartrateAutomatic();
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_SPO_AUTOMATIC_ENABLE:
|
|
setSpoAutomatic();
|
|
break;
|
|
case DeviceSettingsPreferenceConst.PREF_FORCE_ENABLE_SMART_ALARM:
|
|
getAlarms();
|
|
break;
|
|
case HuaweiConstants.PREF_HUAWEI_DEBUG_REQUEST:
|
|
sendDebugRequest();
|
|
break;
|
|
case ActivityUser.PREF_USER_STEPS_GOAL:
|
|
new SendFitnessGoalRequest(this).doPerform();
|
|
break;
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Configuration of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Configuration of Huawei device failed", e);
|
|
// TODO: handle this?
|
|
}
|
|
}
|
|
|
|
public void onFetchRecordedData(int dataTypes) {
|
|
if (gbDevice.isBusy()) {
|
|
LOG.warn("Device is already busy with " + gbDevice.getBusyTask() + ", so won't fetch data now.");
|
|
// TODO: better way of letting user know?
|
|
// TODO: use string that can be translated
|
|
GB.toast("Device is already busy with " + gbDevice.getBusyTask() + ", so won't fetch data now.", Toast.LENGTH_LONG, 0);
|
|
return;
|
|
}
|
|
|
|
// TODO: An exception during the parsing can leave GB thinking that the sync is not yet
|
|
// finished, but it won't ever complete because of the parsing exception
|
|
// Maybe this can be fixed with an exception handler from the callback? If then
|
|
// called from the ResponseManager, it may not be too much work to implement.
|
|
|
|
if ((dataTypes & RecordedDataTypes.TYPE_ACTIVITY) != 0) {
|
|
fetchActivityData();
|
|
} else if (dataTypes == RecordedDataTypes.TYPE_GPS_TRACKS) {
|
|
fetchWorkoutData();
|
|
} else {
|
|
// TODO: tell user
|
|
LOG.warn("Recorded data type {} not implemented yet.", dataTypes);
|
|
}
|
|
|
|
// Get the battery level, as that isn't shared nicely for now
|
|
getBatteryLevel();
|
|
|
|
// Get the alarms as they cannot be retrieved on opening the alarm window
|
|
// TODO: get the alarms if the alarm settings are opened instead of here
|
|
getAlarms();
|
|
}
|
|
|
|
private void fetchActivityData() {
|
|
int sleepStart = 0;
|
|
int stepStart = 0;
|
|
int end = (int) (System.currentTimeMillis() / 1000);
|
|
|
|
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
|
|
long prefLastSyncTime = sharedPreferences.getLong("lastSyncTimeMillis", 0);
|
|
if (prefLastSyncTime != 0) {
|
|
sleepStart = (int) (prefLastSyncTime / 1000);
|
|
stepStart = (int) (prefLastSyncTime / 1000);
|
|
|
|
// Reset for next calls
|
|
sharedPreferences.edit().putLong("lastSyncTimeMillis", 0).apply();
|
|
} else {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
HuaweiSampleProvider sampleProvider = new HuaweiSampleProvider(gbDevice, db.getDaoSession());
|
|
sleepStart = sampleProvider.getLastSleepFetchTimestamp();
|
|
stepStart = sampleProvider.getLastStepFetchTimestamp();
|
|
} catch (Exception e) {
|
|
LOG.warn("Exception for getting start times, using 01/01/2000 - 00:00:00.");
|
|
}
|
|
|
|
// Some bands don't work with zero timestamp, so starting later
|
|
if (sleepStart == 0)
|
|
sleepStart = 946684800;
|
|
if (stepStart == 0)
|
|
stepStart = 946684800;
|
|
}
|
|
final GetSleepDataCountRequest getSleepDataCountRequest;
|
|
if (isBLE()) {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder leBuilder = createLeTransactionBuilder("FetchRecordedData");
|
|
leBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction(gbDevice, context.getString(R.string.busy_task_fetch_activity_data), context));
|
|
getSleepDataCountRequest = new GetSleepDataCountRequest(this, leBuilder, sleepStart, end);
|
|
} else {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder brBuilder = createBrTransactionBuilder("FetchRecordedData");
|
|
brBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetDeviceBusyAction(gbDevice, context.getString(R.string.busy_task_fetch_activity_data), context));
|
|
getSleepDataCountRequest = new GetSleepDataCountRequest(this, brBuilder, sleepStart, end);
|
|
}
|
|
|
|
final GetStepDataCountRequest getStepDataCountRequest = new GetStepDataCountRequest(this, stepStart, end);
|
|
final GetFitnessTotalsRequest getFitnessTotalsRequest = new GetFitnessTotalsRequest(this);
|
|
|
|
getFitnessTotalsRequest.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
handleSyncFinished();
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Fitness totals exception", e);
|
|
handleSyncFinished();
|
|
}
|
|
});
|
|
|
|
getStepDataCountRequest.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
try {
|
|
getFitnessTotalsRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Exception on starting fitness totals request", e);
|
|
handleSyncFinished();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Step data count exception", e);
|
|
handleSyncFinished();
|
|
}
|
|
});
|
|
|
|
getSleepDataCountRequest.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
try {
|
|
getStepDataCountRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Exception on starting step data count request", e);
|
|
handleSyncFinished();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Sleep data count exception", e);
|
|
handleSyncFinished();
|
|
}
|
|
});
|
|
|
|
try {
|
|
getSleepDataCountRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Exception on starting sleep data count request", e);
|
|
handleSyncFinished();
|
|
}
|
|
}
|
|
|
|
private void fetchWorkoutData() {
|
|
int start = 0;
|
|
int end = (int) (System.currentTimeMillis() / 1000);
|
|
|
|
SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
|
|
long prefLastSyncTime = sharedPreferences.getLong("lastSportsActivityTimeMillis", 0);
|
|
if (prefLastSyncTime != 0) {
|
|
start = (int) (prefLastSyncTime / 1000);
|
|
|
|
// Reset for next calls
|
|
sharedPreferences.edit().putLong("lastSportsActivityTimeMillis", 0).apply();
|
|
} else {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
|
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
|
|
|
QueryBuilder<HuaweiWorkoutSummarySample> qb1 = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
|
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
|
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId)
|
|
).orderDesc(
|
|
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp
|
|
).limit(1);
|
|
|
|
List<HuaweiWorkoutSummarySample> samples1 = qb1.list();
|
|
if (!samples1.isEmpty())
|
|
start = samples1.get(0).getEndTimestamp();
|
|
|
|
QueryBuilder<BaseActivitySummary> qb2 = db.getDaoSession().getBaseActivitySummaryDao().queryBuilder().where(
|
|
BaseActivitySummaryDao.Properties.DeviceId.eq(deviceId),
|
|
BaseActivitySummaryDao.Properties.UserId.eq(userId)
|
|
).orderDesc(
|
|
BaseActivitySummaryDao.Properties.StartTime
|
|
).limit(1);
|
|
|
|
List<BaseActivitySummary> samples2 = qb2.list();
|
|
if (!samples2.isEmpty())
|
|
start = Math.min(start, (int) (samples2.get(0).getEndTime().getTime() / 1000L));
|
|
|
|
start = start + 1;
|
|
} catch (Exception e) {
|
|
LOG.warn("Exception for getting start time, using 10/06/2022 - 00:00:00.");
|
|
}
|
|
|
|
if (start == 0 || start == 1)
|
|
start = 1654819200;
|
|
}
|
|
|
|
final GetWorkoutCountRequest getWorkoutCountRequest;
|
|
if (isBLE()) {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder leBuilder = createLeTransactionBuilder("FetchWorkoutData");
|
|
// TODO: maybe use a different string from the other synchronization
|
|
leBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction(gbDevice, context.getString(R.string.busy_task_fetch_activity_data), context));
|
|
getWorkoutCountRequest = new GetWorkoutCountRequest(this, leBuilder, start, end);
|
|
} else {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder brBuilder = createBrTransactionBuilder("FetchWorkoutData");
|
|
// TODO: maybe use a different string from the other synchronization
|
|
brBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetDeviceBusyAction(gbDevice, context.getString(R.string.busy_task_fetch_activity_data), context));
|
|
getWorkoutCountRequest = new GetWorkoutCountRequest(this, brBuilder, start, end);
|
|
}
|
|
|
|
getWorkoutCountRequest.setFinalizeReq(new RequestCallback() {
|
|
@Override
|
|
public void call() {
|
|
handleSyncFinished();
|
|
}
|
|
|
|
@Override
|
|
public void handleException(Request.ResponseParseException e) {
|
|
LOG.error("Workout parsing exception", e);
|
|
handleSyncFinished();
|
|
}
|
|
});
|
|
|
|
try {
|
|
getWorkoutCountRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Exception on starting workout count request", e);
|
|
handleSyncFinished();
|
|
}
|
|
}
|
|
|
|
private void handleSyncFinished() {
|
|
if (gbDevice.isBusy()) {
|
|
gbDevice.unsetBusyTask();
|
|
gbDevice.sendDeviceUpdateIntent(context);
|
|
}
|
|
GB.signalActivityDataFinish();
|
|
}
|
|
|
|
public void onReset(int flags) {
|
|
try {
|
|
if(flags== GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET) {
|
|
SendFactoryResetRequest sendFactoryResetReq = new SendFactoryResetRequest(this);
|
|
sendFactoryResetReq.doPerform();
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Factory resetting Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Factory resetting Huawei device failed", e);
|
|
}
|
|
}
|
|
|
|
public void setNotificationStatus() {
|
|
/*
|
|
* TODO: this doesn't work as expected
|
|
* We thought it would disable(/enable) the notifications on the device side,
|
|
* but at least the disabling doesn't work - so we don't send notifications to the
|
|
* device at all if the setting is disabled now.
|
|
* TRYING to debug this as it should really be handled on device side...
|
|
*/
|
|
try {
|
|
SetNotificationRequest setNotificationReq = new SetNotificationRequest(this);
|
|
setNotificationReq.doPerform();
|
|
// SetWearMessagePushRequest setWearMessagePushReq = new SetWearMessagePushRequest(this);
|
|
// setWearMessagePushReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Setting notification failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Setting notification failed", e);
|
|
}
|
|
}
|
|
|
|
public short getNotificationId() {
|
|
if (msgId < 256) {
|
|
msgId += 1;
|
|
} else {
|
|
msgId = 0;
|
|
}
|
|
return msgId;
|
|
}
|
|
|
|
public void onNotification(NotificationSpec notificationSpec) {
|
|
if (!GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREF_NOTIFICATION_ENABLE, false)) {
|
|
// Don't send notifications when they are disabled
|
|
LOG.info("Stopped notification as they are disabled.");
|
|
return;
|
|
}
|
|
|
|
SendNotificationRequest sendNotificationReq = new SendNotificationRequest(this);
|
|
try {
|
|
sendNotificationReq.buildNotificationTLVFromNotificationSpec(notificationSpec);
|
|
sendNotificationReq.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Sending notification failed", e);
|
|
}
|
|
}
|
|
|
|
public void setDateFormat() {
|
|
try {
|
|
SetDateFormatRequest setDateFormatReq = new SetDateFormatRequest(this);
|
|
setDateFormatReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure date format", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure date format", e);
|
|
}
|
|
}
|
|
|
|
public void onSetTime() {
|
|
try {
|
|
setTime().doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure time", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure time", e);
|
|
}
|
|
}
|
|
|
|
private Request setTime() {
|
|
SetTimeRequest setTimeReq = new SetTimeRequest(this);
|
|
return setTimeReq;
|
|
}
|
|
|
|
public void setTimeZoneId() {
|
|
try {
|
|
SetTimeZoneIdRequest setTimeZoneIdReq = new SetTimeZoneIdRequest(this);
|
|
setTimeZoneIdReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure time and zoneId", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
}
|
|
}
|
|
|
|
public void onSetAlarms(ArrayList<? extends nodomain.freeyourgadget.gadgetbridge.model.Alarm> alarms) {
|
|
boolean smartAlarmEnabled = getHuaweiCoordinator().supportsSmartAlarm(getDevice());
|
|
|
|
AlarmsRequest smartAlarmReq = new AlarmsRequest(this, true);
|
|
AlarmsRequest eventAlarmReq = new AlarmsRequest(this, false);
|
|
for (nodomain.freeyourgadget.gadgetbridge.model.Alarm alarm : alarms) {
|
|
if (alarm.getPosition() == 0 && smartAlarmEnabled) {
|
|
smartAlarmReq.buildSmartAlarm(alarm);
|
|
} else {
|
|
eventAlarmReq.addEventAlarm(alarm, !smartAlarmEnabled);
|
|
}
|
|
}
|
|
try {
|
|
if (smartAlarmEnabled)
|
|
smartAlarmReq.doPerform();
|
|
eventAlarmReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure alarms", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure alarms", e);
|
|
}
|
|
}
|
|
|
|
public void onSetCallState(CallSpec callSpec) {
|
|
if (callSpec.command == CallSpec.CALL_INCOMING) {
|
|
SendNotificationRequest sendNotificationReq = new SendNotificationRequest(this);
|
|
try {
|
|
sendNotificationReq.buildNotificationTLVFromCallSpec(callSpec);
|
|
sendNotificationReq.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Failed to send start call notification", e);
|
|
}
|
|
} else if (
|
|
callSpec.command == CallSpec.CALL_ACCEPT ||
|
|
callSpec.command == CallSpec.CALL_START ||
|
|
callSpec.command == CallSpec.CALL_REJECT ||
|
|
callSpec.command == CallSpec.CALL_END
|
|
) {
|
|
StopNotificationRequest stopNotificationRequest = new StopNotificationRequest(this);
|
|
try {
|
|
stopNotificationRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Failed to send stop call notification", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void onSetMusicState(MusicStateSpec stateSpec) {
|
|
this.musicStateSpec = stateSpec;
|
|
sendSetMusic();
|
|
}
|
|
|
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
|
this.musicSpec = musicSpec;
|
|
sendSetMusic();
|
|
}
|
|
|
|
public void onSetPhoneVolume() {
|
|
// TODO: check when implemented in GB
|
|
|
|
// We get the audio volume manually, so ignoring the argument
|
|
sendSetMusic();
|
|
}
|
|
|
|
public void sendSetMusic() {
|
|
// This often gets called twice in a row because of onSetMusicState and onSetMusicInfo
|
|
// Maybe we can consolidate that into just one request?
|
|
SetMusicRequest setMusicRequest = new SetMusicRequest(this, this.musicStateSpec, this.musicSpec);
|
|
try {
|
|
setMusicRequest.doPerform();
|
|
} catch (IOException e) {
|
|
LOG.error("Failed to send set music request", e);
|
|
}
|
|
}
|
|
|
|
public void addInProgressRequest(Request request) {
|
|
responseManager.addHandler(request);
|
|
}
|
|
|
|
public void addSleepActivity(int timestamp, short duration, byte type) {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
|
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
|
HuaweiSampleProvider sampleProvider = new HuaweiSampleProvider(gbDevice, db.getDaoSession());
|
|
|
|
HuaweiActivitySample activitySample = new HuaweiActivitySample(
|
|
timestamp,
|
|
deviceId,
|
|
userId,
|
|
timestamp + duration,
|
|
FitnessData.MessageData.sleepId,
|
|
type,
|
|
1,
|
|
ActivitySample.NOT_MEASURED,
|
|
ActivitySample.NOT_MEASURED,
|
|
ActivitySample.NOT_MEASURED,
|
|
ActivitySample.NOT_MEASURED,
|
|
ActivitySample.NOT_MEASURED
|
|
);
|
|
activitySample.setProvider(sampleProvider);
|
|
|
|
sampleProvider.addGBActivitySample(activitySample);
|
|
} catch (Exception e) {
|
|
LOG.error("Failed to add sleep activity to database", e);
|
|
}
|
|
}
|
|
|
|
public void addStepData(int timestamp, short steps, short calories, short distance, byte spo, byte heartrate) {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
|
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
|
HuaweiSampleProvider sampleProvider = new HuaweiSampleProvider(gbDevice, db.getDaoSession());
|
|
|
|
HuaweiActivitySample activitySample = new HuaweiActivitySample(
|
|
timestamp,
|
|
deviceId,
|
|
userId,
|
|
timestamp + 60,
|
|
FitnessData.MessageData.stepId,
|
|
ActivitySample.NOT_MEASURED,
|
|
1,
|
|
steps,
|
|
calories,
|
|
distance,
|
|
spo,
|
|
heartrate
|
|
);
|
|
activitySample.setProvider(sampleProvider);
|
|
|
|
sampleProvider.addGBActivitySample(activitySample);
|
|
} catch (Exception e) {
|
|
LOG.error("Failed to add step data to database", e);
|
|
}
|
|
}
|
|
|
|
public void addTotalFitnessData(int steps, int calories, int distance) {
|
|
LOG.debug("FITNESS total steps: " + steps);
|
|
LOG.debug("FITNESS total calories: " + calories); // TODO: May actually be kilocalories
|
|
LOG.debug("FITNESS total distance: " + distance + " m");
|
|
|
|
// TODO: potentially do more with this, maybe through realtime data?
|
|
}
|
|
|
|
public Long addWorkoutTotalsData(Workout.WorkoutTotals.Response packet) {
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
Long userId = DBHelper.getUser(db.getDaoSession()).getId();
|
|
Long deviceId = DBHelper.getDevice(gbDevice, db.getDaoSession()).getId();
|
|
|
|
// Avoid duplicates
|
|
QueryBuilder<HuaweiWorkoutSummarySample> qb = db.getDaoSession().getHuaweiWorkoutSummarySampleDao().queryBuilder().where(
|
|
HuaweiWorkoutSummarySampleDao.Properties.UserId.eq(userId),
|
|
HuaweiWorkoutSummarySampleDao.Properties.DeviceId.eq(deviceId),
|
|
HuaweiWorkoutSummarySampleDao.Properties.WorkoutNumber.eq(packet.number),
|
|
HuaweiWorkoutSummarySampleDao.Properties.StartTimestamp.eq(packet.startTime),
|
|
HuaweiWorkoutSummarySampleDao.Properties.EndTimestamp.eq(packet.endTime)
|
|
);
|
|
List<HuaweiWorkoutSummarySample> results = qb.build().list();
|
|
Long workoutId = null;
|
|
if (!results.isEmpty())
|
|
workoutId = results.get(0).getWorkoutId();
|
|
|
|
byte[] raw;
|
|
if (packet.rawData == null)
|
|
raw = null;
|
|
else
|
|
raw = StringUtils.bytesToHex(packet.rawData).getBytes(StandardCharsets.UTF_8);
|
|
|
|
HuaweiWorkoutSummarySample summarySample = new HuaweiWorkoutSummarySample(
|
|
workoutId,
|
|
deviceId,
|
|
userId,
|
|
packet.number,
|
|
packet.status,
|
|
packet.startTime,
|
|
packet.endTime,
|
|
packet.calories,
|
|
packet.distance,
|
|
packet.stepCount,
|
|
packet.totalTime,
|
|
packet.duration,
|
|
packet.type,
|
|
packet.strokes,
|
|
packet.avgStrokeRate,
|
|
packet.poolLength,
|
|
packet.laps,
|
|
packet.avgSwolf,
|
|
raw
|
|
);
|
|
db.getDaoSession().getHuaweiWorkoutSummarySampleDao().insertOrReplace(summarySample);
|
|
|
|
return summarySample.getWorkoutId();
|
|
} catch (Exception e) {
|
|
LOG.error("Failed to add workout totals data to database", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void addWorkoutSampleData(Long workoutId, List<Workout.WorkoutData.Response.Data> dataList) {
|
|
if (workoutId == null)
|
|
return;
|
|
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
HuaweiWorkoutDataSampleDao dao = db.getDaoSession().getHuaweiWorkoutDataSampleDao();
|
|
|
|
for (Workout.WorkoutData.Response.Data data : dataList) {
|
|
byte[] unknown;
|
|
if (data.unknownData == null)
|
|
unknown = null;
|
|
else
|
|
unknown = StringUtils.bytesToHex(data.unknownData).getBytes(StandardCharsets.UTF_8);
|
|
|
|
HuaweiWorkoutDataSample dataSample = new HuaweiWorkoutDataSample(
|
|
workoutId,
|
|
data.timestamp,
|
|
data.heartRate,
|
|
data.speed,
|
|
data.stepRate,
|
|
data.cadence,
|
|
data.stepLength,
|
|
data.groundContactTime,
|
|
data.impact,
|
|
data.swingAngle,
|
|
data.foreFootLanding,
|
|
data.midFootLanding,
|
|
data.backFootLanding,
|
|
data.eversionAngle,
|
|
data.swolf,
|
|
data.strokeRate,
|
|
unknown
|
|
);
|
|
dao.insertOrReplace(dataSample);
|
|
}
|
|
} catch (Exception e) {
|
|
LOG.error("Failed to add workout data to database", e);
|
|
}
|
|
}
|
|
|
|
public void addWorkoutPaceData(Long workoutId, List<Workout.WorkoutPace.Response.Block> paceList) {
|
|
if (workoutId == null)
|
|
return;
|
|
|
|
try (DBHandler db = GBApplication.acquireDB()) {
|
|
HuaweiWorkoutPaceSampleDao dao = db.getDaoSession().getHuaweiWorkoutPaceSampleDao();
|
|
|
|
for (Workout.WorkoutPace.Response.Block block : paceList) {
|
|
HuaweiWorkoutPaceSample paceSample = new HuaweiWorkoutPaceSample(
|
|
workoutId,
|
|
block.distance,
|
|
block.type,
|
|
block.pace,
|
|
block.correction
|
|
);
|
|
dao.insertOrReplace(paceSample);
|
|
}
|
|
} catch (Exception e) {
|
|
LOG.error("Failed to add workout pace data to database", e);
|
|
}
|
|
}
|
|
|
|
public void setWearLocation() {
|
|
try {
|
|
SetWearLocationRequest setWearLocationReq = new SetWearLocationRequest(this);
|
|
setWearLocationReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure Wear Location", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure Wear Location", e);
|
|
}
|
|
}
|
|
|
|
public void getBatteryLevel() {
|
|
try {
|
|
GetBatteryLevelRequest batteryLevelReq = new GetBatteryLevelRequest(this);
|
|
batteryLevelReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to get battery Level", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to get battery Level", e);
|
|
}
|
|
}
|
|
|
|
public void setActivateOnLift() {
|
|
try {
|
|
SetActivateOnLiftRequest setActivateOnLiftReq = new SetActivateOnLiftRequest(this);
|
|
setActivateOnLiftReq.doPerform();
|
|
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceMac);
|
|
boolean statusDndLiftWrist = sharedPrefs.getBoolean(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_LIFT_WRIST, false);
|
|
if (statusDndLiftWrist) {
|
|
setDnd();
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure Activate on Rotate", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure Activate on Rotate", e);
|
|
}
|
|
}
|
|
|
|
public void setNavigateOnRotate() {
|
|
try {
|
|
SetNavigateOnRotateRequest setNavigateOnRotateReq = new SetNavigateOnRotateRequest(this);
|
|
setNavigateOnRotateReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure Navigate on Rotate", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure Navigate on Rotate", e);
|
|
}
|
|
}
|
|
|
|
public void setActivityReminder() {
|
|
try {
|
|
SetActivityReminderRequest setActivityReminderReq = new SetActivityReminderRequest(this);
|
|
setActivityReminderReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure Activity reminder", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure Activity reminder", e);
|
|
}
|
|
}
|
|
|
|
public void setTrusleep() {
|
|
try {
|
|
SetTruSleepRequest setTruSleepReq = new SetTruSleepRequest(this);
|
|
setTruSleepReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to configure truSleep", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to configure truSleep", e);
|
|
}
|
|
}
|
|
|
|
public void setDnd() {
|
|
try {
|
|
SendDndDeleteRequest sendDndDeleteReq = new SendDndDeleteRequest(this);
|
|
SendDndAddRequest sendDndAddReq = new SendDndAddRequest(this);
|
|
sendDndDeleteReq.nextRequest(sendDndAddReq);
|
|
sendDndDeleteReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to set DND", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to set DND", e);
|
|
}
|
|
}
|
|
|
|
public void setDndNotWear() {
|
|
try {
|
|
SetWearMessagePushRequest setWearMessagePushReq = new SetWearMessagePushRequest(this);
|
|
setWearMessagePushReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Setting DND not wear failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Setting DND not wear failed", e);
|
|
}
|
|
|
|
}
|
|
|
|
private void setDisconnectNotification() {
|
|
try {
|
|
SetDisconnectNotification req = new SetDisconnectNotification(this);
|
|
req.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to set disconnect notification", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to set disconnect notification", e);
|
|
}
|
|
}
|
|
|
|
private void setHeartrateAutomatic() {
|
|
try {
|
|
SetAutomaticHeartrateRequest req = new SetAutomaticHeartrateRequest(this);
|
|
req.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to set automatic heart rate", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to set automatic heart rate", e);
|
|
}
|
|
}
|
|
|
|
private void setSpoAutomatic() {
|
|
try {
|
|
SetAutomaticSpoRequest req = new SetAutomaticSpoRequest(this);
|
|
req.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to set automatic SpO", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to set automatic SpO", e);
|
|
}
|
|
}
|
|
|
|
public void sendDebugRequest() {
|
|
try {
|
|
LOG.debug("Send debug request");
|
|
DebugRequest req = new DebugRequest(this);
|
|
req.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to send debug request", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to send debug request", e);
|
|
}
|
|
}
|
|
|
|
public void onStopFindPhone() {
|
|
try {
|
|
LOG.debug("Send stop find phone request");
|
|
StopFindPhoneRequest stopFindPhoneRequest = new StopFindPhoneRequest(this);
|
|
stopFindPhoneRequest.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to send stop find phone request", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to send stop find phone request", e);
|
|
}
|
|
}
|
|
|
|
public void setLanguageSetting() {
|
|
try {
|
|
SetLanguageSettingRequest setLocaleReq = new SetLanguageSettingRequest(this);
|
|
setLocaleReq.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to set language settings request", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to set language settings request", e);
|
|
}
|
|
}
|
|
|
|
public Weather.WeatherIcon openWeatherMapConditionCodeToHuaweiIcon(int conditionCode) {
|
|
// More exact first, groups after
|
|
switch (conditionCode) {
|
|
case 500:
|
|
return Weather.WeatherIcon.LIGHT_RAIN;
|
|
case 501:
|
|
return Weather.WeatherIcon.RAIN;
|
|
case 502:
|
|
return Weather.WeatherIcon.HEAVY_RAIN;
|
|
case 503:
|
|
return Weather.WeatherIcon.RAIN_STORM;
|
|
case 504:
|
|
return Weather.WeatherIcon.SEVERE_RAIN_STORMS;
|
|
case 511:
|
|
return Weather.WeatherIcon.FREEZING_RAIN;
|
|
case 600:
|
|
return Weather.WeatherIcon.LIGHT_SNOW;
|
|
case 601:
|
|
return Weather.WeatherIcon.SNOW;
|
|
case 602:
|
|
return Weather.WeatherIcon.HEAVY_SNOW;
|
|
case 611:
|
|
return Weather.WeatherIcon.SLEET;
|
|
case 701:
|
|
case 741:
|
|
return Weather.WeatherIcon.FOG;
|
|
case 721:
|
|
return Weather.WeatherIcon.HAZY;
|
|
case 751:
|
|
return Weather.WeatherIcon.SAND;
|
|
case 761:
|
|
return Weather.WeatherIcon.DUST;
|
|
case 800:
|
|
return Weather.WeatherIcon.SUNNY;
|
|
case 801:
|
|
case 802:
|
|
return Weather.WeatherIcon.CLOUDY;
|
|
case 803:
|
|
case 804:
|
|
return Weather.WeatherIcon.OVERCAST;
|
|
}
|
|
if (conditionCode >= 200 && conditionCode < 300)
|
|
return Weather.WeatherIcon.THUNDERSTORMS;
|
|
if (conditionCode >= 300 && conditionCode < 400)
|
|
return Weather.WeatherIcon.LIGHT_RAIN;
|
|
if (conditionCode >= 500 && conditionCode < 600)
|
|
return Weather.WeatherIcon.RAIN;
|
|
if (conditionCode >= 600 && conditionCode < 700)
|
|
return Weather.WeatherIcon.SNOW;
|
|
return Weather.WeatherIcon.UNKNOWN;
|
|
}
|
|
|
|
public void onSendWeather(ArrayList<WeatherSpec> weatherSpecs) {
|
|
// Initialize weather settings and send weather
|
|
if (!getHuaweiCoordinator().supportsWeather()) {
|
|
LOG.error("onSendWeather called while weather is not supported.");
|
|
return;
|
|
}
|
|
|
|
WeatherSpec weatherSpec = weatherSpecs.get(0);
|
|
|
|
Weather.Settings weatherSettings = new Weather.Settings();
|
|
|
|
SendWeatherStartRequest weatherStartRequest = new SendWeatherStartRequest(this, weatherSettings);
|
|
Request lastRequest = weatherStartRequest;
|
|
|
|
if (getHuaweiCoordinator().supportsWeatherUnit()) {
|
|
SendWeatherUnitRequest weatherUnitRequest = new SendWeatherUnitRequest(this);
|
|
lastRequest.nextRequest(weatherUnitRequest);
|
|
lastRequest = weatherUnitRequest;
|
|
}
|
|
|
|
SendWeatherSupportRequest weatherSupportRequest = new SendWeatherSupportRequest(this, weatherSettings);
|
|
lastRequest.nextRequest(weatherSupportRequest);
|
|
lastRequest = weatherSupportRequest;
|
|
|
|
if (getHuaweiCoordinator().supportsWeatherExtended()) {
|
|
SendWeatherExtendedSupportRequest weatherExtendedSupportRequest = new SendWeatherExtendedSupportRequest(this, weatherSettings);
|
|
lastRequest.nextRequest(weatherExtendedSupportRequest);
|
|
lastRequest = weatherExtendedSupportRequest;
|
|
}
|
|
|
|
if (getHuaweiCoordinator().supportsWeatherMoonRiseSet()) {
|
|
SendWeatherSunMoonSupportRequest weatherSunMoonSupportRequest = new SendWeatherSunMoonSupportRequest(this, weatherSettings);
|
|
lastRequest.nextRequest(weatherSunMoonSupportRequest);
|
|
lastRequest = weatherSunMoonSupportRequest;
|
|
}
|
|
|
|
// End of initialization and start of actually sending weather
|
|
|
|
SendWeatherCurrentRequest sendWeatherCurrentRequest = new SendWeatherCurrentRequest(this, weatherSettings, weatherSpec);
|
|
lastRequest.nextRequest(sendWeatherCurrentRequest);
|
|
lastRequest = sendWeatherCurrentRequest;
|
|
|
|
SendGpsAndTimeToDeviceRequest sendGpsAndTimeToDeviceRequest = new SendGpsAndTimeToDeviceRequest(this);
|
|
lastRequest.nextRequest(sendGpsAndTimeToDeviceRequest);
|
|
lastRequest = sendGpsAndTimeToDeviceRequest;
|
|
|
|
if (getHuaweiCoordinator().supportsWeatherForecasts()) {
|
|
SendWeatherForecastRequest sendWeatherForecastRequest = new SendWeatherForecastRequest(this, weatherSettings, weatherSpec);
|
|
lastRequest.nextRequest(sendWeatherForecastRequest);
|
|
lastRequest = sendWeatherForecastRequest;
|
|
}
|
|
|
|
try {
|
|
weatherStartRequest.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to send weather", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to send weather", e);
|
|
}
|
|
}
|
|
|
|
public void onSetGpsLocation(Location location) {
|
|
if (gpsParametersResponse == null) {
|
|
GB.toast(context, "Received location without knowing supported parameters", Toast.LENGTH_SHORT, GB.ERROR);
|
|
LOG.error("Received location without knowing supported parameters");
|
|
return;
|
|
}
|
|
|
|
SendGpsDataRequest sendGpsDataRequest = new SendGpsDataRequest(this, location, gpsParametersResponse);
|
|
try {
|
|
sendGpsDataRequest.doPerform();
|
|
} catch (IOException e) {
|
|
// TODO: Use translatable string
|
|
GB.toast(context, "Failed to send GPS data", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to send GPS data", e);
|
|
}
|
|
}
|
|
|
|
public void onInstallApp(Uri uri) {
|
|
LOG.info("enter onAppInstall uri: "+uri);
|
|
HuaweiFwHelper huaweiFwHelper = new HuaweiFwHelper(uri, getContext());
|
|
huaweiUploadManager.setBytes(huaweiFwHelper.getBytes());
|
|
huaweiUploadManager.setFileType(huaweiFwHelper.getFileType());
|
|
if (huaweiFwHelper.isWatchface()) {
|
|
huaweiUploadManager.setFileName(huaweiWatchfaceManager.getRandomName());
|
|
} else {
|
|
huaweiUploadManager.setFileName(huaweiFwHelper.getFileName());
|
|
}
|
|
|
|
try {
|
|
SendFileUploadInfo sendFileUploadInfo = new SendFileUploadInfo(this, huaweiUploadManager);
|
|
sendFileUploadInfo.doPerform();
|
|
} catch (IOException e) {
|
|
GB.toast(context, "Failed to send file upload info", Toast.LENGTH_SHORT, GB.ERROR, e);
|
|
LOG.error("Failed to send file upload info", e);
|
|
}
|
|
}
|
|
|
|
public void onUploadProgress(int textRsrc, int progressPercent, boolean ongoing) {
|
|
try {
|
|
if (isBLE()) {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder leBuilder = createLeTransactionBuilder("FetchRecordedData");
|
|
leBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction(
|
|
context.getString(textRsrc),
|
|
ongoing,
|
|
progressPercent,
|
|
context
|
|
));
|
|
leBuilder.queue(leSupport.getQueue());
|
|
} else {
|
|
nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder brBuilder = createBrTransactionBuilder("FetchRecordedData");
|
|
brBuilder.add(new nodomain.freeyourgadget.gadgetbridge.service.btbr.actions.SetProgressAction(
|
|
context.getString(textRsrc),
|
|
ongoing,
|
|
progressPercent,
|
|
context));
|
|
brBuilder.queue(brSupport.getQueue());
|
|
|
|
}
|
|
|
|
} catch (final Exception e) {
|
|
LOG.error("Failed to update progress notification", e);
|
|
}
|
|
}
|
|
|
|
public void onAppInfoReq() {
|
|
huaweiWatchfaceManager.requestWatchfaceList();
|
|
}
|
|
|
|
public void onAppStart(final UUID uuid, boolean start) {
|
|
if (start) {
|
|
huaweiWatchfaceManager.setWatchface(uuid);
|
|
}
|
|
}
|
|
|
|
public void onAppDelete(final UUID uuid) {
|
|
huaweiWatchfaceManager.deleteWatchface(uuid);
|
|
}
|
|
|
|
}
|