diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java index aa41f6b3c..34b1f6af7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java @@ -396,18 +396,17 @@ public class ConfigActivity extends AbstractGBActivity { }); } - ItemWithDetails buttonInfo = device.getDeviceInfo(FossilWatchAdapter.ITEM_BUTTONS); + final String buttonJson = device.getDeviceInfo(FossilWatchAdapter.ITEM_BUTTONS).getDetails(); try { - JSONArray buttonConfig = new JSONArray(new String[]{"Unknown", "Unknown", "Unknown"}); - String buttonJson = null; - if (buttonInfo != null) { - buttonJson = buttonInfo.getDetails(); - } - if (buttonJson != null && !buttonJson.isEmpty()) { - buttonConfig = new JSONArray(buttonJson); + JSONArray buttonConfig_; + if (buttonJson == null || buttonJson.isEmpty()) { + buttonConfig_ = new JSONArray(new String[]{"", "", ""}); + }else{ + buttonConfig_ = new JSONArray(buttonJson); } - final JSONArray finalButtonConfig = buttonConfig; + final JSONArray buttonConfig = buttonConfig_; + LinearLayout buttonLayout = findViewById(R.id.buttonConfigLayout); buttonLayout.removeAllViews(); findViewById(R.id.buttonOverwriteButtons).setVisibility(View.GONE); @@ -437,11 +436,14 @@ public class ConfigActivity extends AbstractGBActivity { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); ConfigPayload selected = payloads[which]; + try { - finalButtonConfig.put(currentIndex, selected.toString()); - device.addDeviceInfo(new GenericItem(FossilWatchAdapter.ITEM_BUTTONS, finalButtonConfig.toString())); + buttonConfig.put(currentIndex, selected.toString()); + device.addDeviceInfo(new GenericItem(FossilWatchAdapter.ITEM_BUTTONS, buttonConfig.toString())); updateSettings(); - LocalBroadcastManager.getInstance(ConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS)); + Intent buttonIntent = new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS); + buttonIntent.putExtra(FossilWatchAdapter.ITEM_BUTTONS, buttonConfig.toString()); + LocalBroadcastManager.getInstance(ConfigActivity.this).sendBroadcast(buttonIntent); } catch (JSONException e) { e.printStackTrace(); } @@ -455,9 +457,9 @@ public class ConfigActivity extends AbstractGBActivity { buttonLayout.addView(buttonTextView); } - } catch ( - JSONException e) { + } catch (JSONException e) { e.printStackTrace(); + GB.toast("error parsing button config", Toast.LENGTH_LONG, GB.ERROR); } } }); 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 ab4538fce..b6eb3a127 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 @@ -93,7 +93,7 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator { public boolean supportsAlarmConfiguration() { GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice(); - if(connectedDevice == null || connectedDevice.getType() != DeviceType.FOSSILQHYBRID){ + if(connectedDevice == null || connectedDevice.getType() != DeviceType.FOSSILQHYBRID || connectedDevice.getState() != GBDevice.State.INITIALIZED){ return false; } return true; 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 f5e605942..b44dc444a 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 @@ -51,6 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.WatchAdapter; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.WatchAdapterFactory; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.DownloadFileRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -178,7 +179,8 @@ public class QHybridSupport extends QHybridBaseSupport { break; } case QHYBRID_COMMAND_OVERWRITE_BUTTONS: { - watchAdapter.overwriteButtons(); + String buttonConfig = intent.getStringExtra(FossilWatchAdapter.ITEM_BUTTONS); + watchAdapter.overwriteButtons(buttonConfig); break; } case QHYBRID_COMMAND_NOTIFICATION_CONFIG_CHANGED: { 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 d299ad6f6..e9d88f163 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 @@ -31,7 +31,7 @@ public abstract class WatchAdapter { public abstract void playPairingAnimation(); public abstract void playNotification(NotificationConfiguration config); public abstract void setTime(); - public abstract void overwriteButtons(); + public abstract void overwriteButtons(String buttonConfigJson); public abstract void setActivityHand(double progress); public abstract void setHands(short hour, short minute); public abstract void vibrate(PlayNotificationRequest.VibrationType vibration); 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 ceca3c43a..bb25b65da 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 @@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fos import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Build; import android.util.Log; import android.widget.Toast; @@ -19,6 +20,7 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper; @@ -34,10 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Req import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.RequestMtuRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.AlarmsGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.AlarmsSetRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.button.ButtonConfigurationGetRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.NotificationFilterPutRequest; @@ -48,6 +47,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.mis import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.RequestHandControlRequest; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.ITEM_STEP_GOAL; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.ITEM_TIMEZONE_OFFSET; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.ITEM_VIBRATION_STRENGTH; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_EVENT_BUTTON_PRESS; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_EVENT_MULTI_BUTTON_PRESS; @@ -58,9 +60,14 @@ public class FossilWatchAdapter extends WatchAdapter { private int MTU = 23; - private String ITEM_MTU = "MTU"; + private final String ITEM_MTU = "MTU"; static public final String ITEM_BUTTONS = "BUTTONS"; + private final String CONFIG_ITEM_STEP_GOAL = "step_goal"; + private final String CONFIG_ITEM_VIBRATION_STRENGTH = "vibration_strength"; + private final String CONFIG_ITEM_TIMEZONE_OFFSET = "timezone_offset"; + public final String CONFIG_ITEM_BUTTONS = "buttons"; + private int lastButtonIndex = -1; Logger logger = LoggerFactory.getLogger(getClass()); @@ -76,11 +83,16 @@ public class FossilWatchAdapter extends WatchAdapter { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { queueWrite(new RequestMtuRequest(512), false); } - queueWrite(new ConfigurationGetRequest(this), false); + // queueWrite(new FileCloseRequest((short) 0xFFFF)); + // queueWrite(new ConfigurationGetRequest(this), false); + + syncConfiguration(); syncNotificationSettings(); - queueWrite(new ButtonConfigurationGetRequest(this) { + syncButtonSettings(); + + /* queueWrite(new ButtonConfigurationGetRequest(this) { @Override public void onConfigurationsGet(ConfigPayload[] configs) { super.onConfigurationsGet(configs); @@ -90,11 +102,43 @@ public class FossilWatchAdapter extends WatchAdapter { String json = buttons.toString(); getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(ITEM_BUTTONS, json)); } - }); + }); */ queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED), false); } + private void syncButtonSettings(){ + String buttonConfig = getDeviceSpecificPreferences().getString(CONFIG_ITEM_BUTTONS, null); + getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(ITEM_BUTTONS, buttonConfig)); + overwriteButtons(buttonConfig); + } + + private SharedPreferences getDeviceSpecificPreferences(){ + return GBApplication.getDeviceSpecificSharedPrefs( + getDeviceSupport().getDevice().getAddress() + ); + } + + private void syncConfiguration(){ + SharedPreferences preferences = getDeviceSpecificPreferences(); + + int stepGoal = preferences.getInt(CONFIG_ITEM_STEP_GOAL, 1000000); + byte vibrationStrength = (byte) preferences.getInt(CONFIG_ITEM_VIBRATION_STRENGTH, 100); + int timezoneOffset = preferences.getInt(CONFIG_ITEM_TIMEZONE_OFFSET, 0); + + GBDevice device = getDeviceSupport().getDevice(); + + device.addDeviceInfo(new GenericItem(ITEM_STEP_GOAL, String.valueOf(stepGoal))); + device.addDeviceInfo(new GenericItem(ITEM_VIBRATION_STRENGTH, String.valueOf(vibrationStrength))); + device.addDeviceInfo(new GenericItem(ITEM_TIMEZONE_OFFSET, String.valueOf(timezoneOffset))); + + queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[]{ + new ConfigurationPutRequest.DailyStepGoalConfigItem(stepGoal), + new ConfigurationPutRequest.VibrationStrengthConfigItem(vibrationStrength), + new ConfigurationPutRequest.TimezoneOffsetConfigItem((short) timezoneOffset) + }, this)); + } + public int getMTU() { if (this.MTU < 0) throw new RuntimeException("MTU not configured"); @@ -132,9 +176,15 @@ public class FossilWatchAdapter extends WatchAdapter { } @Override - public void overwriteButtons() { + public void overwriteButtons(String jsonConfigString) { try { - JSONArray buttonConfigJson = new JSONArray(getDeviceSupport().getDevice().getDeviceInfo(ITEM_BUTTONS).getDetails()); + if(jsonConfigString == null) return; + getDeviceSpecificPreferences() + .edit() + .putString(CONFIG_ITEM_BUTTONS, jsonConfigString) + .apply(); + JSONArray buttonConfigJson = new JSONArray(jsonConfigString); + // JSONArray buttonConfigJson = new JSONArray(getDeviceSupport().getDevice().getDeviceInfo(ITEM_BUTTONS).getDetails()); ConfigPayload[] payloads = new ConfigPayload[buttonConfigJson.length()]; @@ -198,6 +248,11 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void setStepGoal(int stepGoal) { + getDeviceSpecificPreferences() + .edit() + .putInt(CONFIG_ITEM_STEP_GOAL, stepGoal) + .apply(); + queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.DailyStepGoalConfigItem(stepGoal), this) { @Override public void onFilePut(boolean success) { @@ -210,6 +265,11 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void setVibrationStrength(short strength) { + getDeviceSpecificPreferences() + .edit() + .putInt(CONFIG_ITEM_VIBRATION_STRENGTH, (byte) strength) + .apply(); + ConfigurationPutRequest.ConfigItem vibrationItem = new ConfigurationPutRequest.VibrationStrengthConfigItem((byte) strength); @@ -268,6 +328,11 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void setTimezoneOffsetMinutes(short offset) { + getDeviceSpecificPreferences() + .edit() + .putInt(CONFIG_ITEM_TIMEZONE_OFFSET, offset) + .apply(); + queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.TimezoneOffsetConfigItem(offset), this){ @Override public void onFilePut(boolean success) { 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 95eb195a1..9bca5bc16 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 @@ -458,7 +458,7 @@ public class MisfitWatchAdapter extends WatchAdapter { } @Override - public void overwriteButtons() { + public void overwriteButtons(String jsonConfigString) { uploadFileRequest = new UploadFileRequest((short) 0x0800, new byte[]{ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x10, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x30, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x2E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/MicroAppCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/MicroAppCommand.java new file mode 100644 index 000000000..fb3298487 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/MicroAppCommand.java @@ -0,0 +1,160 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.microapp; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public interface MicroAppCommand { + byte[] getData(); +} + +class StartCriticalCommand implements MicroAppCommand{ + @Override + public byte[] getData() { + return new byte[]{(byte) 0x03, (byte) 0x00}; + } +} + +class CloseCommand implements MicroAppCommand{ + @Override + public byte[] getData() { + return new byte[]{(byte) 0x01, (byte) 0x00}; + } +} + +class DelayCommand implements MicroAppCommand{ + private double delayInSeconds; + + public DelayCommand(double delayInSeconds) { + this.delayInSeconds = delayInSeconds; + } + + @Override + public byte[] getData() { + return ByteBuffer.wrap(new byte[]{0x08, 0x01, 0x00, 0x00}) + .order(ByteOrder.LITTLE_ENDIAN) + .putShort(2, (short)(delayInSeconds * 10f)) + .array(); + } +} + +enum VibrationType{ + NORMAL((byte) 0x04); + + private byte value; + + VibrationType(byte value){ + this.value = value; + } + + public byte getValue(){ return this.value; } +} + +class VibrateCommand implements MicroAppCommand{ + private VibrationType vibrationType; + + public VibrateCommand(VibrationType vibrationType) { + this.vibrationType = vibrationType; + } + + @Override + public byte[] getData() { + return ByteBuffer.wrap(new byte[]{(byte) 0x93, 0x00, 0x00}) + .put(2, vibrationType.getValue()) + .array(); + } +} + +enum MovingDirection{ + CLOCKWISE((byte) 0x00), + COUNTER_CLOCKWISE((byte) 0x01), + SHORTEST((byte) 0x02), + ; + private byte value; + + MovingDirection(byte value){ this.value = value; } + + public byte getValue() { + return value; + } +} + +enum MovingSpeed{ + MAX((byte) 0x00), + HALF((byte) 0x01), + QUARTER((byte) 0x02), + EIGHTH((byte) 0x03), + SIXTEENTH((byte) 0x04), + ; + private byte value; + + MovingSpeed(byte value){ this.value = value; } + + public byte getValue() { + return value; + } +} + +class StreamCommand implements MicroAppCommand{ + private byte type; + + public StreamCommand(byte type) { + this.type = type; + } + + + @Override + public byte[] getData() { + return new byte[]{(byte) 0x8B, (byte) 0x00, this.type}; + } +} + +class RepeatStartCommand implements MicroAppCommand{ + private byte count; + + public RepeatStartCommand(byte count) { + this.count = count; + } + + + @Override + public byte[] getData() { + return new byte[]{(byte) 0x86, (byte) 0x00, this.count}; + } +} + +class RepeatStopCommand implements MicroAppCommand{ + @Override + public byte[] getData() { + return new byte[]{(byte) 0x07, (byte) 0x00}; + } +} + +class AnimationCommand implements MicroAppCommand{ + private short hour, minute; + private MovingDirection direction; + private MovingSpeed speed; + private byte absoluteMovementFlag; + + public AnimationCommand(short hour, short minute) { + this.hour = hour; + this.minute = minute; + this.speed = MovingSpeed.MAX; + this.direction = MovingDirection.SHORTEST; + this.absoluteMovementFlag = 1; + } + + @Override + public byte[] getData() { + return ByteBuffer.allocate(10) + .order(ByteOrder.LITTLE_ENDIAN) + .put((byte) 0x09) + .put((byte) 0x04) + .put((byte) 0x01) + .put((byte) 0x03) + .put((byte) ((direction.getValue() << 6) | (byte)(absoluteMovementFlag << 5) | this.speed.getValue())) + .putShort(this.hour) + .put((byte) ((direction.getValue() << 6) | (byte)(absoluteMovementFlag << 5) | this.speed.getValue())) + .putShort(this.minute) + .array(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/PlayCrazyShitRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/PlayCrazyShitRequest.java new file mode 100644 index 000000000..c67d27d83 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/microapp/PlayCrazyShitRequest.java @@ -0,0 +1,59 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.microapp; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.CRC32; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; + +public class PlayCrazyShitRequest extends FilePutRequest { + public PlayCrazyShitRequest(byte[] appData, FossilWatchAdapter adapter) { + super((short) 0x0600, createPayload(appData), adapter); + } + + private static byte[] createPayload(byte[] appData) { + List commands = new ArrayList<>(); + + commands.add(new StartCriticalCommand()); + // commands.add(new RepeatStartCommand((byte) 10)); + commands.add(new VibrateCommand(VibrationType.NORMAL)); + commands.add(new DelayCommand(1)); + // commands.add(new RepeatStopCommand()); + // commands.add(new StreamCommand((byte) 0b11111111)); + // commands.add(new AnimationCommand((short) 300, (short) 60)); + // commands.add(new DelayCommand(2)); + commands.add(new CloseCommand()); + + int length = 0; + + for (MicroAppCommand command : commands) length += command.getData().length; + + ByteBuffer buffer = ByteBuffer.allocate( + 3 /* magic bytes */ + + 8 /* button header copy */ + + 1 /* 0xFF */ + + 2 /* payload length */ + + length + + 4 /* crc */ + ); + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.put((byte) 0x01); + buffer.put((byte) 0x00); + buffer.put((byte) 0x08); + buffer.put(appData, 3, 8); + buffer.put((byte) 0xFF); + buffer.putShort((short)(length + 3)); + + for(MicroAppCommand command : commands) buffer.put(command.getData()); + + CRC32 crc = new CRC32(); + crc.update(buffer.array(), 0, buffer.position()); + + buffer.putInt((int) crc.getValue()); + + return buffer.array(); + } +}