diff --git a/CHANGELOG.md b/CHANGELOG.md index 455992e32..271a14803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ### Changelog -#### Version 0.43.2 -* Fossil Q Hybrid: Allow choosing and cropping image to be set as watch background +#### NEXT +* Fossil Hybrid HR: Allow choosing and cropping image to be set as watch background +* Fossil Hybrid HR: Option to draw circles around widgets +* Fossil Hybrid HR: Experimenal firmware update support * Steps/Sleep averages: skip days with zero data #### Version 0.43.1 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilHRInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilHRInstallHandler.java new file mode 100644 index 000000000..56df44809 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FossilHRInstallHandler.java @@ -0,0 +1,100 @@ +/* Copyright (C) 2020 Andreas Shimokawa + + 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 . */ +package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; + +import android.content.Context; +import android.net.Uri; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; +import nodomain.freeyourgadget.gadgetbridge.util.UriHelper; + +public class FossilHRInstallHandler implements InstallHandler { + private final Context mContext; + private boolean mIsValid; + + FossilHRInstallHandler(Uri uri, Context context) { + mContext = context; + UriHelper uriHelper = null; + try { + uriHelper = UriHelper.get(uri, mContext); + } catch (IOException e) { + mIsValid = false; + return; + } + try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) { + byte[] bytes = new byte[16]; + in.read(bytes); + ByteBuffer buf = ByteBuffer.wrap(bytes); + buf.order(ByteOrder.LITTLE_ENDIAN); + int header0 = buf.getInt(); + int size = buf.getInt(); + int header2 = buf.getInt(); + int header3 = buf.getInt(); + if (header0 != 1 || header2 != 0x00012000 || header3 != 0x00012000) { + mIsValid = false; + return; + } + } catch (Exception e) { + mIsValid = false; + return; + } + mIsValid = true; + } + + @Override + public void validateInstallation(InstallActivity installActivity, GBDevice device) { + if (device.isBusy()) { + installActivity.setInfoText(device.getBusyTask()); + installActivity.setInstallEnabled(false); + return; + } + if (device.getType() != DeviceType.FOSSILQHYBRID || !device.isConnected()) { + installActivity.setInfoText("Element cannot be installed"); + installActivity.setInstallEnabled(false); + return; + } + GenericItem installItem = new GenericItem(); + installItem.setIcon(R.drawable.ic_firmware); + installItem.setName("Fossil Hybrid HR Firmware"); + installItem.setDetails("Unknown version"); + + installActivity.setInfoText(mContext.getString(R.string.firmware_install_warning, "(unknown)")); + installActivity.setInstallEnabled(true); + installActivity.setInstallItem(installItem); + } + + + @Override + public void onStartInstall(GBDevice device) { + } + + @Override + public boolean isValid() { + return mIsValid; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java index 4c41aea9c..6cfd1f57c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java @@ -100,6 +100,10 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator { @Override public InstallHandler findInstallHandler(Uri uri, Context context) { + if (isHybridHR()) { + FossilHRInstallHandler installHandler = new FossilHRInstallHandler(uri, context); + return installHandler.isValid() ? installHandler : null; + } return null; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java index cb2d1eced..36cf3305e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java @@ -541,6 +541,11 @@ public class QHybridSupport extends QHybridBaseSupport { watchAdapter.onTestNewFunction(); } + @Override + public void onInstallApp(Uri uri) { + watchAdapter.onInstallApp(uri); + } + private void backupFile(DownloadFileRequest request) { try { File file = FileUtils.getExternalFile("qFiles/" + request.timeStamp); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java index 634fde4fb..aa88203f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java @@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; +import android.net.Uri; import java.util.ArrayList; @@ -63,6 +64,7 @@ public abstract class WatchAdapter { public abstract void syncNotificationSettings(); public abstract void onTestNewFunction(); public abstract void setTimezoneOffsetMinutes(short offset); + public abstract void onInstallApp(Uri uri); public abstract boolean supportsFindDevice(); public abstract boolean supportsExtendedVibration(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java index e7c1aba93..59688fd10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Build; import android.widget.Toast; @@ -382,6 +383,11 @@ public class FossilWatchAdapter extends WatchAdapter { }); } + @Override + public void onInstallApp(Uri uri) { + + } + @Override public boolean supportsFindDevice() { return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java index fe4befd76..e926c5766 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java @@ -1,7 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr; import android.bluetooth.BluetoothGattCharacteristic; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; @@ -9,6 +8,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.net.Uri; import android.os.Build; import android.widget.Toast; @@ -16,11 +16,13 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.BufferOverflowException; import java.util.ArrayList; import java.util.Calendar; @@ -31,7 +33,6 @@ import java.util.TimeZone; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.activities.GBActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; @@ -58,6 +59,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationPutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.AssetFilePutRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FirmwareFilePutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImage; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImageFactory; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.ImagesSetRequest; @@ -72,9 +74,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.CustomWidgetElement; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.Widget; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.WidgetsPutRequest; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; +import nodomain.freeyourgadget.gadgetbridge.util.UriHelper; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.MUSIC_PHONE_REQUEST; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.MUSIC_WATCH_REQUEST; @@ -693,6 +697,25 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } */ + + @Override + public void onInstallApp(Uri uri) { + UriHelper uriHelper = null; + try { + uriHelper = UriHelper.get(uri, getContext()); + } catch (IOException e) { + GB.toast(getContext(), "Could not open firmare: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e); + } + if (uriHelper != null) { + try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) { + byte[] firmwareBytes = FileUtils.readAll(in, 1024 * 2024); // 2MB + queueWrite(new FirmwareFilePutRequest(firmwareBytes, this)); + } catch (Exception e) { + GB.toast(getContext(), "Firmware cannot be installed: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e); + } + } + } + public byte[] getSecretKey() { byte[] authKeyBytes = new byte[16]; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/misfit/MisfitWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/misfit/MisfitWatchAdapter.java index 8e469093d..b40119ca5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/misfit/MisfitWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/misfit/MisfitWatchAdapter.java @@ -19,7 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.mis import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; -import android.util.Log; +import android.net.Uri; import android.util.SparseArray; import android.widget.Toast; @@ -49,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.WatchAdapter; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.ActivityPointGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.AnimationRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.BatteryLevelRequest; @@ -66,7 +67,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.mis import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.OTAEraseRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.ReleaseHandsControlRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.RequestHandControlRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.SetCurrentStepCountRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.SetStepGoalRequest; @@ -418,6 +418,11 @@ public class MisfitWatchAdapter extends WatchAdapter { GB.toast("old firmware does't support timezones", Toast.LENGTH_LONG, GB.ERROR); } + @Override + public void onInstallApp(Uri uri) { + + } + @Override public boolean supportsFindDevice() { return supportsExtendedVibration(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/notification/PlayNotificationRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/notification/PlayNotificationRequest.java index 04c2c7d64..0d97fa14c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/notification/PlayNotificationRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/notification/PlayNotificationRequest.java @@ -63,7 +63,7 @@ public abstract class PlayNotificationRequest extends FilePutRequest { String nullTerminatedMessage = StringUtils.terminateNull(message); byte[] messageBytes = nullTerminatedMessage.getBytes(charsetUTF8); if (messageBytes.length > 490) { - messageBytes = Arrays.copyOf(messageBytes, 490); + messageBytes = Arrays.copyOf(messageBytes, 475); } short mainBufferLength = (short) (lengthBufferLength + uidLength + appBundleCRCLength + titleBytes.length + senderBytes.length + messageBytes.length); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java index 2c4f06c76..5c1fa6203 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java @@ -18,15 +18,10 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fo import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.HashMap; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedPutRequest; -import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.ConfigItem; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedPutRequest; public class ConfigurationPutRequest extends FileEncryptedPutRequest { public ConfigurationPutRequest(ConfigItem item, FossilHRWatchAdapter adapter) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java index e6709d971..78236cf51 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/AssetFilePutRequest.java @@ -12,16 +12,16 @@ public class AssetFilePutRequest extends FilePutRequest { public AssetFilePutRequest(AssetFile[] files, byte subHandle, FossilWatchAdapter adapter) throws IOException { super((short) (0x0700 | subHandle), prepareFileData(files), adapter); } - public AssetFilePutRequest(AssetFile file, byte subHandle, FossilWatchAdapter adapter) throws IOException { + public AssetFilePutRequest(AssetFile file, byte subHandle, FossilWatchAdapter adapter) { super((short) (0x0700 | subHandle), prepareFileData(file), adapter); } private static byte[] prepareFileData(AssetFile[] files) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); - for(int i = 0; i < files.length; i++){ + for (AssetFile file : files) { stream.write( - prepareFileData(files[i]) + prepareFileData(file) ); } @@ -29,7 +29,7 @@ public class AssetFilePutRequest extends FilePutRequest { } private static byte[] prepareFileData(AssetFile file){ - int size = file.getFileName().length() + file.getFileData().length + 1 /**null byte **/; + int size = file.getFileName().length() + file.getFileData().length + 1; // null byte ByteBuffer buffer = ByteBuffer.allocate(size + 2); buffer.order(ByteOrder.LITTLE_ENDIAN); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FirmwareFilePutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FirmwareFilePutRequest.java new file mode 100644 index 000000000..e8781af49 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FirmwareFilePutRequest.java @@ -0,0 +1,9 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter; + +public class FirmwareFilePutRequest extends FilePutRawRequest { + public FirmwareFilePutRequest(byte[] firmwareBytes, FossilHRWatchAdapter adapter) { + super((short) 0x00FF, firmwareBytes, adapter); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/WidgetsPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/WidgetsPutRequest.java index aecef97e0..b42d95145 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/WidgetsPutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/widget/WidgetsPutRequest.java @@ -4,9 +4,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FilePutRawRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.json.JsonPutRequest; public class WidgetsPutRequest extends JsonPutRequest {