mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-23 18:36:50 +01:00
Fossil Hybrid HR: Experimenal firmware update support
TODO: - progress bar - set device state to firmware update There is currently absolutely no feedback during install, just wait and pray
This commit is contained in:
parent
630c58c8f4
commit
1e93a8c1cc
@ -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
|
||||
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user