Garmin: Support file archival (deletion) on watch

Also add original timestamp to local cache filename as the file identifier are reused
Also fix imports of Test class
This commit is contained in:
Daniele Gobbetti 2024-04-18 17:26:40 +02:00
parent bcfaf7b3e8
commit 6b821a2f1f
6 changed files with 106 additions and 8 deletions

View File

@ -10,6 +10,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.DownloadRequestMessage;
@ -319,7 +320,9 @@ public class FileTransferHandler implements MessageHandler {
}
public String getFileName() {
return getFiletype().name() + "_" + getFileIndex() + (getFiletype().isFitFile() ? ".fit" : "");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String dateString = dateFormat.format(fileDate);
return getFiletype().name() + "_" + getFileIndex() + "_" + dateString + (getFiletype().isFitFile() ? ".fit" : "");
}
@NonNull

View File

@ -103,6 +103,7 @@ public abstract class GFDIMessage {
UPLOAD_REQUEST(5003, UploadRequestMessage.class),
FILE_TRANSFER_DATA(5004, FileTransferDataMessage.class),
CREATE_FILE(5005, CreateFileMessage.class),
SET_FILE_FLAG(5008, SetFileFlagsMessage.class),
FIT_DEFINITION(5011, FitDefinitionMessage.class),
FIT_DATA(5012, FitDataMessage.class),
WEATHER_REQUEST(5014, WeatherMessage.class),

View File

@ -0,0 +1,41 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
import org.apache.commons.lang3.EnumUtils;
import java.util.EnumSet;
public class SetFileFlagsMessage extends GFDIMessage {
private final int fileIndex;
private final FileFlags flags;
public SetFileFlagsMessage(int fileIndex, FileFlags flags) {
this.garminMessage = GarminMessage.SET_FILE_FLAG;
this.fileIndex = fileIndex;
this.flags = flags;
}
@Override
protected boolean generateOutgoing() {
final MessageWriter writer = new MessageWriter(response);
writer.writeShort(0); // packet size will be filled below
writer.writeShort(this.garminMessage.getId());
writer.writeShort(this.fileIndex);
writer.writeByte((int) EnumUtils.generateBitVector(FileFlags.class, this.flags));
return true;
}
public enum FileFlags {
UNK_00000001,
UNK_00000010,
UNK_00000100,
UNK_00001000,
ARCHIVE,
;
public static EnumSet<FileFlags> fromBitMask(final int code) {
return EnumUtils.processBitVector(FileFlags.class, code);
}
}
}

View File

@ -26,6 +26,8 @@ public abstract class GFDIStatusMessage extends GFDIMessage {
SupportedFileTypesStatusMessage supportedFileTypesStatusMessage = SupportedFileTypesStatusMessage.parseIncoming(reader, garminMessage);
LOG.info("{}", supportedFileTypesStatusMessage);
return supportedFileTypesStatusMessage;
} else if (GarminMessage.SET_FILE_FLAG.equals(originalGarminMessage)) {
return SetFileFlagsStatusMessage.parseIncoming(reader, garminMessage);
} else if (GarminMessage.FIT_DEFINITION.equals(originalGarminMessage)) {
return FitDefinitionStatusMessage.parseIncoming(reader, originalGarminMessage);
} else if (GarminMessage.FIT_DATA.equals(originalGarminMessage)) {

View File

@ -0,0 +1,58 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status;
import java.util.EnumSet;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.SetFileFlagsMessage;
public class SetFileFlagsStatusMessage extends GFDIStatusMessage {
private final Status status;
private final FlagsStatus flagsStatus;
private final int fileIdentifier;
private final EnumSet<SetFileFlagsMessage.FileFlags> fileFlags;
public SetFileFlagsStatusMessage(GarminMessage garminMessage, Status status, FlagsStatus flagsStatus, int fileIdentifier, EnumSet<SetFileFlagsMessage.FileFlags> fileFlags) {
this.garminMessage = garminMessage;
this.status = status;
this.flagsStatus = flagsStatus;
this.fileIdentifier = fileIdentifier;
this.fileFlags = fileFlags;
}
public static SetFileFlagsStatusMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
final Status status = Status.fromCode(reader.readByte());
if (!status.equals(Status.ACK)) {
return null;
}
final FlagsStatus flagsStatus = FlagsStatus.fromCode(reader.readByte());
final int originalFileIdentifier = reader.readShort() + 1; //TODO: check if always or only on archival
final EnumSet<SetFileFlagsMessage.FileFlags> fileFlags = SetFileFlagsMessage.FileFlags.fromBitMask(reader.readByte());
if (!FlagsStatus.APPLIED.equals(flagsStatus)) {
LOG.warn("Received {} / {} for file identifier {} and flags {} - message {}", status, flagsStatus, originalFileIdentifier, fileFlags, garminMessage);
} else {
LOG.info("Received {} / {} for file identifier {} and flags {} - message {}", status, flagsStatus, originalFileIdentifier, fileFlags, garminMessage);
}
return new SetFileFlagsStatusMessage(garminMessage, status, flagsStatus, originalFileIdentifier, fileFlags);
}
enum FlagsStatus {
APPLIED,
ERROR, //guessed
;
public static FlagsStatus fromCode(final int code) {
for (final FlagsStatus status : FlagsStatus.values()) {
if (status.ordinal() == code) {
return status;
}
}
throw new IllegalArgumentException("Unknown FlagsStatus code " + code);
}
}
}

View File

@ -2,27 +2,20 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin;
import org.junit.Assert;
import org.junit.Test;
import org.threeten.bp.Instant;
import org.threeten.bp.ZoneId;
import java.math.BigInteger;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitFile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFITMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.LocalMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes.BaseType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ChecksumCalculator;
import nodomain.freeyourgadget.gadgetbridge.util.GB;