diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed089eaf..59bab8dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ### Changelog +#### Version 0.35.1 +* Mi Band 4: Support flashing watchfaces, res and firmware (.ft untested) + #### Version 0.35.0 * Mi Band 4: Initial support (WARNING: INITIAL SETUP NEEDS MI FIT WITH ACCOUNT AND ROOT, NOT A RECOMMENDED DEVICE FOR GADGETBRIDGE) diff --git a/app/build.gradle b/app/build.gradle index 76fc13d37..05eddf98b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { targetSdkVersion 27 // Note: always bump BOTH versionCode and versionName! - versionName "0.35.0" - versionCode 152 + versionName "0.35.1" + versionCode 153 vectorDrawables.useSupportLibrary = true } buildTypes { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java index 6d1bd6f49..ad5d07e65 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java @@ -80,4 +80,13 @@ public class AmazfitCor2Coordinator extends HuamiCoordinator { public boolean supportsUnicodeEmojis() { return true; } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_amazfitcor, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_disconnectnotification, + R.xml.devicesettings_pairingkey}; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java index c511daa27..681c4bd93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java @@ -106,12 +106,14 @@ public abstract class HuamiFirmwareInfo { } private final int crc16; + private final int crc32; private byte[] bytes; public HuamiFirmwareInfo(byte[] bytes) { this.bytes = bytes; crc16 = CheckSums.getCRC16(bytes); + crc32 = CheckSums.getCRC32(bytes); firmwareType = determineFirmwareType(bytes); } @@ -140,6 +142,9 @@ public abstract class HuamiFirmwareInfo { public int getCrc16() { return crc16; } + public int getCrc32() { + return crc32; + } public int getFirmwareVersion() { return getCrc16(); // HACK until we know how to determine the version from the fw bytes diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index 6bc42278e..ad7d815e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -113,7 +113,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions.StopNo import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchSportsSummaryOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; @@ -1000,7 +999,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { @Override public void onInstallApp(Uri uri) { try { - new UpdateFirmwareOperation(uri, this).perform(); + createUpdateFirmwareOperation(uri).perform(); } catch (IOException ex) { GB.toast(getContext(), "Firmware cannot be installed: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); } @@ -1965,4 +1964,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { return new MiBand2FWHelper(uri, context); } + + public UpdateFirmwareOperation createUpdateFirmwareOperation(Uri uri) { + return new UpdateFirmwareOperation(uri, this); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java index 2250925a0..7e687932c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java @@ -24,6 +24,7 @@ import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband4.MiBand4FWHelper; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperationNew; public class MiBand4Support extends MiBand3Support { @@ -36,4 +37,9 @@ public class MiBand4Support extends MiBand3Support { public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { return new MiBand4FWHelper(uri, context); } + + @Override + public UpdateFirmwareOperationNew createUpdateFirmwareOperation(Uri uri) { + return new UpdateFirmwareOperationNew(uri, this); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java index de1178000..07ca209cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java @@ -50,8 +50,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { private static final Logger LOG = LoggerFactory.getLogger(UpdateFirmwareOperation.class); protected final Uri uri; - protected final BluetoothGattCharacteristic fwCControlChar; - protected final BluetoothGattCharacteristic fwCDataChar; + final BluetoothGattCharacteristic fwCControlChar; + private final BluetoothGattCharacteristic fwCDataChar; protected final Prefs prefs = GBApplication.getPrefs(); protected HuamiFirmwareInfo firmwareInfo; @@ -81,7 +81,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { //the firmware will be sent by the notification listener if the band confirms that the metadata are ok. } - protected HuamiFirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { + private HuamiFirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { HuamiFWHelper fwHelper = getSupport().createFWHelper(uri, context); return fwHelper.getFirmwareInfo(); } @@ -128,8 +128,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { * @param value */ private void handleNotificationNotif(byte[] value) { - if (value.length != 3) { - LOG.error("Notifications should be 3 bytes long."); + if (value.length != 3 && value.length != 11) { + LOG.error("Notifications should be 3 or 11 bytes long."); getSupport().logMessageContent(value); return; } @@ -160,7 +160,6 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { case HuamiService.COMMAND_FIRMWARE_REBOOT: { LOG.info("Reboot command successfully sent."); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext()); -// getSupport().onReboot(); done(); break; } @@ -170,7 +169,6 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { operationFailed(); displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); - return; } } } catch (Exception ex) { @@ -185,7 +183,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { done(); } } - protected void displayMessage(Context context, String message, int duration, int severity) { + + private void displayMessage(Context context, String message, int duration, int severity) { getSupport().handleGBDeviceEvent(new GBDeviceEventDisplayMessage(message, duration, severity)); } @@ -208,7 +207,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { bytes[i++] = sizeBytes[1]; bytes[i++] = sizeBytes[2]; if (!isFirmwareCode) { - bytes[i++] = getFirmwareInfo().getFirmwareType().getValue(); + bytes[i] = getFirmwareInfo().getFirmwareType().getValue(); } builder.write(fwCControlChar, bytes); @@ -241,10 +240,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); - if (prefs.getBoolean("mi_low_latency_fw_update", true)) { - getSupport().setLowLatency(builder); - } - builder.write(fwCControlChar, new byte[] { HuamiService.COMMAND_FIRMWARE_START_DATA }); + builder.write(fwCControlChar, getFirmwareStartCommand()); for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); @@ -262,14 +258,13 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { if (firmwareProgress < len) { byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); builder.write(fwCDataChar, lastChunk); - firmwareProgress = len; } builder.write(fwCControlChar, new byte[]{HuamiService.COMMAND_FIRMWARE_UPDATE_SYNC}); builder.queue(getQueue()); } catch (IOException ex) { - LOG.error("Unable to send fw to MI 2", ex); + LOG.error("Unable to send fw to device", ex); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_firmware_not_sent), false, 0, getContext()); return false; } @@ -277,11 +272,11 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { } - private void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { + protected void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { TransactionBuilder builder = performInitialized("send firmware checksum"); int crc16 = firmwareInfo.getCrc16(); byte[] bytes = BLETypeConversions.fromUint16(crc16); - builder.write(fwCControlChar, new byte[] { + builder.write(fwCControlChar, new byte[]{ HuamiService.COMMAND_FIRMWARE_CHECKSUM, bytes[0], bytes[1], @@ -289,7 +284,11 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { builder.queue(getQueue()); } - private HuamiFirmwareInfo getFirmwareInfo() { + HuamiFirmwareInfo getFirmwareInfo() { return firmwareInfo; } -} + + protected byte[] getFirmwareStartCommand() { + return new byte[]{HuamiService.COMMAND_FIRMWARE_START_DATA}; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java new file mode 100644 index 000000000..e2991366c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations; + +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; + +public class UpdateFirmwareOperationNew extends UpdateFirmwareOperation { + private static final Logger LOG = LoggerFactory.getLogger(UpdateFirmwareOperationNew.class); + + + public UpdateFirmwareOperationNew(Uri uri, HuamiSupport support) { + super(uri, support); + } + + + public boolean sendFwInfo() { + try { + TransactionBuilder builder = performInitialized("send firmware info"); + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); + int fwSize = getFirmwareInfo().getSize(); + byte[] sizeBytes = BLETypeConversions.fromUint24(fwSize); + byte[] bytes = new byte[10]; + int i = 0; + bytes[i++] = HuamiService.COMMAND_FIRMWARE_INIT; + bytes[i++] = getFirmwareInfo().getFirmwareType().getValue(); + bytes[i++] = sizeBytes[0]; + bytes[i++] = sizeBytes[1]; + bytes[i++] = sizeBytes[2]; + bytes[i++] = 0; // TODO: what is that? + int crc32 = firmwareInfo.getCrc32(); + byte[] crcBytes = BLETypeConversions.fromUint32(crc32); + bytes[i++] = crcBytes[0]; + bytes[i++] = crcBytes[1]; + bytes[i++] = crcBytes[2]; + bytes[i] = crcBytes[3]; + + + builder.write(fwCControlChar, bytes); + builder.queue(getQueue()); + return true; + } catch (IOException e) { + LOG.error("Error sending firmware info: " + e.getLocalizedMessage(), e); + return false; + } + } + + @Override + protected void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { + TransactionBuilder builder = performInitialized("send firmware upload finished"); + builder.write(fwCControlChar, new byte[]{HuamiService.COMMAND_FIRMWARE_CHECKSUM}); + builder.queue(getQueue()); + } + + @Override + protected byte[] getFirmwareStartCommand() { + return new byte[]{HuamiService.COMMAND_FIRMWARE_START_DATA, 1}; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java index 671d79af0..f33ef49a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.zip.CRC32; public class CheckSums { public static int getCRC8(byte[] seq) { @@ -46,9 +47,9 @@ public class CheckSums { public static int getCRC16(byte[] seq) { int crc = 0xFFFF; - for (int j = 0; j < seq.length; j++) { + for (byte b : seq) { crc = ((crc >>> 8) | (crc << 8)) & 0xffff; - crc ^= (seq[j] & 0xff);//byte to int, trunc sign + crc ^= (b & 0xff);//byte to int, trunc sign crc ^= ((crc & 0xff) >> 4); crc ^= (crc << 12) & 0xffff; crc ^= ((crc & 0xFF) << 5) & 0xffff; @@ -57,6 +58,12 @@ public class CheckSums { return crc; } + public static int getCRC32(byte[] seq) { + CRC32 crc = new CRC32(); + crc.update(seq); + return (int) (crc.getValue()); + } + public static void main(String[] args) throws IOException { if (args == null || args.length == 0) { throw new IllegalArgumentException("Pass the files to be checksummed as arguments"); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index c9002f075..0eff99c63 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,8 @@ + + Mi Band 4: Support flashing watchfaces, res and firmware (fonts untested) + Mi Band 4: Initial support (WARNING: INITIAL SETUP NEEDS MI FIT WITH ACCOUNT AND ROOT, NOT A RECOMMENDED DEVICE FOR GADGETBRIDGE) diff --git a/fastlane/metadata/android/en-US/changelogs/153.txt b/fastlane/metadata/android/en-US/changelogs/153.txt new file mode 100644 index 000000000..bfd64ad46 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/153.txt @@ -0,0 +1 @@ +* Mi Band 4: Support flashing watchfaces, res and firmware (.ft untested)